Changelog

What’s new

The latest updates, improvements, and fixes.

New featureImprovement

v0.5.0: AgentConfig and structured agent outputs

  • Structured agent outputs: Agent.run_structured(prompt, schema=...) returns parsed JSON for JSON schema dictionaries, or a validated instance when a Pydantic model class is passed. Pydantic remains optional and is not a core Orchflow dependency.
  • Typed provider configuration: new AgentConfig dataclass carries model, temperature, max_tokens, api_base, api_key, timeout, and an extra dict for provider-specific kwargs. Pass it via Agent(config=AgentConfig(...)); existing direct fields on Agent still work and take priority if both are set.
agent = Agent(
    name="extractor",
    role="Extract structured data.",
    config=AgentConfig(model="openai/gpt-5-mini", temperature=0),
)
parsed = await agent.run_structured(prompt, schema={"type": "object", ...})
  • New error type: StructuredOutputError is raised for invalid JSON, schema/validation failures, unsupported schema types, or empty model output from run_structured(...).
  • New example: examples/structured_agent.py demonstrates a JSON-schema extraction flow using AgentConfig and run_structured(...) inside a Flow step.
  • Docs and roadmap refresh: quickstart, API reference, concepts, and AGENTS.md now document AgentConfig and structured outputs; roadmap adds a 0.6.0 target to evaluate one-turn tool execution.
  • Clarified tool-calling error: calling an Agent configured with tools now raises NotImplementedError mentioning "outside Orchflow v0.5" (previously referenced v0.1), pointing users to call tools inside normal steps.
Read full entry →

Orchflow v0.5.0: AgentConfig and Structured Outputs

Features

AgentConfig for typed provider configuration

Agent now accepts an optional config: AgentConfig field for provider settings, instead of only direct constructor fields.

from orchflow import Agent, AgentConfig

agent = Agent(
    name="extractor",
    role="Extract structured data.",
    config=AgentConfig(
        model="openai/gpt-5-mini",
        temperature=0,
    ),
)

AgentConfig fields: model (required), temperature, max_tokens, api_base, api_key, timeout, extra: dict[str, Any].

Merge behavior: direct Agent fields (model, temperature, max_tokens) take precedence over config when both are set. config.extra is passed through directly as additional keyword arguments to the LiteLLM acompletion call (e.g. drop_params).

Agent.run_structured(prompt, schema=...)

New method returns parsed structured output instead of plain text.

parsed = await agent.run_structured(
    "Extract name and company from: Ada at OpenAI",
    schema={
        "type": "object",
        "properties": {
            "name": {"type": "string"},
            "company": {"type": "string"},
        },
        "required": ["name", "company"],
    },
)

Behavior:

  • JSON schema dictionaries are converted into a response_format payload ({"type": "json_schema", "json_schema": {"name": ..., "schema": ...}}, using schema["title"] or falling back to "orchflow_schema") and the LiteLLM response content is parsed with json.loads.
  • Pydantic model classes (any class exposing model_validate_json) are passed directly as response_format and parsed via schema.model_validate_json(content). Pydantic is not a core Orchflow dependency; it only needs to be installed by the caller.
  • Empty response content, invalid JSON, unsupported schema types, and Pydantic validation failures all raise the new StructuredOutputError.

New StructuredOutputError exception

Exported from orchflow alongside AgentConfig:

from orchflow import Agent, AgentConfig, StructuredOutputError

Raised for: invalid JSON, Pydantic validation failures, unsupported schema types (not a dict or Pydantic model class), and empty structured content from the model.

New example: examples/structured_agent.py

Demonstrates AgentConfig plus run_structured(...) inside a Flow step, extracting a {"name", "company"} object from free text.

uv run python examples/structured_agent.py

Documentation and roadmap updates

  • docs/api-reference.md, docs/quickstart.md, docs/concepts.md, docs/examples.md, and README.md all add AgentConfig / run_structured(...) usage and snippets.
  • docs/roadmap.md marks AgentConfig, run_structured(...), and JSON/Pydantic schema support as shipped in 0.5.0, and adds a new 0.6.0 - Tool Execution milestone (small tool schema helper, one model-requested tool call followed by a final response; long-running tool loops and MCP remain out of scope).
  • README.md roadmap section now reads 0.5.x: structured agent polish and docs improvements / 0.6.0: evaluate one-turn tool execution.

Fixes

  • Tool-calling NotImplementedError message updated from "outside Orchflow v0.1" to "outside Orchflow v0.5" and now explicitly points users to normal steps: "Agent tool execution is outside Orchflow v0.5. Call tools inside normal steps or create an Agent without tools."
  • Agent.run(...) internals were refactored into _complete, _resolved_model, and _completion_kwargs helpers; behavior for existing direct-field usage (model, temperature, max_tokens) is unchanged and remains backward compatible, now covered by tests/test_agent.py::test_agent_run_uses_fake_litellm_and_returns_text.

Breaking changes

Agent.model is now optional

Who is affected: Code that relies on Agent.model always being a populated string field (e.g. via introspection, serialization, or type-checking against a required str).

Before:

model: str

After:

model: str | None = None
config: AgentConfig | None = None

A model must now come from either the direct model field or config.model. If neither is set, calling run() or run_structured() raises ValueError("Agent requires a model or AgentConfig model") at call time rather than failing at construction time.

Migration: No change required for existing Agent(model="...") call sites, they still work identically. If you were relying on static typing that Agent.model is always a non-None str, update type checks to handle str | None, or pass config=AgentConfig(model=...) instead.

Verification: agent = Agent(name="x", role="y") followed by await agent.run("prompt") without a model or config now raises ValueError instead of proceeding; passing model= or config=AgentConfig(model=...) continues to work.

Read full entry →

© Primitive SDKs

Powered by Browzer