mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-09 08:18:12 +00:00
* docs(agents): ban semicolons and colons in human-facing text Tighten the writing rule so ";" and ":" are only allowed where the sentence would be nonsensical without them, rewrite the existing semicolons in AGENTS.md to follow it, and note that docs (litellm-docs) keep their trailing "." * docs(agents): rewrite semicolons in nested AGENTS.md files * docs(agents): hand-maintain the dashboard Next.js warning and sync e2e CONTRIBUTING lines * docs(agents): point at the PR template path that exists * docs(agents): restore the generated Next.js block in the dashboard AGENTS.md
7.7 KiB
7.7 KiB
Layering
gateway-inference(serving) ->inference-<fmt>(one API's call) ->inference(shared base)- This crate is the shared base every format crate builds on
RouteError(src/error.rs),CallOptions(src/lib.rs),CallContext(src/context.rs)- diagnostic spans (
src/diagnostic.rs), outbound send and signing (src/outbound.rs), provider resolution (src/provider.rs),CoreResources(src/resources.rs) - response caching (
src/caching.rs):Cachable,StreamCachable,CacheRequest,CallCache,execute_unary,execute_streaming, stream capture - Shared test helpers belong in litellm-inference-testing, used only as a dev dependency
- Nothing here names an API format. Format-specific code, constants, tests and test builders live in their
inference-<fmt>crate - Never depend on an
inference-*crate from here
Format crates
inference-chat,inference-messages,inference-responses,inference-ocr,inference-transcription, one per API- Each owns its entrypoint, route request types (
*Request<'a>), credential fallback, provider dispatch, and the handler glue that runs a provider config - Each owns its
Protocol, itsCachable/StreamCachableimpl, its constants, and its tests inference-*is orchestration only- shared API data contracts belong in
litellm-llms-types - adapter contracts and shared transformation machinery in
llms/src/base_llm/<format>/ - provider policy in
llms/src/<provider>/<format>/
- shared API data contracts belong in
- Select concrete adapters, then invoke their contracts instead of applying one provider's policy to every call
- Route types describe call envelopes and execution state, not duplicate public payload schemas
- Format-specific rules go in that crate's
AGENTS.md
Routes
- Hosts assemble route objects from shared
CoreResources, HTTP settings, and secret sources- each route owns its provider client and authentication dependencies
- gateway routes live for the gateway lifetime. Python assembles routes per call from its settings snapshot
- Calls pass
Interceptorsand an optionalObservationSenderseparately- use
&()for no hooks andNonefor no observer - handlers accept
Interceptors, never a concreteChannelInterceptors
- use
- Construction does no work. Preparation and lifecycle observation begin when the future is polled
- Native observers receive start and terminal events through the shared call runner. A stream retains its lifecycle until exhaustion, error, or drop. Hosted routes leave terminal observation to their driver
- A hosted format's
route.rsdeclares the concreteProtocoland a route method that takes a typed request and builds alitellm_host::call::HostedMachinewithhosted_call. Audio transcription has no hosted machine and returns its response directly- the shared call plumbing owns stream opening, delivery, backpressure, and detachment
- request decoding belongs to the boundary before the machine starts
- route closures only supply execution dependencies and route-specific host capabilities
- Python uses its own shared driver and preserves caller-task callback execution
- Not here: serving HTTP (axum routes, extractors), config file reading, rollout state, databases, or callback execution of any kind. Routes run as machines that yield host operations and call events. Which integrations consume those events is the host's business
Crate boundaries
- Dependencies only point down. Python package names identify counterparts, not ownership
litellm-llms-typesowns shared inference API contracts, grouped by format: pure serde data and shape validation, no I/Olitellm-core-utilsmirrorslitellm/litellm_core_utils/: pure helpers (provider resolution, prompt factory, call arguments, settings lookup and layer merge), no network I/Olitellm-httpis Rust-only and route-neutral: settings resolution, pooledreqwestclients, TLS, proxies, the SSRF-safe media fetcher, request and header helpers, and transport errorslitellm-llmsmirrorslitellm/llms/:base_llm/<api>/transformation.rs,<provider>/<api>/transformation.rs, andbase_llm/ocr/handler.rslitellm-inference-<fmt>mirrors the route packages (litellm/ocr/,litellm/messages/, ...)- Provider code never imports from
inferenceorinference-* - Handlers belong in
inference-*orllms, never in a host crate - Import every item from its canonical path. Never re-export another crate's items or give an item a second public path. The only allowed re-exports are a private submodule surfacing its item at its module root (
mod error; pub use error::Error;) and each format crate'spub use litellm_inference::RouteError as Error;, which keeps the pre-split<fmt>::Errorname
Error placement
- The workspace
Error definitionsrules shape each crate's error. This section decides which crate and module a failure belongs to - A failure is declared once, by the lowest crate that raises it
- every crate above nests it unchanged (
#[error(transparent)] Auth(#[from] litellm_auth::Error)) or maps it once at its boundary, assrc/error.rsdoes forlitellm_llms::Error RouteErrorcollects route failures for every format crate and never re-declares a variant a lower crate raises
- every crate above nests it unchanged (
- Scope follows the concept, not the first caller
- an error type under
litellm-llms's<provider>/is private to that provider: no other provider and nothing inbase_llmmay import it - a failure two providers or two routes can hit (wire framing, stream event decoding, a malformed provider response) belongs to the crate that owns the concept:
litellm-framerfor framing,litellm_llms::Errorfor the transformation layer
- an error type under
litellm_llms::Error(crates/llms/src/error.rs) is the one transformation error for every provider and API.base_llm/ocr/error.rsis the recorded exception until OCR folds into it
Response caching and accounting
- Attach a
litellm_cache_response::ScopedCachewithroute.with_cache(cache). Cached and uncached routes use the sameexecuteandmachinemethods CallOptionscarries a scope-freeCachePolicyand observation. Per-call policy never replaces the attached scope or service- Provider transport does not own cache orchestration. Stream capture stays in
src/caching.rs - This crate and the format crates own request identity, typed response reconstruction, and stream capture and replay
cache-responseowns cache policy, namespacing, scope encoding, versioned envelopes, and freshness- The SDK explicitly chooses shared scope. The gateway derives isolated scope from authenticated identity before attaching its service
ExecutionFactsare delivered through the awaitedResultReadyhost operation for both provider and cached results, before public response processing or stream opening- facts carry resolved model/provider and result source, including the hit key
- usage remains in the typed response or delivered stream, where completion and cancellation determine what was actually reported
- passive observation is not an accounting delivery mechanism
- Inference does not calculate prices, charge budgets, or update rate-limit counters
- the legacy Python callback adapter translates execution facts into the existing Python logging contract. Python remains the accounting owner on that path
- native gateway accounting belongs to gateway dependencies, independently of
host-python - response-cache services expose no coordination counters or reservation APIs. A shared Redis deployment does not make response storage and accounting coordination the same dependency
- Cache lookup follows provider preparation, credential resolution, and the request interceptor
- keys describe the effective provider URL, authenticated headers, and rewritten body
- signed requests bypass caching until the signing identity has a stable cache representation