Commit graph

39866 commits

Author SHA1 Message Date
Ishaan Jaffer
8a40dcd84f
ci: run tests/test_litellm/ocr in the misc unit-test group
The OCR test directory was not wired into any CI test group, so its
coverage never uploaded to Codecov and patch coverage failed for new
OCR lines. Add it to the misc group.
2026-06-23 12:27:58 -07:00
Cursor Agent
7d6f03744a
fix(ocr): preserve injected rust bridge across toggle calls
use_litellm_rust() unconditionally assigned the keyword default of None to
_rust_ocr_impl, so any call without ocr= silently dropped a previously
injected bridge. Use a sentinel default so omission preserves the impl
while ocr=None still clears it explicitly.
2026-06-23 16:02:42 +00:00
Cursor Agent
f99f0cd8ca
refactor(ocr): inject the rust bridge via a typed seam, drop the importlib cycle dodge
The rust OCR path was reached through importlib.import_module both for the
bridge module and for probing the native extension, purely to keep CodeQL from
flagging a cyclic import. rust_bridge has no litellm imports, so it is a leaf
and main.py can import it statically without any cycle; the dance is gone

Bridge selection now goes through a typed RustOcr Protocol and a load_rust_ocr()
seam. use_litellm_rust() takes an optional injected bridge, so an embedder (or a
test) can supply an alternative without reaching into sys.modules. The rust-path
body moves into _run_rust_ocr(), which receives its dependencies (the bridge
callable, the logging object, the key resolver) as arguments and is unit-tested
by passing fakes in rather than monkeypatching class methods or module globals

The tests are rewritten around that injection: the bridge is provided via
use_litellm_rust(ocr=...), pre_call is observed through a spy logging object, and
the missing-extension fallback is covered by load_rust_ocr() returning None when
no wheel is built. Types were tightened along the way (a cast for the logging
object, OCRResponse.model_validate for the bridge result) so no basedpyright
per-rule count increases

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-06-23 08:15:11 +00:00
Cursor Agent
b87dcb2c97
fix(ocr): log resolved api_base and headers on Rust path 2026-06-23 07:21:58 +00:00
Cursor Agent
b126fbb439
fix(ocr): honor timeout, logging, and missing-bridge fallback on Rust OCR path
- Forward the caller's timeout into the Rust bridge so the fixed 600s client
  ceiling no longer overrides shorter deadlines or the library default.
- Run update_from_kwargs and pre_call before invoking the Rust shortcut so
  observability, callbacks, and spend tracking match the Python path.
- Fall back to the Python OCR path when litellm_python_bridge isn't importable
  instead of raising ImportError to callers.
- Truncate upstream Mistral OCR error bodies before they cross the host
  boundary to avoid leaking document or prompt contents in CoreError::Http.
