litellm/litellm-rust/crates/callbacks-legacy-python/AGENTS.md
Yujong Lee c63d46becc
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
LiteLLM Rust / rust-wheel (push) Has been cancelled
refactor(host): split call hooks into facets with the policy in the return type
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
2026-10-14 10:16:21 -07:00

3.3 KiB

  • Target invariants, not completion claims
  • This crate owns compatibility for all existing Python callbacks and loggers, including CustomLogger. facets/ holds LegacyHooks on each facet of the call, one file per facet, each listing the Python callbacks its stages reach. mapping.rs collects those lists and the inventory of Python-owned hooks. A Python-owned entry records an existing path, never permission to invoke it a second time. The native call adapter preserves the Logging contract (function_setup, the deployment hooks, pre_call/post_call, the sync and async success and failure fan-out, the deferred proxy release, the argument sharing those callbacks rely on)
    • Smell test: if a future callback host (callbacks-v1-python, WASM, in-process Rust) could share a piece of this crate, it does not belong here
    • SDK request policy (credential inheritance, the budget and retry-count limits) is a separate argument group supplied by python-bridge
    • The driver in litellm-host-python, the routes and core see one PythonGroup. They never learn which Python objects consume a call
  • Every litellm Python internal Rust still borrows is a variant of LegacyPython, grouped by subsystem, with its signature pinned in python_contract.json
    • The enum only shrinks: when Rust owns a subsystem, delete its group rather than adding a Rust path beside it
    • Calling a user's own callback directly is permanent Python surface and gets its own type outside LegacyPython
  • PublicCall is the caller's call as Logging sees it: the positional arguments, the keyword view as the call rewrites it (setup, deployment hook, preflight) and the bound request object backing omitted keywords. Shared bridge composition hands it to LegacyLogging. Routes use the neutral call boundary
  • LoggingOperation selects legacy logging entrypoints and response handling. It belongs here rather than in shared inference data contracts
  • setup reuses a Logging passed as litellm_logging_obj (the proxy and Router) and otherwise builds one through function_setup. Which callbacks run is Logging's decision, never this crate's
  • Callbacks receive the caller's own objects and may mutate them. This crate alone carries that obligation
    • Retain complete boundary arguments, opaque values, aliases, omitted/default distinctions and deliberate copies. Preserve the deployment-hook kwargs view
    • Before pre_call, re-alias every body key whose value equals the caller's argument to the caller's own object, resolved through litellm_host_python::lookup
    • Retain body/header roots from pre_call to post_call. In-place mutation reaches the wire, envelope field replacement is visible to later callbacks only
  • Success and failure handlers receive the exact selected public response or exception
    • A failure-handler error cannot suppress the other eligible family or replace the mapped provider error. A cancellation ends the call with no further dispatch
    • Dispatch errors never replay provider work or trigger the opposite outcome. The proxy releases deferred success at most once
    • Delivery follows the registry, not the callable's type: direct, awaited, executor-submitted, logging-worker and deferred paths stay distinct
  • Traverse every retained Python edge. close is idempotent and restores the correlation context once