Commit graph

35 commits

Author SHA1 Message Date
Nate Armstrong
59538ff1d9
feat(messages): serve anthropic messages on rust only (#51119)
* feat(messages): apply modify_params rewrites on rust

* feat(messages): mint anthropic workload identity tokens on rust

* feat(messages): apply prompt caching injection on rust

* feat(messages): serve the easy python-only detectors on rust

* feat(messages): retry rejected thinking signatures on rust

* feat(messages): serve response cache reads on rust

* feat(messages): retry rejected strict tools on rust

* refactor(messages): delete the python path for anthropic messages

Provider anthropic on /v1/messages is now RUST_REQUIRED, so it runs on Rust
without LITELLM_RUST or litellm.rust(True) and never falls back to Python.
A call the rules would send to Python (a MESSAGES_PYTHON_FEATURES feature or a
detector that raises) gets UnsupportedParamsError naming the feature, raised
from one place in litellm/messages/dispatch.py. Other providers keep the
Python fallback. A call whose arguments the handler signature rejects still
gets Python's TypeError.

Deleted Python code that only served anthropic Messages:
- workload identity in AnthropicMessagesConfig.validate/avalidate (subclasses
  were never eligible; the Rust host mints the token itself)
- the anthropic Foundry beta-row and affinity-base branch in
  async_anthropic_messages_handler, and the "forward" mode of the beta filter
  that only that branch used

The proxy MCP tools detector now uses the light tool precheck so a function
tool never imports the gateway handler, since a raising detector now refuses.

* feat(messages): forward message threads on rust

The Rust path for anthropic /v1/messages dropped the thread body field, so
a create or continue turn reached Anthropic without its thread or the
message-threads-2026-08-12 beta. It now matches the Python handler from
#50976:

- the host forwards thread with the other body params
- a non-null thread adds the message-threads beta
- a well-formed continue turn keeps advisor blocks in history and derives
  no compaction, per-turn-control or tool-change beta from its messages
- the recorded httpx_response carries the request that was sent, for JSON
  and streamed calls, so the proxy's thread ownership hook resends the
  create turn's betas on a continue as it does for Python

Upstream error.details already reached the exception message unchanged, so
thread_not_found relays and keeps the deployment out of cooldown on both
paths. Parity cases cover create, continue, streaming, sync, retries,
thread_not_found and the ownership hook round trip.

* feat(messages): serve mock responses on rust

* feat(messages): send caller clients and unsupported tls through python on rust

* feat(messages): run pre-request hooks on rust

* feat(messages): run agentic loop hooks on rust

* feat(messages): run the safeguards classifier on rust

* feat(messages): run registered interceptors on rust

* fix(messages): match python stream cache gate, logging and interceptor order on rust

Store a streamed Messages response in the cache only when Python would: a
message_stop event line, no provider error frame and at least one frame with
output. Leave dict results untouched by mark_rust_response so hidden params
never reach result dicts, logging objects or hook digests. Ask interceptors
whether they handle the call after the cache lookup, not at decode time, so a
cache hit never calls can_handle. Emit the track_llm_api_timing service event
for the provider exchange. Bill and log a stream superseded by a router
fallback as the failure of its error frame, with the partial usage warning.
Hand litellm_logging_obj to deployment pre-call hooks as @client does, so the
autorouter baseline cache sees retried requests.

* fix(messages): forward python-only json values to anthropic as python does

Accept any max_tokens, sampling scalar and messages collection that
Python main accepts, and carry values serde_json cannot hold (ints beyond
64 bits, NaN and infinities, strings with lone surrogates) as validated
raw JSON leaves written back verbatim on the wire, so the upstream body
matches Python's json.dumps

* fix(messages): match python's body reads, error bodies, encoded heads and tls warnings on rust

* fix(messages): log python's plain provider header names on rust streams

* fix(messages): match python's request params, logged params and validation on rust

Read a non-bool stream by Python truthiness and send it as given, log the
caller's stream flag as Python's handler does, raise the raw Python errors
main raises for malformed messages, tools, model_info and hook-set
litellm_params or providers, read the tool custom-field drop from
drops_unsupported_params, log the caller's nested fields that
additional_drop_params strips and the Python values of raw JSON leaves,
keep a missing stop_reason absent, and map provider-config rejections
(missing max_tokens, unknown reasoning effort) as Python's HTTP handler
maps them, with Python's text, effort order, model and provider

* fix(messages): match python's tls curve names, redirects, auth token, header encoding and timeouts on rust

* fix(messages): match python on hooked streams, empty cache writes and mcp cache hits

* fix(messages): join already buffered stream reads and trim the env api key as python does on rust

* fix(messages): deliver stream bytes read before a cut, keep wide reply numbers and follow python's redirect limit on rust

A stream cut by a transport error now hands the caller the bytes already
buffered before raising, as aiohttp does for a caller that reads them before
the event loop sees the cut, so the frames and their usage reach the caller and
the failure log. Provider replies keep integers wider than 64 bits exactly and
read numbers that overflow a float as infinity, through the response, the
response cache and the logged result. The provider client follows up to 20
redirects as httpx does, and running out raises Python's text. A
provider_specific_header value the ascii codec cannot encode fails before the
send like a caller extra header. The deployment success hook gets the stream as
AnthropicMessagesStreamingResponse, as Python's handler hands it.

* feat(messages): serve proxy mcp tools on rust

* test(messages): cover hook, agentic and interceptor edge cases in the parity suite

* fix(messages): keep provider raw-json marked strings, drop non-string tool_result texts and key clamped streams as python does on rust

* fix(messages): match python's api key encoding, malformed api_base and string provider header errors on rust

A header value Python's client cannot encode as ASCII now fails before the send
whichever header carries it, so a caller api_key with a non-ASCII character
raises Python's InternalServerError instead of a raw UnicodeEncodeError after
the reply. A caller api_base without an http(s) scheme, or with whitespace,
control or non-ASCII characters, is sent through Python's client at the URL
Python builds, so its relative-URL and invalid-URL errors match, and a base
urlsplit cannot split raises Python's bare ValueError before the send. A
provider_specific_header of the wrong shape is scoped through Python's
ProviderSpecificHeaderUtils first, so a plain string raises Python's
AttributeError.

* fix(messages): keep integer temperature and top_p digits a float cannot hold and raise python's typeerror for a string max_tokens while capping the thinking budget on rust

temperature and top_p now deserialize through recognized::exact_float, which keeps an integer
where float(n) != n (beyond 2**53) as an unrecognized value with its exact digits, so it is sent
upstream and logged as the integer python forwards, and comparisons still go through scalar.rs

cap_thinking_budget_to_max_tokens compares max_tokens <= 1024 first, so a max_tokens python
cannot order against an int (a string, list or dict) raises TypeError "'<=' not supported between
instances of 'str' and 'int'" in both the reasoning effort translation and the adaptive effort
reshape. rust now returns ErrorDetail::UnorderableTypes there, the bridge raises it as a python
TypeError marked as a late rejection, and map_failure hands it to the caller unmapped

* test(messages): format the non-string tool_result parity helper

* fix(messages): flag and log the call before python's pre-send raises on rust

The bridge now hands settings() and map_failure() whether the call is async.
A provider_specific_header of the wrong shape sets the aanthropic_messages
flag before re-raising, as Python's handler has already flagged the call. A
base urlsplit cannot split also logs the resolved model, provider and optional
params first, as Python raises it only after its logger is updated. The
TypeError raised while capping the thinking budget for a string max_tokens
sets the flag too

* feat(messages): serve the web search short-circuit override on rust through the intercept host call

* fix(messages): give the web search entry kwargs its mutable-ok reason

* test(messages): drop the body hook gap the rust short-circuit closes

* fix(messages): name the partially unknown handler types on the short-circuit host call
2026-11-12 00:07:20 +00:00
Nate Armstrong
34314a99f0
fix(messages): make rust messages copy python messages behaviour (#49611)
Closes the Rust vs Python /v1/messages differences the parity suite marks as known gaps, copying Python exactly.
2026-11-02 21:02:52 -08:00
Nate Armstrong
5e08cb0e63
test(messages): add rust vs python messages parity characterization suite (#49610)
Runs every case through the Python /v1/messages path and the Rust path against one recording upstream and compares dispatch, upstream request, outcome, hidden params, standard logging fields and callback kwargs. Known Rust differences are strict xfails tagged with their inventory ID.
2026-11-02 21:02:52 -08:00
Nate Armstrong
bcd80b6aa1
feat(rust): encode LiteLLM response ids on native responses (#46572)
Native Responses now hands back the same managed ids as Python: the
resp_ id carries the provider and deployment, encrypted-content items get
encitem_ ids and wrapped content under affinity routing, and container ids
become cntr_ ids. Encoded encrypted-content items in the input are restored
before the provider request. The codec lives in inference-responses with
goldens taken from litellm/responses/utils.py
2026-10-18 20:52:34 -07:00
Nate Armstrong
4995d319ed
test(rust): add native vs Python parity harness for responses (#46567)
Each case runs one aresponses call through Python and through the native
route against the same recorded OpenAI response and compares the dispatch,
provider request, return value and success log. Known gaps are strict xfails
so the PRs that close them must flip the marker
2026-10-18 20:52:33 -07:00
Mateo Wang
3069a467d0
docs(agents): ban semicolons and colons in human-facing text (#45684)
* 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
2026-10-09 16:36:32 -07:00
devin-ai-integration[bot]
d5173e3d70
test: move 76 live top-level tests to offline unit and integration coverage (#45352)
* test: move 76 live top-level tests to offline unit and integration coverage

* test: keep the mock-param opt-in guard valid once the top-level senders are gone

* test: address review on offline replacements

* docs(test): drop the deleted harness test file from the harness check command

* test: make the key rebind, team member delete and routes integration tests exercise the legacy paths

* test: make fallback, rpm and spend integration contracts deterministic and clean up their rows, assert the rpm limit in usage-based routing

* test: expect the no-deployments error at the rpm limit and cover the strategy check without pre-call checks

* test: hand member cleanups to the scenario instead of growing a budget list, flatten callback kinds

* test: scope the admin health check to the test's own deployment

---------

Co-authored-by: yuneng <yuneng@berri.ai>
2026-10-08 10:33:35 -07:00
devin-ai-integration[bot]
e2302be068
refactor(ocr): remove the Python OCR execution path and require the Rust route (#43081)
* refactor(ocr): remove the Python OCR execution path and require the Rust route

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fmt

* refactor(ocr): tidy the native OCR passthrough binding

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* style(ocr): ruff format the azure passthrough transformation

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* refactor(ocr): resolve passthrough OCR costing in one Rust call

Replace passthrough_url/passthrough_transform with passthrough_response,
which matches the relayed endpoint against each Azure config's path
segments instead of building a fake request to call get_complete_url.
The binding drops the unused headers, status and api_base arguments.

Catch the ValueError/RuntimeError the binding raises so a relayed body
that is not OCR-shaped falls back to the passthrough object instead of
failing logging, and cover the relay against the real binding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ocr): drop the unused LlmProviders import from health check helpers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: drop the ocr_testing job now that tests/ocr_tests is gone

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ocr): restore the live OCR matrix and the ocr_testing job

The public litellm.ocr / aocr / Router interface is unchanged by the Rust
migration, so the live provider matrix still applies. Drops the stale VCR skip
list for the deleted test_rust_bridge.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ocr): import Final in the health check helper tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Yujong Lee <yujong@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 18:18:50 -07:00
kerry
8ecbf3dbc1 test: drop tests that pin provider-owned cost map values
The repo rule is that a test must only fail when litellm code changes, never when a vendor updates a price, renames a field, or drops a model. These tests asserted shipped catalog entries directly, comparing lookup results to literals copied from model_prices_and_context_window.json or requiring named entries to exist or be absent, so every cost map sync could break them without any litellm code changing

Tests that exercise real litellm behavior with an injected local model_cost, invariants like backup parity, and assertions on non-lookup code paths are untouched

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 03:55:51 +00:00
Yujong Lee
96baeb8b04 refactor(rust): remove gateway, config, router, realtime, and trace-parity infrastructure
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 16:00:07 +00:00
Yujong Lee
da839d4a11 fix(harness): skip unavailable Rust traces 2026-09-14 14:01:28 -07:00
Yujong Lee
27981c7d20 fix: preserve rust setting in trace parity 2026-09-14 12:17:15 -07:00
Yujong Lee
db378d8963 fix(harness): preserve unmapped calls in execution traces 2026-09-14 12:08:31 -07:00
Yujong Lee
209fc7afb0 fix(harness): trace OCR Python and native dispatch paths 2026-09-14 12:03:54 -07:00
Yujong Lee
d4869ba710 refactor: print rust and python traces independently 2026-09-14 11:48:25 -07:00
Yujong Lee
eb2ffaae79 fix(harness): rebuild bridge for trace parity 2026-09-14 11:48:25 -07:00
Yujong Lee
561533c596 wip 2026-09-14 11:48:24 -07:00
Yujong Lee
ad5b87eb91 fix: capture reused worker threads in Python traces 2026-09-14 11:47:30 -07:00
yujonglee
347b642bdd
refactor(ocr): complete native lifecycle and preserve Azure auth (#40734)
* refactor(ocr): extract call completion boundary

* fix(ocr): release completion state after dispatch

* test(ocr): prove wrapper completion handoff

* test(ocr): narrow mapped failure assertion

* fix(ocr): preserve wrapper invocation kwargs

* fix(ocr): retain completion through finalization

* fix(ocr): make completion ownership explicit

* refactor(ocr): resolve logging executor explicitly

* fix(callbacks): preserve completion lifecycle behavior

* refactor(ocr): move public OCR into native lifecycle

* refactor(ocr): remove unused rust bridge capability

* wip

* wip

* refactor

* wip

* fix(ocr): preserve reducto native compatibility

* wip

* fix(ocr): document native callable casts

* perf(ocr): bound responses and reduce native scheduling overhead

* refactor(python-bridge): organize placeholder routes

* refactor test

* fix(ocr): normalize DeepSeek document content

* perf(ocr): skip unused callback work and benchmark callback overhead

* fix(ocr): align conversion contracts

* test(ocr): cover official provider response shapes

* fix(ocr): restore Python fallback and honor Rust opt-out

* fixes and refactor

* fix(ocr): preserve Azure Document Intelligence authentication

* fix(rust): enforce OCR response limits and lint contracts

* test(rust): align native OCR contract coverage

* test(ocr): isolate Azure auth precedence coverage
2026-09-12 11:56:49 -07:00
yujonglee
5f2b4d27d7
test(ocr): add SDK callback E2E parity (#40061)
* test(ocr): cover SDK callback parity

* test(ocr): compare callback kwargs
2026-09-07 11:23:12 -07:00
yujonglee
8da735410c
test(ocr): trace callback lifecycle parity (#40063) 2026-09-07 10:44:22 -07:00
Yuneng Jiang
a5a78670d0
test: address review notes on the chronic-test repairs
Drop the two new docstrings, annotate the new locals Final, and replace the
mutable call recorder with a rebuild stub that fails the test if it is ever
reached.
2026-09-04 10:18:15 -07:00
Yuneng Jiang
dbf8fe0f4e
test: repair four chronically failing CI tests
test_no_linear_scans_in_router: #39468 added config_deployments() and
heuristic_v2_router_limit_violation(), which both scan the whole model_list
from admin-only paths (model add/upsert), so add them to the allowlist. The
allowlist becomes a mapping so each exemption carries its reason as data.

test_missing_model_parameter_curl: a request with no model is rejected by the
proxy when nothing can serve it and by the router when a wildcard or default
deployment exists, and by the upstream provider when a wildcard forwards it,
so the message text is not a stable contract. Assert the contract that holds
in every case: HTTP 400 with a non-empty error message.

test_model_group_info_e2e: /model_group/info resolves wildcards, so it can
never return "anthropic/*" verbatim. cc3f9cd65b rewrote the assertion to
expect the raw pattern after claude-3-5-haiku-20241022 left the price map,
which made it unsatisfiable. Assert the expansion instead.

test_should_derive_ocr_mapping_status_from_live_tests: the audit needs a
native bridge built with the trace-parity feature, which CI never builds, so
skip with the harness's own diagnostic instead of erroring. Extract that
check out of ensure_trace_bridge as trace_bridge_error so a pytest run
reports the state without kicking off a maturin rebuild.
2026-09-04 10:10:47 -07:00
Yujong Lee
6675e1fd9c fix: preserve Python 3.10 harness compatibility 2026-09-04 09:13:05 -07:00
Yujong Lee
fae3d224eb Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_python_version_ci
# Conflicts:
#	basedpyright-code-budget.json
#	tests/sdk_function_trace/profiler.py
#	tests/sdk_function_trace/test_profiler.py
2026-09-04 09:01:13 -07:00
yujonglee
b75ac5cf52
feat(python): rename Rust rollout API (#39704) 2026-09-04 08:40:44 -07:00
yujonglee
eb67e5402b
test(ocr): complete Rust unit test parity (#39689)
* feat(ocr): complete Rust unit test parity

* test(ocr): keep parity changes harness-only

* test(ocr): share azure DI native fixture across response tests

* refactor(ocr): colocate gateway unit tests and extract lifecycle integration tests
2026-09-03 21:15:01 -07:00
yujonglee
ee08c36fc0
refactor(tests): restructure rust python harness around strategy definitions (#39628)
* wip

* refactor(tests): move sdk function tracing into rust python harness

* dead code

* fix: handle harness keyboard interrupts

* refactor(tests): deduplicate rust python harness helpers

* fix(harness): expose validated strategy choices

* wip

* refactor(harness): let strategies own parity reports

* docs(harness): update strategy structure

* refactor(harness): localize strategy report views

* wip

* fix(harness): satisfy mapping runner type checks

* fix(harness): clarify trace parity output

* wip

* fix(harness): clarify unit mapping report

* fix(harness): finalize trace parity contracts

* refactor(harness): structure parity contracts

* feat: derive unit test mapping from traces

* feat(harness): map rstest test families

* feat(ocr): port Azure document intelligence tests

* feat(harness): enforce complete unit mappings

* feat(ocr): add reducto core transforms

* feat(harness): classify host-only unit tests

* fix(ocr): complete Rust provider plumbing

* fix(harness): reuse OCR parity workers
2026-09-03 21:15:01 -07:00
Yujong Lee
8c180afd4b test(ocr): record provider fixtures in the migration harness 2026-09-03 10:16:40 -07:00
yujonglee
2c30fe16b0
Merge pull request #38765 from BerriAI/litellm_ocr_sdk_parity_tests
test(harness): add OCR parity with migration strategy runners
2026-09-03 10:16:35 -07:00
ishaan-berri
e058aa68c4
test: add mistral ocr transformation parity coverage (#39482)
* test: cover mistral ocr transformation parity

Co-Authored-By: Claude Code <noreply@anthropic.com>

* test: map mistral ocr parity contracts

Co-Authored-By: Claude Code <noreply@anthropic.com>

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-09-02 20:40:00 -07:00
ishaan-berri
bcd3e2d94d
feat(rust-python-harness): wire existing e2e SDK tests into the matrix (#39463)
Adds chat_completions and transcription as SDK function columns, backed
by the existing rust_bridge test files. Adds a fourth strategy folder,
existing_e2e_test_sdk, that points at already-existing live-API SDK
tests (tests/ocr_tests/ as a whole folder, plus chat completion and
Whisper transcription tests) instead of writing new parity tests.
Extends selector_matches_node with trailing-slash folder selectors so
a whole test folder can back one matrix cell.
2026-09-03 02:50:08 +00:00
ishaan-berri
a5639b8e2a
test: add OCR python-to-rust test parity ledger (WIP) (#39434)
* test: add OCR python-to-rust test parity ledger

* feat: add ledger loader for OCR test parity data

* feat: add drift audit for OCR test parity ledger (WIP, untested)

* fix: correct drift in OCR test-parity ledger

Two entries referenced a typo'd Python test name, four duplicated entries already tracked under TestProxySecurityGuard, five real Python tests in test_rust_bridge.py were untracked, and three real Rust custom_logger tests were missing from rust_only_tests. Found by running validate_ledger.py's audit against the live repo.

* test: add regression coverage for the OCR ledger and audit script

Covers schema validation, AST/regex test enumeration, drift detection on both the Python and Rust sides, LedgerDriftError content, and a live-repo clean-audit guard against future drift.

* test: simplify ledger test to one drift-guard assertion

Replace the ledger-internals unit tests with a single test that runs the real audit against the live repo and asserts every OCR test is accounted for (mapped, unmapped-with-reason, or rust_only), printing the exact diff on failure.

* chore: move OCR test-parity ledger to core/ocr

validate_sub_methods/ mixes strategy-catalog metadata with the ledger. Ledger data belongs under a per-function core/<function>/ path instead.

* fix: point LEDGER_PATH at the new core/ocr location
2026-09-02 19:22:52 -07:00
yujonglee
87618b7da8
doc (#39456) 2026-09-02 18:12:03 -07:00
ishaan-berri
4c51ca72d6
test: add interactive Rust Python parity harness (#39419)
* test: add interactive Rust Python parity harness

* test: simplify Rust Python parity harness structure

* test: show parity confidence by SDK section

---------

Co-authored-by: yuneng-jiang <yuneng@berri.ai>
2026-09-02 15:51:08 -07:00