Skip to content

Upgrading to 2.0

kneo-agent 2.0.0 shipped 2026-06-03 (on PyPI). This guide covers the breaking changes from 1.5.x; pip install --upgrade kneo-agent moves you to 2.0.0.

A short, practical "what breaks and how to adapt" walkthrough for users on 1.5.x. The full list is in CHANGELOG.md; this page covers only the parts that affect existing code. 2.0 is a major — behaviour-breaking changes are allowed and bundled here so you adapt once.

TL;DR

  • 2.0 is breaking. Unlike every 1.x release, pip install --upgrade may change behaviour without a code change. Read the breaking changes below before upgrading.
  • Most users are affected by at most one or two: the RetryMiddleware tool-retry default flip (Bridge only), the RunConfig partial-override + validation changes, the OpenAI Agents Adapter call interface, and the workflow .stream() honesty fix.
  • Nothing was removed from the top-level namespace — these are behaviour changes, not surface removals. The public-API name snapshot is unchanged except where noted.
  • Read this page first if you: rely on RetryMiddleware retrying tool calls; pass a partial RunConfig per run; set temperature/max_iterations via with_defaults/skill defaults; pass a custom runner to the OpenAI Agents Adapter; or call .stream() on a Concurrent / Handoff / GroupChat / graph (or non-streamable Sequential) Workflow.

Breaking changes

1. RetryMiddleware no longer retries tool calls by default

RetryMiddleware(retry_tool_calls=...) now defaults to False (was True). Non-idempotent tools — payments, writes, sends — are no longer re-executed on a transient failure unless you opt in. 1.3.0 shipped a docstring .. warning:: flagging this; 2.0 makes the flip. No deprecation warning is emitted — this is a clean major-version flip.

Scope: RetryMiddleware is a Bridge-runtime feature (a documented no-op under the Adapter / Native paths), so this only changes behaviour for Bridge agents that use it.

Migration — if you want tools retried (your tools are idempotent, or you accept re-execution), opt back in explicitly:

from kneo_agent.middleware import RetryMiddleware

# 1.x default behaviour, now explicit:
RetryMiddleware(max_attempts=3, retry_tool_calls=True)

Model-call retries are unchanged (retry_model_calls still defaults to True).

2. A partial RunConfig override no longer clobbers agent defaults

In 1.x, a per-run RunConfig replaced max_iterations, temperature, and tools wholesale — even when you left them at RunConfig's own defaults. Passing RunConfig(system_prompt="…") silently reset max_iterations to 10, temperature to 0.7, and tools to [], discarding whatever the agent was built with.

In 2.0, a partial override changes only the fields you actually set; unspecified fields keep the agent / skill default.

agent = (
    AgentBuilder()
    .use_runtime(my_runtime)
    .with_defaults(max_iterations=25, temperature=0.2)
    .build()
)

# 1.x: max_iterations silently snaps back to 10, temperature to 0.7
# 2.0: max_iterations stays 25, temperature stays 0.2 — only system_prompt changes
await agent.run("…", run_config=RunConfig(system_prompt="Be terse."))

Migration — if you were relying on a partial override to reset fields to RunConfig's defaults, restate them explicitly:

RunConfig(system_prompt="Be terse.", max_iterations=10, temperature=0.7)

If you already restate every field in every override (the defensive 1.x pattern), nothing changes for you.

3. RunConfig validation now applies to merged agent / skill defaults

Coupled to change 2: in 1.x, RunConfig.__post_init__ validated values only at direct construction, so a bad value supplied through AgentBuilder.with_defaults(...) or a skill-file defaults block bypassed validation, and a non-numeric value raised TypeError rather than the ValueError the changelog promised. 2.0 revalidates the resolved config, so:

  • An out-of-range agent / skill default (e.g. max_iterations=0, temperature=-1) now raises ValueError when the run config is built, instead of silently taking effect.
  • A non-numeric value raises ValueError (was TypeError).
  • temperature validation is stricter: bool (True/False) and non-finite values (NaN / inf) are now rejected with ValueError (1.x accepted them — True >= 0 and nan < 0 / inf < 0 all passed the old < 0 check).

