Migrating from 0.7.x to 0.7.9¶
LazyBridge 0.7.9 is the simplification release. The framework
had no real users yet, so we ship breaking changes without
deprecation paths or shims. Net public surface change: −1 in
lazybridge.__all__ (50 → 49), plus 5 deleted Agent.from_*
class methods. No new public concept.
The single LLM-friendliness lever is consistency: one canonical form per concept, errors always raise, no opt-in modes.
If you wrote code against 0.7.x, this page is the deletion-by-deletion codemod you need to run.
TL;DR — what changed¶
| Removed | Replacement | One-liner |
|---|---|---|
Agent.from_model("X") |
Agent("X") |
string-positional shortcut |
Agent.from_engine(e) |
Agent(engine=e) |
canonical ctor |
Agent.from_chain(a, b) |
Agent.chain(a, b) |
non-trivial factory kept; alias gone |
Agent.from_plan(*steps, store=…) |
Agent(engine=Plan(*steps, store=…)) |
canonical ctor |
Agent.from_parallel(*agents) |
Agent.parallel(*agents) |
non-trivial factory kept; alias gone |
AgentRuntimeConfig / ResilienceConfig / ObservabilityConfig |
flat kwargs on Agent(...) |
dict-spread for fleet defaults |
_UNSET precedence game |
flat kwargs only | each kwarg has a real default |
Tool(fn, mode="auto", schema_llm=…, allow_llm_schema=True) |
Tool(fn) (default mode="signature") or explicit mode="hybrid"/mode="llm" |
no graceful-fallback ladder |
_ParallelAgent (returns list[Envelope]) |
ParallelAgent (returns one folded Envelope) |
run_branches(task) for per-branch list |
wrap_tool(...) |
private _wrap_tool |
use Tool.wrap(...) instead |
tool_choice="parallel" |
tool_choice="auto" |
parallel is automatic, not a config |
Old doc/ directory |
gone | 1.2 MB of stale fragments removed |
Plus 9 silent-fallback paths converted to explicit errors —
from_step("typo") raises PlanRuntimeError, unknown provider
strings raise ValueError, MCP non-object schemas raise
ValueError, Envelope.text() on unsupported payloads raises
TypeError, etc.
Detailed migrations¶
1. Factory aliases → canonical ctor¶
# 0.7.x
agent = Agent.from_model("claude-opus-4-7", tools=[search])
# 0.7.9
agent = Agent("claude-opus-4-7", tools=[search])
# or, more explicit:
agent = Agent(engine=LLMEngine("claude-opus-4-7"), tools=[search])
# 0.7.x
pipeline = Agent.from_plan(
Step("research"),
Step("write"),
store=Store(db="run.sqlite"),
checkpoint_key="research",
resume=True,
)
# 0.7.9 — Plan kwargs live on Plan, Agent kwargs on Agent
pipeline = Agent(
engine=Plan(
Step("research"),
Step("write"),
store=Store(db="run.sqlite"),
checkpoint_key="research",
resume=True,
),
name="research_pipeline", # required when engine is non-LLM (T7)
)
# 0.7.x — alias of Agent.chain
agent = Agent.from_chain(researcher, writer)
# 0.7.9 — only the real factory remains
agent = Agent.chain(researcher, writer)
Agent.from_provider("anthropic", tier="top") is kept (it's the
only Agent.from_* that does non-trivial work — resolves a tier
alias to the provider's current model).
2. Config objects → flat kwargs¶
# 0.7.x
agent = Agent(
engine=LLMEngine("claude-opus-4-7"),
resilience=ResilienceConfig(timeout=60, max_retries=5, cache=True),
observability=ObservabilityConfig(session=session, name="agent-A"),
)
# 0.7.9 — flat kwargs (no precedence game, no _UNSET sentinel)
agent = Agent(
engine=LLMEngine("claude-opus-4-7"),
timeout=60,
max_retries=5,
cache=True,
session=session,
name="agent-A",
)
For fleet defaults, use a Python dict spread:
PROD_DEFAULTS = dict(
timeout=60,
max_retries=5,
max_output_retries=2,
cache=True,
verbose=False,
session=session,
)
# Same end-user value as a config object, no precedence game,
# better introspectability, fewer concepts to learn.
research = Agent(**PROD_DEFAULTS, engine=LLMEngine("claude-opus-4-7"), name="research")
write = Agent(**PROD_DEFAULTS, engine=LLMEngine("claude-opus-4-7"), name="write")
CacheConfig is kept — it carries real semantic value (enabled
/ ttl), consumed inside LLMEngine as the canonical cache
representation. Pass cache=CacheConfig(ttl="1h") for the longer
Anthropic TTL.
3. Tool mode="auto" → explicit mode¶
# 0.7.x
search = Tool.wrap(search_web, name="search") # default mode="auto"
# silently degrades signature → hybrid → llm depending on schema_llm
# 0.7.9 — both ``Tool.wrap()`` and ``Tool()`` default to mode="signature"
search = Tool.wrap(search_web, name="search")
# Explicit hybrid / llm enrichment now requires opt-in:
enriched = Tool.wrap(
search_web,
name="search",
mode="hybrid",
schema_llm=engine,
)
Passing mode="auto" raises ValueError at construction.
4. _ParallelAgent → ParallelAgent¶
# 0.7.x
fan = Agent.parallel(researcher_us, researcher_eu, researcher_asia)
results = fan("Same task for everyone") # → list[Envelope]
for r in results:
print(r.text())
# 0.7.9 — single Envelope (matches the framework invariant)
fan = Agent.parallel(researcher_us, researcher_eu, researcher_asia)
env = fan("Same task for everyone") # → Envelope (joined)
print(env.text()) # labelled-text join across branches
# For typed per-branch access:
import asyncio
branches = asyncio.run(fan.run_branches("Same task for everyone")) # → list[Envelope]
for r in branches:
print(r.text())
The wrapper Envelope's metadata.nested_* rolls every branch's cost
up; the first non-None branch error propagates through .error.
5. wrap_tool → Tool.wrap()¶
# 0.7.x
from lazybridge.tools import wrap_tool
t = wrap_tool(some_callable_or_agent)
# 0.7.9 — wrap_tool is private (_wrap_tool); use Tool.wrap() factory
from lazybridge import Tool
t = Tool.wrap(some_callable_or_agent, name="...") # name= required for callables
The module-level :func:lazybridge.tool (lowercase) is a thin
backwards-compat alias for :meth:Tool.wrap and continues to work
indefinitely; new code should reach for Tool.wrap so the factory
sits next to the constructor on the class.
6. Silent fallbacks → explicit errors¶
| 0.7.x behaviour | 0.7.9 behaviour |
|---|---|
from_step("typo") warns + uses start envelope |
raises PlanRuntimeError with typo-aware "Did you mean?" |
LLMEngine("unknown-model-xyz") warns + routes to Anthropic |
raises ValueError listing known providers |
MCP server emits non-object inputSchema |
raises ValueError (was silently {}) |
Envelope.text() on custom class |
raises TypeError (was str(payload) fallback) |
Memory(summarizer_timeout=2.0) |
warns at construction (timeout almost always fires) |
BaseProvider._resolve_model empty |
raises ValueError (was empty string) |
To opt back into the legacy 0.7 silent-default for unknown models (rarely needed):
7. Agent(name=...) required for non-LLM engines¶
# 0.7.x — silently named "agent"; collided when used as a tool
pipeline = Agent(engine=Plan(Step("research"), Step("write")))
# 0.7.9 — must pass an explicit name
pipeline = Agent(
engine=Plan(Step("research"), Step("write")),
name="research_pipeline",
)
The engine factories (supervisor_agent, human_agent, Agent.chain)
supply sensible defaults so you don't need to retype name= for them.
8. Agent(model=..., engine=non-LLM) raises¶
# 0.7.x — model= silently ignored
agent = Agent(model="claude-opus-4-7", engine=Plan(Step("x")))
# 0.7.9 — raises ValueError; pass model on the engine itself
agent = Agent(engine=Plan(Step("x")), name="agent")
# (model is irrelevant to a Plan engine)
New in 0.7.9 — adopt these¶
The release deleted more than it added, but several new surfaces are worth picking up on the way through:
| Symbol | Purpose | Reference |
|---|---|---|
lazybridge.matrix.provider_capabilities() / native_tool_support() |
Declarative provider-capability aggregator (ClassVars → typed dict). Read-only; safe for docs / introspection / capability-aware error messages. | reference/providers.md |
lazybridge.PROVIDER_ALIASES |
Snapshot of the 12 model-string → provider routing aliases. Read it instead of reaching into _PROVIDER_ALIASES. |
reference/providers.md |
LLMEngine.provider_aliases() |
Fresh-copy classmethod variant of PROVIDER_ALIASES. |
reference/providers.md |
lazybridge.store.encryption.EncryptedStoreAdapter |
At-rest Fernet encryption wrapper for Store. Optional install: pip install 'lazybridge[encryption]'. Supports MultiFernet key rotation. |
reference/state.md |
BaseProvider capability ClassVars |
supports_streaming / supports_structured_output / supports_thinking + supported_native_tools. Subclasses override when a backend doesn't. |
reference/providers.md |
| Standard error-message format | Every PlanCompileError / PlanRuntimeError / UnsupportedFeatureError carries a four-part body (what failed → what was passed → what's expected → concrete fix snippet). LLM assistants should read the bottom line verbatim. |
for-llms/error-recovery.md |
Session.emit warn-once dedup |
Per (exporter, exception class) pair, with a suppressed-count summary. No more log floods when an exporter goes flaky. |
reference/session.md |
OTel gen_ai.agent.nesting_level span attribute |
Distinguishes root agent spans from nested sub-agent spans for dashboarding. | guides/advanced/otel.md |
examples/llm_assistant/ |
Six runnable, MockAgent-backed examples covering the canonical patterns. | examples/llm_assistant/ |
docs/for-llms/codegen-contract.md + lazybridge/llms.json |
LLM-targeted Always/Never rules + machine-readable canonical-form contract. | for-llms/codegen-contract.md |
What's identical¶
Agent("model")string-positional shortcut works as before.Agent(engine=LLMEngine(...))canonical ctor unchanged.Agent.chain/Agent.parallel/Agent.from_providerkept.Tool.wrap(fn, name=...)factory unchanged surface; default mode now matches the explicitTool()ctor (bothmode="signature").- All sentinels (
from_prev,from_start,from_step,from_parallel,from_parallel_all,from_memory,from_agent) unchanged. - All ext modules (
lazybridge.ext.hil,.mcp,.planners,.evals,.viz,.gateway) unchanged. CacheConfigkept (carries real semantic value).Toolandtoolboth still public;Tool.wrap()is the canonical construction path.
Mechanical migration¶
LazyBridge does not ship a turnkey codemod for the 0.7 → 0.7.9 migration; the surface that changed is small enough that the patterns in the TL;DR table above are usually the fastest path.
For ad-hoc migration, pytest against the 0.7 test you're porting
will surface every breakage point one at a time — the new error
messages tell you exactly what to change at each call site.