One trait per payload replaces CallHooks and its on_event bag: ArgumentHooks,
ProviderHooks, ResponseHooks and OutcomeHooks over a Surface type bag. Transforms
fold through the groups and stop on the first error, outcomes reach every group
and only a Cancelled group stops the rest, so ChainRuntime, HookGroupAdapter,
DynCallHooks and the runtime terminal match are gone. Hooks<S> composes mounted
groups, request stages in mount order, response and terminal stages in reverse
host-python keeps PythonSurface, the per-stage cancellation guard behind
PythonSurface::group, and report_unless_cancelled as the one place an ordinary
outcome error becomes unraisable. The legacy adapter implements each facet in
its own file under facets/, which also lists the Python callbacks each stage
reaches, and mapping.rs only collects them. SdkPolicy does its work in prepare,
so the logging-before-policy ordering rule is no longer load-bearing
The public stream wrappers only forwarded to the lifecycle streams and added the
header dict, so the streams now carry _hidden_params themselves. lifecycle.py no
longer held any call lifecycle, just the Execution driver, so it is renamed to
execution.py and absorbs the synchronous and asynchronous adapters
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rust's run_call built Python streams itself and imported
litellm.rust_bridge.streams by name, so sync calls were driven in Rust
while async calls came back as a Rust-made coroutine. Every native
inference entrypoint now returns one unstarted Execution and
public_execution.py drives it with lifecycle.run or lifecycle.drive,
wrapping streams and _hidden_params on the Python side. Rust only
imports the lifecycle step classes through StepTypes, and
python-bridge/src/lifecycle.rs, PythonLifecycle and streams.py are gone.
The adapter trusts the _native.pyi contract with a single cast instead
of a per-request runtime_checkable isinstance. Binding tests assert the
adapter starts the execution and returns its completed value rather
than checking callable identity.
missing_state() and the inline PyRuntimeError/PyTypeError messages become Error variants with one From<Error> for PyErr, and InvokeError derives thiserror so From<PyErr> comes from #[from]
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>