Migration — fix any out-of-range defaults in your with_defaults(...) calls and skill files; if you catch this error, catch ValueError.

4. OpenAI Agents Adapter call interface; ADK & LangChain are pass-through

The OpenAI Agents Adapter's call interface now matches the real openai-agents Runner: it calls Runner.run(starting_agent=…) and Runner.run_streamed(…).stream_events() (1.x used an invented runner.run(agent=…) / runner.stream() shape) and reads object- or dict-shaped results.

Migration — if you passed a custom runner to AdapterAgentFactory.for_openai(...) that implemented the old run(agent=…) / stream() shape, update it to run(starting_agent=…) / run_streamed(…).stream_events().

Note — adapter fidelity (2.0–2.1 vs 2.2.0). In 2.0–2.1 the OpenAIAgentsAdapter / LangChainAdapter were demonstration adapters, validated against SDK-shaped fakes and not fully wired to the live SDKs. Resolved in 2.2.0 (additive, no signature change): the OpenAI adapter builds a real agents.Agent and reads tool metadata from item.raw_item, and the LangChain adapter handles both a LangGraph create_agent graph and a legacy AgentExecutor. The native runtime (NativeRuntimeFactory.for_openai(...)) remains a fine choice for the OpenAI path. See the 2.2.0 release notes.

The Google ADK and LangChain Adapters are pass-through: the wrapped runner / executor owns its own configuration, and RunConfig run-level fields are not injected per call. (An earlier 2.0 draft had the LangChain adapter forward max_iterations — reverted: the pinned langchain 1.x has no AgentExecutor, so that was a no-op there and clobbered a user-set value on a no-override run.) temperature is uninjected on every adapter.

5. Workflow .stream() is honest about what can stream

Several orchestration workflows exposed a .stream() method whose only behaviour was to run the whole workflow to completion and emit the result as a single chunk — while supports_streaming() correctly returned False and Agent.stream() raised StreamingNotSupportedError. That split was contradictory: a direct .stream() call "worked" (degenerately) while the agent-level path refused.

In 2.0, .stream() raises StreamingNotSupportedError on workflows that cannot incrementally stream — ConcurrentWorkflow, HandoffWorkflow, GroupChatWorkflow, and the graph Workflow — matching their supports_streaming() == False. SequentialWorkflow still streams genuinely when its participants can; but when a participant can't stream, its .stream() now raises StreamingNotSupportedError instead of the 1.x NotImplementedError (the new type does not subclass NotImplementedError, so update any except NotImplementedError around it).

Migration — check supports_streaming() before calling .stream(), or use .run() for the non-streaming workflows:

if workflow.supports_streaming():
    async for chunk in workflow.stream(messages, config):
        ...
else:
    result = await workflow.run(messages, config)

If you need the old run-to-completion-then-emit behaviour, call .run() and yield the result yourself.

What didn't change

  • The top-level kneo_agent.__all__ name set — 2.0 changes behaviour, not the exported surface (the public-API snapshot test is unchanged save for any explicitly noted entry). Names you import keep importing.
  • SequentialWorkflow streaming — unchanged.
  • temperature on the OpenAI Agents adapter — still not injected (now consistent across all adapters by design).
  • The Bridge / Native execution paths' RunConfig handling, model-call retry defaults, MCP knobs, the middleware bundle's other defaults, and the provider version floors.
  • The factory advisory UserWarnings (BridgeAgentFactory.for_openai / for_google_adk) remain supported advisories — not promoted to deprecations. Both entry points keep working.

CHANGELOG and policy

Full diff: CHANGELOG.md. Public-API stability guarantees and the deprecation policy: api_stability.md. The per-item reasoning and the scope-freeze decisions behind this guide live in ../releases/archive_TODO-2.0.md.