Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion velvetflow/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,14 @@ def _resolve(value: Any, path: str = "") -> Any:
if stripped_template.startswith("{{") and stripped_template.endswith("}}"):
inner = stripped_template[2:-2].strip()
try:
return eval_jinja_expression(inner, ctx.build_jinja_context())
jinja_result = eval_jinja_expression(inner, ctx.build_jinja_context())
try:
from jinja2.runtime import Undefined # type: ignore[import]
except Exception: # pragma: no cover - fallback when jinja is absent
Undefined = () # type: ignore[assignment]

if jinja_result is not None and not isinstance(jinja_result, Undefined):
return jinja_result
except Exception:
pass
rendered_inline = _render_json_bindings(normalized_templates)
Expand Down
9 changes: 6 additions & 3 deletions velvetflow/executor/loops.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ def _apply_loop_exports(
aggregates_output: Dict[str, Any],
avg_state: Dict[str, Dict[str, float]],
default_from_node: Optional[str],
extra_exports: Optional[Mapping[str, Any]],
extra_output: Dict[str, Any],
loop_binding: Optional[BindingContext],
extra_exports: Optional[Mapping[str, Any]] = None,
extra_output: Optional[Dict[str, Any]] = None,
loop_binding: Optional[BindingContext] = None,
) -> None:
if extra_output is None:
extra_output = {}

normalized_items_spec: Optional[Mapping[str, Any]] = None

if isinstance(items_spec, list):
Expand Down
49 changes: 48 additions & 1 deletion velvetflow/planner/agent_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,51 @@
from openai import OpenAI
from pydantic import create_model

from agents import Agent, Runner, function_tool
try: # Prefer the official Agents SDK when available
from agents import Agent, Runner, function_tool
except Exception as import_error: # pragma: no cover - exercised in CI without agents
import functools
import warnings

warnings.warn(
"Falling back to the built-in Agents shim because the optional "
"'agents' package is missing or incompatible. Install the official "
"package alongside openai>=1.30.0 to enable full agent execution.",
RuntimeWarning,
)

class Agent:
def __init__(self, *, name: str, instructions: str, tools: Sequence[Callable[..., Any]], model: str, **_: Any):
self.name = name
self.instructions = instructions
self.tools = list(tools)
self.model = model

def function_tool(strict_mode: bool = True, **decorator_kwargs: Any) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
def _decorator(func: Callable[..., Any]) -> Callable[..., Any]:
@functools.wraps(func)
def _wrapper(*args: Any, **kwargs: Any) -> Any:
return func(*args, **kwargs)

# Preserve metadata that downstream code may introspect
_wrapper.__original_function__ = func # type: ignore[attr-defined]
_wrapper.__function_tool_strict_mode__ = strict_mode # type: ignore[attr-defined]
_wrapper.__function_tool_kwargs__ = decorator_kwargs # type: ignore[attr-defined]
return _wrapper

return _decorator

class Runner:
@staticmethod
def run_sync(agent: Agent, run_input: Any, *, max_turns: int | None = None) -> None:
raise RuntimeError(
"agents shim does not implement execution. Install the 'agents' package "
"compatible with openai>=1.30.0 to run agents."
) from import_error

@staticmethod
async def run(agent: Agent, run_input: Any, *, max_turns: int | None = None) -> None:
raise RuntimeError(
"agents shim does not implement execution. Install the 'agents' package "
"compatible with openai>=1.30.0 to run agents."
) from import_error