2026-06-23 05:05:45 +00:00
Ishaan Jaffer
39d132f2a6
Merge remote-tracking branch 'upstream/litellm_internal_staging' into litellm_hotfix_rust_mistral_ocr
# Conflicts:
#	tests/test_litellm/interactions/test_openapi_compliance.py
2026-06-22 21:14:26 -07:00
Ishaan Jaffer
657c062c3f
rust(mistral): document that secret-manager resolution happens on the Python side 2026-06-22 21:05:16 -07:00
Ishaan Jaffer
097bab9441
test(ocr): assert rust path resolves key via secret manager 2026-06-22 21:05:16 -07:00
Ishaan Jaffer
a21a80751d
ocr: resolve mistral key via get_secret_str before the rust path (secret-manager parity) 2026-06-22 21:05:16 -07:00
Ishaan Jaffer
53f059a1d3
test(interactions): add budget_exceeded to expected status enum (Google updated the published spec) 2026-06-22 20:58:49 -07:00
Yassin Kortam
a9de75b1f7
fix(realtime): stop revalidating realtime events at the logging boundary (#31054)
Realtime websocket sessions emit events outside the OpenAIRealtimeEvents
union (e.g. rate_limits.updated, response.function_call_arguments.delta,
surfaced when logged_real_time_event_types="*"). Building
LiteLLMRealtimeStreamLoggingObject revalidated every stored event against
the 16-member union, producing thousands of ValidationErrors per session
(12,670 for a ~281-event session). That synchronous work blocked the
asyncio event loop, degrading realtime time-to-first-audio and dial latency
and tripping readiness probes, and the raised error discarded the session
usage so no cost was tracked.

Type results as SkipValidation[OpenAIRealtimeStreamList] and serialize the
events verbatim, so already-formed event dicts are not revalidated. The
flood drops from 12,670 errors to 0 and the combined usage survives to the
cost calculator.

Resolves LIT-3919
Resolves LIT-3920
2026-06-22 20:43:03 -07:00
Ishaan Jaffer
ca904f5607
test(ocr): assert rust_ocr returns the raw bridge dict 2026-06-22 20:37:21 -07:00
Ishaan Jaffer
17791a2174
ocr: wrap rust bridge dict into OCRResponse at the call site 2026-06-22 20:37:21 -07:00
Ishaan Jaffer
718b1d550c
ocr: make rust_bridge a leaf (return raw dict, no litellm import) so the CodeQL autofix stops re-breaking it 2026-06-22 20:37:21 -07:00
ishaan-berri
c9b712c823
Potential fix for pull request finding 'CodeQL / Cyclic import'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-06-22 20:26:59 -07:00
ishaan-berri
bbbb9ededb
Potential fix for pull request finding 'CodeQL / Cyclic import'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-06-22 20:26:48 -07:00
Ishaan Jaffer
fe1cbe505d
ci: re-trigger checks 2026-06-22 20:14:54 -07:00
Ishaan Jaffer
d6b9928dc1
ci: re-trigger checks 2026-06-22 20:10:01 -07:00
yuneng-jiang
6f6aec2930
fix(proxy): serialize team budget_limits to JSON in jsonify_team_object (#31045)
POST /team/new with any budget_limits returned 500 because
jsonify_team_object serialized members_with_roles but left budget_limits
as a raw Python list, which Prisma's Json column rejects. /team/update
and /key/generate worked only because each json.dumps the windows itself.
Serialize budget_limits in the shared helper, guarded by isinstance(list)
so the pre-serialized /team/update path is unaffected.
2026-06-22 19:10:34 -07:00
Mateo Wang
0d08a57dc4
fix(ui): clarify OpenAI-compatible provider dropdown labels (chat vs legacy completions) (#31046)
* fix(ui): clarify OpenAI-compatible provider dropdown labels (chat vs legacy completions)

* style: change labeling to be clearer
2026-06-22 19:00:19 -07:00
Ishaan Jaffer
a4b4965888
ocr: modernize rust_bridge typing (PEP 604, drop typing.Any/Dict) to satisfy strict-rule gate 2026-06-22 18:56:24 -07:00
Yassin Kortam
b24b964e04
fix(passthrough,streaming): recover cost on interrupted and agentic Anthropic streams (#31035)
Streaming and pass-through requests could be logged with $0 cost or dropped from
SpendLogs entirely while the upstream provider still billed every token. This
closes the leak paths not already covered by #30160, #30787 and #30788.

- Catch a stream_chunk_builder raise in the core CustomStreamWrapper (sync and
  async). Large agentic tool-use / thinking streams can make assembly re-raise
  as APIError from inside the except-StopIteration handler, where the sibling
  except does not catch it, so it escaped __next__/__anext__ and dropped the
  request; recover best-effort usage from the raw chunks instead
- Add a usage-only fallback for Anthropic streaming pass-through: when
  stream_chunk_builder returns None or raises, rebuild usage from the
  message_start / message_delta SSE events via AnthropicConfig.calculate_usage so
  cache, web-search and geo tokens are priced instead of left at $0
- Decode buffered pass-through bytes with errors="replace" so a stream cut
  mid-multibyte-sequence still logs the usage events already received
- Record response_cost into model_call_details on the pass-through success path
  (it is read from there, not from kwargs), matching the gemini/cohere/openai
  handlers
- Name the key (alias + masked key) in the virtual-key BudgetExceededError so
  operators don't have to reverse-map spend back to a key
2026-06-22 18:51:13 -07:00
Ishaan Jaffer
88a9e6ed57
ocr: guard OCRResponse under TYPE_CHECKING so the annotation resolves 2026-06-22 18:49:03 -07:00
Ishaan Jaffer
3188f93be0
ocr: lazily import rust bridge inside ocr() to break the import cycle the CodeQL autofix mangled 2026-06-22 18:49:03 -07:00
ishaan-berri
54577a691e
Potential fix for pull request finding 'CodeQL / Module-level cyclic import'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-06-22 18:36:24 -07:00
ishaan-berri
8325d52451
Potential fix for pull request finding 'CodeQL / Module-level cyclic import'
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-06-22 18:36:10 -07:00
Ishaan Jaffer
6fee78a760
ci(rust): build with --locked to enforce the lockfile 2026-06-22 18:28:06 -07:00
Ishaan Jaffer
c6aa0607fe
rust: commit Cargo.lock for reproducible builds 2026-06-22 18:28:06 -07:00
Ishaan Jaffer
100861fba1
rust: stop ignoring Cargo.lock 2026-06-22 18:28:06 -07:00
Krrish Dholakia
1cdb6cd3ac
docs: add MCP server change guidelines (#31038)
* docs: add MCP server change guidelines

* Add outbound_credentials directory and update AGENTS.md

Updated AGENTS.md to include new outbound_credentials directory and its files, along with additional comments on existing files.

---------

Co-authored-by: tin-berri <tin@berri.ai>
2026-06-22 18:25:15 -07:00
Ishaan Jaffer
d2339a9f3b
test(ocr): cover Rust OCR routing + toggle 2026-06-22 18:25:06 -07:00
Ishaan Jaffer
d7ce63f20f
litellm: export use_litellm_rust() 2026-06-22 18:25:06 -07:00
Ishaan Jaffer
b370c1c191
ocr: route mistral to Rust when enabled; keep bare-str file rejection 2026-06-22 18:25:06 -07:00
Ishaan Jaffer
c697154bf3
ocr: add minimal Rust bridge (use_litellm_rust + rust_ocr) 2026-06-22 18:25:06 -07:00
Ishaan Jaffer
8feefec4f4
rust(bridge): end-to-end ocr() + gil_stats(), GIL released for HTTP 2026-06-22 18:25:06 -07:00
Ishaan Jaffer
9240cad089
rust(bridge): add GIL release accounting 2026-06-22 18:25:06 -07:00
Ishaan Jaffer
0ce378d358
rust(bridge): depend on litellm-core 2026-06-22 18:25:06 -07:00
Ishaan Jaffer
055bef8482
rust(providers): end-to-end run_ocr orchestrator with shared client + timeout 2026-06-22 18:25:05 -07:00
Ishaan Jaffer
ba8c541804
rust(mistral): add complete_url + resolve_api_key helpers 2026-06-22 18:25:05 -07:00
Ishaan Jaffer
74b1e6d691
rust(providers): depend on reqwest 2026-06-22 18:25:05 -07:00
Ishaan Jaffer
b86d3168d6
rust: add reqwest (rustls-tls) workspace dependency 2026-06-22 18:25:05 -07:00
Ishaan Jaffer
6b83353639
rust(core): add Auth/Http/Network error variants 2026-06-22 18:25:05 -07:00
ryan-crabbe-berri
3fc73c1aff
test(ui): scrub stale return-url cookie from e2e storageState (#30317)
The login flow stores a post-login return URL in the litellm_return_url
cookie (5 minute TTL). globalSetup snapshots cookies into the per-role
storageState that every spec reuses, so when the snapshot races ahead
of the app consuming that cookie, each test inheriting it gets
redirected to the stale URL (/ui/?login=success) mid-assertion the
first time it mounts a page. That one rogue navigation is behind the
recurring e2e failures whose call logs all show "navigated to
/ui/?login=success" while waiting for an element; which specs die
varies run to run with snapshot timing. Clear the cookie right before
saving the snapshot so no test starts with a pending redirect.
2026-06-22 18:11:05 -07:00
ryan-crabbe-berri
ee5b2a367d
fix(ui): label request logs column "Key Alias" to match filter (#31037)
The request logs table column displayed "Key Name" while its accessor
(metadata.user_api_key_alias) and the corresponding filter both use the
"Key Alias" label; this aligns the column header with that naming.
2026-06-22 18:07:35 -07:00
Ishaan Jaffer
fea5204a2f
Simplify rust ocr entrypoint 2026-06-22 17:53:18 -07:00
Ishaan Jaffer
6a135105eb
address greptile rust ocr feedback 2026-06-22 17:38:24 -07:00
ryan-crabbe-berri
3615049071
feat(proxy): allow llm_api_routes virtual keys to list MCP tools via /v1/mcp/tools (#31031)
* feat(proxy): allow llm_api_routes virtual keys to list MCP tools via /v1/mcp/tools

GET /v1/mcp/tools returns the MCP tools available to the calling key, the same
data already exposed through /mcp/tools/list and /mcp-rest/tools/list, both of
which are in llm_api_routes. The /v1/mcp/tools path was in no route group, so
virtual keys created from the UI (which default to allowed_routes=["llm_api_routes"])
got a 403 listing tools one way but not the other.

Add it to mcp_inference_routes. Unlike /v1/mcp/server, this path has no
management write counterpart, so it does not need the method-aware carve-out
used for server discovery.

* test(proxy): parametrize MCP inference route check over the full endpoint set
2026-06-22 17:36:26 -07:00
ryan-crabbe-berri
26da56fbb6
feat(ui): add Amazon Bedrock Mantle to the Add Model provider dropdown (#31034)
The Add Model provider dropdown is driven by provider_create_fields.json
(served at /public/providers/fields), and Bedrock Mantle had no entry, so it
could not be selected even though the backend provider, its models, and the UI
enum/logo mappings already existed.

Add a bedrock_mantle entry exposing the credential fields the provider actually
honors: an optional bearer api_key for BYOK, the AWS SigV4 chain, a region, and
an api_base override. Selecting it now populates the bedrock_mantle models from
the cost map via the existing getProviderModels filter.

Also resolve the provider logo when getProviderLogoAndName is given the enum key
(e.g. BedrockMantle) rather than the slug, which the dropdown passes; previously
only slugs that lowercase-matched their key (like bedrock) resolved a logo.
2026-06-22 17:31:25 -07:00
ryan-crabbe-berri
19a29e0579
feat(mcp): scope a key to zero MCP servers with no-mcp-servers sentinel (#31029)
* feat(mcp): scope a key to zero MCP servers with no-mcp-servers sentinel

A key under a team that has MCP servers had no way to opt out of them;
an empty list has always meant "inherit the team". This adds a
no-mcp-servers sentinel (mirroring no-default-models for models) so a key
can declare an explicit zero that overrides team inheritance, additive
grants, and allow_all_keys servers, surfaced as an exclusive "No MCP
Servers" option in the key create/edit UI.

* refactor(ui): centralize no-mcp-servers sentinel in a shared constant

The sentinel string was defined under two different local names and
inlined in two more files; a single exported constant removes the drift
risk flagged in review.

* fix(mcp): enforce no-mcp-servers sentinel on toolset-scoped routes

Toolset scoping replaced a key's mcp_servers with the toolset's servers,
dropping the no-mcp-servers sentinel, so a key opted out of all MCP could
still execute a granted toolset's tools via /toolset/{name}/mcp. Deny
toolset access when the key carries the sentinel, checked before the admin
branch to match get_allowed_mcp_servers.
2026-06-22 17:08:28 -07:00
Ishaan Jaffer
70afb75ff1
Add litellm rust workspace with mistral ocr bridge 2026-06-22 17:07:00 -07:00