Commit graph

168 commits

Author SHA1 Message Date
yassin
77d913958d feat(openai): add openai_system_messages_first to put system messages first for prompt caching
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 21:53:29 +00:00
tin-berri
13837d319d
fix(openai): drop tool schema regex patterns OpenAI's validator cannot compile (#40485)
* fix(openai): drop tool schema regex patterns OpenAI's validator cannot compile

OpenAI validates function tool parameters with jsonschema's format checker,
which compiles every pattern with Python re. Claude Code's Artifact tool ships
an ECMA-262 pattern with \p{..} Unicode property escapes, so any OpenAI target
behind /v1/messages, /v1/responses or /v1/chat/completions 400s with
"Invalid schema for function 'Artifact': '...' is not a 'regex'" for every
model family. Drop only the patterns Python re rejects, keep the rest, at the
same seams that already flatten top-level combinators.

* fix(openai): walk only schema positions, iteratively, and drop regexes for every openai deployment

Review round: the regex sanitizer now walks JSON Schema applicator positions
only (properties, items, prefixItems, combinators, $defs, additionalProperties
and the rest), so a pattern key inside default, examples, const or a vendor
extension is data and stays. It also drops patternProperties keys Python re
cannot compile, which OpenAI checks the same way. The walk is level-order and
rebuilt deepest level first instead of recursive, so the code-quality recursion
gate passes and there is no depth cap below what a JSON parser admits. On the
chat wire an openai deployment with a custom api_base now drops such regexes
too, since that base is usually a proxy in front of the same validator, while
the lossier combinator flattening stays limited to api.openai.com hosts.
2026-09-09 22:08:30 -07:00
mateo-berri
a6681950e8 Merge branch 'litellm_internal_staging' into litellm_lit_7022_azure_ai_passthrough_config 2026-09-09 19:35:55 -07:00
mateo-berri
13745e89ac chore: merge litellm_internal_staging into litellm_lit_7022_azure_ai_passthrough_config 2026-09-09 15:36:06 -07:00
mateo-berri
830ee2d39d fix(azure): default max_retries to DEFAULT_MAX_RETRIES with regression tests
initialize_azure_sdk_client now falls back to litellm.constants.DEFAULT_MAX_RETRIES
when litellm_params carries no max_retries, so off-router Azure clients (files,
batches, fine-tuning, assistants, audio) honor the env var like OpenAI clients do.
Router paths already default max_retries to 0 and are unchanged.

Regression tests cover the default, explicit 0/5/None values, and the env var
reaching the SDK client in a fresh interpreter.
2026-09-09 14:29:06 -07:00
mateo-berri
5ea447e295 fix(azure): keep the model group segment exact and casefold only deployment names in the relay guard
Some checks failed
LiteLLM Rust / rust-lint (push) Has been cancelled
LiteLLM Rust / rust-test (push) Has been cancelled
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Modules / fmt, validate, test (gcp) (push) Has been cancelled
2026-09-08 17:19:23 -07:00
mateo-berri
a892e67c40 fix(azure): match deployment segments case-insensitively and keep relay helpers immutable 2026-09-08 17:13:53 -07:00
mateo-berri
831a2a13fb fix(azure): price azure_ai transcriptions at the azure_ai cost-map entry 2026-09-08 17:12:18 -07:00
mateo-berri
5e056a264e refactor(azure): move the passthrough deployment-segment helpers under llms
Some checks failed
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
Terraform Modules / fmt, validate, test (aws) (push) Waiting to run
Terraform Modules / fmt, validate, test (gcp) (push) Waiting to run
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
2026-09-08 12:45:05 -07:00
mateo-berri
db59e99932 fix(azure): let the caller's api-version override a full-URL api_base on relays 2026-09-08 12:21:34 -07:00
mateo-berri
13db79da46 fix(azure_ai): price streamed Responses relays from their terminal event
A streamed Responses API relay handed the success handler a bare ResponsesAPIResponse, which the streaming assembly step drops, so the relay never reached the spend callbacks. Hand it the terminal response.completed event instead, which the assembly step already converts, and cover the whole flush path with a regression test that fails on the previous tip.
2026-09-07 22:38:41 -07:00
mateo-berri
52e9cfe254 fix(azure): price streamed Responses relays and keep a full-URL api_base from doubling the native path 2026-09-07 21:57:00 -07:00
mateo-berri
1de369a1a4 fix(passthrough): bound azure relays to the key's model group and reject foreign deployment segments 2026-09-07 21:54:37 -07:00
mateo-berri
17b7003592 fix(azure_ai): cost embeddings, responses, images, and rerank relays instead of logging zero 2026-09-07 18:11:17 -07:00
mateo-berri
c698ddeacb fix(azure): let the caller's api-version win over the deployment's on passthrough relays 2026-09-07 18:00:48 -07:00
mateo-berri
4706acef95 fix(passthrough): charge remote high-detail images at the high-detail upper bound 2026-09-05 03:48:06 -07:00
mateo-berri
2f981d14e4 refactor(passthrough): inject the streaming prompt-token counter instead of a default-image flag 2026-09-05 02:09:26 -07:00
mateo-berri
bbbdccb82d fix(azure_ai): count relayed image prompt tokens without fetching the image 2026-09-05 00:30:13 -07:00
mateo-berri
9ea9aa2e7b fix(azure_ai): count prompt tokens for streaming relays that carry no usage chunk 2026-09-05 00:00:23 -07:00
mateo-berri
cd25eb9189 fix(azure_ai): cost streaming relays and return upstream errors from router relays
Streaming chat relays on Azure and azure_ai deployments rebuild the response from
the SSE chunks through the OpenAI passthrough assembler, so the spend log carries
usage. The router relays keep the JSON body when the Content-Type carries a
charset, return the upstream status and body instead of a 500 when the deployment
rejects the call, and fall back to the caller's api-version when the deployment
sets none. Lint budgets ratcheted to the measured totals
2026-09-04 23:32:27 -07:00
mateo-berri
c02f198a4a fix(azure): responses none-effort temperature gate reads the azure/ cost-map entry 2026-09-04 17:45:06 -07:00
mateo-berri
51514b9123 fix(cost-map): azure/gpt-6-astra accepts reasoning_effort none on Foundry 2026-09-04 17:29:55 -07:00
mateo-berri
ab515dbc90 fix: treat gpt-6 names as the gpt-5 request family in OpenAI and Azure configs 2026-09-03 13:20:13 -07:00
mateo-berri
0f35328a21 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_fix_azure_chat_anyof_tool_schema
# Conflicts:
#	tests/test_litellm/litellm_core_utils/prompt_templates/test_litellm_core_utils_prompt_templates_common_utils.py
2026-09-01 13:59:59 -07:00
mateo-berri
b418ccd738 fix(azure): flatten top-level tool schema combinators on Azure chat completions
Azure's chat completions validator rejects tool parameters carrying a
top-level anyOf/oneOf/allOf for every model family. AzureOpenAIConfig and
the o-series config now flatten them via the shared helper moved to
prompt_templates common_utils. Requests bridged to the Responses API for
gpt-5.4+ with reasoning active keep the union, which that surface accepts
2026-08-29 21:27:57 -07:00
Mateo Wang
2a79a81b46
Merge pull request #38837 from BerriAI/litellm_fix_azure_responses_anyof_tool_schema
fix(azure): flatten top-level tool schema combinators for Azure Responses GPT-4-family deployments
2026-08-29 16:46:22 -07:00
mateo-berri
af186eaaf3 fix(azure): flatten top-level tool schema combinators for Azure Responses GPT-4-family deployments 2026-08-29 16:23:01 -07:00
Mateo Wang
c453920f7a
Merge pull request #38285 from BerriAI/litellm_azure_v1_image_routes
fix(azure): use /openai/v1 image routes for v1, preview and latest api versions
2026-08-29 12:45:37 -07:00
mateo-berri
9e01bd1441 fix(azure): send the deployment name as the body model on v1 image routes 2026-08-29 11:41:58 -07:00
Tin Chi Lo
e5c3df2da2 fix(gpt-5): resolve temperature support from the model's default reasoning effort
A gpt-5 model accepts a non-default temperature only while its effective reasoning
effort resolves to "none". litellm had no representation of the effort a model applies
when the request omits reasoning_effort, so it substituted supports_none_reasoning_effort,
which is a different fact. Every model that supports "none" without defaulting to it
therefore had temperature forwarded and rejected upstream, and because the carve-out
returned before the drop_params branch, drop_params: true could not save it.

Declare the fact instead. A new cost-map key, default_reasoning_effort, states the effort
the provider applies when the request omits one, and one shared predicate resolves the
effective effort from it: an explicit reasoning_effort wins, otherwise the declared
default, otherwise the catalogue decides.

That last step matters because the cost map is fetched from the published branch at import
time, so it can be OLDER than the code reading it. On such a map every model looks
undeclared, and reading that as "reasoning is active" would strip temperature from the 39
gpt-5.1/5.2/5.4 entries that accept it, a regression caused by data lag rather than by
anything about the model. So an absent declaration is only meaningful once the catalogue
carries the key at all; a map that predates the feature keeps the answer litellm gave
before it existed, and the conservative answer applies from the moment the data lands.

The top_p/logprobs/top_logprobs gate carried the same assumption spelled differently and
now shares the predicate, as does the Responses API, which reimplemented the rule and is
what the default /v1/messages bridge routes openai models through. Azure normalises its
routing names in one resolver that every capability lookup goes through, which replaces
its bespoke per-lookup rewrite.

Declared on the 37 gpt-5.1/5.2/5.4 entries measured to accept temperature=0 today, so
their behaviour is unchanged. The 23 gpt-5.5/5.6 entries that reject it stay undeclared
and are fixed once the catalogue carries the key.

Resolves LIT-3797
Resolves LIT-5028
2026-08-27 18:46:18 -07:00
mateo-berri
edde2e50ef fix(openai): drop tool_reference parts from tool messages at the chat boundary
OpenAI's chat completions API rejects tool_reference content parts in
role tool messages, so a mixed text plus reference tool result carried
through the Anthropic adapter turned a previously working request into
a 400 on chat-routed OpenAI and Azure deployments. Strip the reference
parts there, keeping a reference-only result as an empty-text tool
message so the preceding tool_call stays answered, mirroring the
Responses bridge skip.
2026-08-26 23:43:41 -07:00
Devin AI
1cf683c980 fix(azure): honor base_url client param and drop stale api-version on v1 image routes
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 01:49:48 +00:00
Devin AI
5802cf0d8d fix(azure): drop deployment path from api_base when building v1 image routes
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 01:40:59 +00:00
Devin AI
80fd9970c2 fix(azure): route image generation and edits through /openai/v1 for v1 api versions
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-26 01:25:26 +00:00
Devin AI
5470645f87 refactor(azure/realtime): keep auth header build within lint budgets after merge
Some checks failed
LiteLLM Rust / rustfmt, clippy, test (push) Has been cancelled
Terraform Modules / fmt, validate, test (aws) (push) Has been cancelled
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-25 17:32:21 +00:00
Devin AI
41e7ea2033 merge: resolve conflicts with litellm_internal_staging
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-25 17:19:12 +00:00
Mateo Wang
da91d4b6c9
Merge pull request #38119 from BerriAI/litellm_bing_grounding_search_provider
feat(search): add Grounding with Bing Search (bing_grounding) as a search provider
2026-08-24 19:33:23 -07:00
mateo-berri
aef7424637 fix(azure/search): reject bool and non-positive max_results for bing_grounding count
Extract a shared _valid_max_results predicate that rejects bools (an int
subclass) and non-positive values, and reuse it from both the connection-mode
request count and the response-side cap so both paths honor the same contract.
2026-08-24 12:53:44 -07:00
mateo-berri
a0c319878b fix(search): harden bing_grounding auth, result cap, status, and cost
- send a caller api_key via the Azure api-key header instead of Authorization: Bearer
- cap web_search results to the requested max_results (the tool has no count knob)
- surface a Foundry failed/incomplete response status as a 502 error
- zero the per-query cost in web_search mode; keep the map price for connection mode
- trim the example config to terse env-var pointers
2026-08-24 12:26:07 -07:00
Mateo Wang
33c51e84b0
Merge pull request #35415 from BerriAI/litellm_azure_ai_entra_auth
feat(azure_ai): support entra id / oauth auth on every azure ai foundry route
2026-08-24 11:54:48 -07:00
mateo-berri
671a454baa Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_bing_grounding_search_provider 2026-08-24 11:53:45 -07:00
mateo-berri
37f8b9600a Merge origin/litellm_internal_staging into litellm_azure_ai_entra_auth
Bring the Entra ID / OAuth auth work for Azure AI Foundry routes up to date
with staging and fix the lint-budget regressions the merge surfaced:

- widen get_azure_ai_auth_headers return type to Mapping[str, str] (LIT001)
- build the azure_ai image_generation request headers into a new Final local
  instead of rebinding the Final headers dict (reportGeneralTypeIssues)
- order HuggingFace rerank validate_environment params to match BaseRerankConfig
  so litellm_params lines up positionally (reportIncompatibleMethodOverride)
- add a match= to the credential-error test and document the handler-boundary
  patches the auth wiring tests rely on
2026-08-24 11:33:18 -07:00
mateo-berri
03a676995a feat(search): add Grounding with Bing Search (bing_grounding) as a search provider 2026-08-24 11:08:24 -07:00
mateo-berri
9906770e41 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_google_interactions_cost
# Conflicts:
#	litellm/constants.py
#	litellm/interactions/main.py
#	litellm/litellm_core_utils/litellm_logging.py
#	litellm/litellm_core_utils/llm_cost_calc/usage_object_transformation.py
#	litellm/proxy/hooks/proxy_track_cost_callback.py
#	litellm/proxy/management_endpoints/credential_migration.py
#	tests/test_litellm/litellm_core_utils/test_litellm_logging.py
#	tests/test_litellm/test_cost_calculator.py
#	ui/litellm-dashboard/src/lib/http/schema.d.ts
2026-08-22 10:39:45 -07:00
yuneng-jiang
6a0d03914c
test: drop the cwd-relative sys.path.insert calls from the test suite (#37802)
* test: drop the cwd-relative sys.path.insert calls from the test suite

TQ003 stands at 1,077 across 1,058 files, and 1,015 of them are the same shape:
sys.path.insert(0, os.path.abspath("../..")) and its deeper siblings. The
argument resolves against the working directory rather than the file, so from
the repo root, where every job runs pytest, it inserts the directory two levels
above the checkout. It has never pointed at litellm. The package is installed
into the environment anyway, which is what actually makes the import work, and
what the rule's message has said all along.

Removing them leaves 1,634 imports of sys and os with no remaining reference,
and those go too, except where another test module imports the name back out of
the file. The rest of TQ003 is 62 call sites that resolve against __file__ or a
variable, which are a different question and are left alone.

Collection is identical either way: 45,871 tests and the same 51 pre-existing
collection errors before and after, and ruff reports no new undefined name.

* test: drop the duplicate imports the sys.path sweep exposed to F811

* test(pre-call-utils): restore the os import the new bedrock tests need
2026-08-22 09:25:58 -07:00
yuneng-jiang
7dff9953cb
test: drop the leftover set_verbose from eleven test files (#37845)
Twenty-three tests across eleven files opened with litellm.set_verbose = True
and never put it back, so the flag stayed on for everything that ran after them
in the same process. None of those files read the output it produces: no
caplog, no capsys, no assertion on a log line, so the flag was left over from
debugging. Deleting it beats restoring it, since restoring keeps the noise.

Ten of the eleven stop leaving the flag on. test_volcengine_embedding.py still
ends with it set, from something it exercises rather than from the test itself,
which is worth its own look.
2026-08-22 08:23:27 -07:00
ryan-crabbe-berri
e9d40a8f73 test: enforce F811 so a duplicate definition cannot silently replace the first
A name bound twice keeps only the second binding. In `tests/` that is nearly
always a repeated import, harmless but misleading, and the same rule is what
catches the cases that are not harmless: a local that shadows an import the
module still calls, and a second `def test_x` that quietly replaces the first.

311 of the 344 sites were repeated imports and came out with ruff's own fix.
The remaining 33 needed a decision. Four modules imported a name they never
used because a local definition below already shadowed it. Two comprehensions
bound `call` over `unittest.mock.call`, which those modules import and use.
One test rebound the two module handles its nested reload closure had captured.
One class attribute shadowed an unused `status` import.

The load-test fixtures move to a conftest, which is how pytest is meant to share
them, so the test module no longer imports three fixture names it never calls.
The nine `prisma_client` parameters keep a narrow `noqa`: pytest resolves that
fixture by name before the body runs, so the parameter never shadows anything.
2026-08-21 12:06:19 -07:00
ryan-crabbe-berri
680bcfd8aa
test(lint): ban blind pytest.raises(Exception) with ruff B017 (#37731)
* test(lint): ban blind pytest.raises(Exception) with ruff B017

A bare pytest.raises(Exception) accepts whatever the body throws. The TypeError
a refactor introduces satisfies it exactly as well as the rejection the test was
written for, so the crash reads as a pass and the test never goes red.

All 111 existing sites are narrowed here. A runtime probe recorded the concrete
exception each one actually catches, and each site now names that type. Where
the code under test genuinely raises a bare Exception, the site pins a stable
slice of the message with match= instead.

Two sites tell on themselves. The shared responses-API cancel test raises
"custom_llm_provider is required but passed as None" rather than talking to a
provider at all, because cancel_responses takes a provider, not a model. And
test_bedrock_guardrails_with_streaming was the only test in its file still
passing without AWS credentials, because the NoCredentialsError boto3 raised
long before the guardrail ran satisfied the blind raises.

* fix(test): widen the openai batch-dispatch assertion to OpenAIError

The narrowed NotFoundError only holds where OPENAI_API_KEY is set. Without one
the SDK raises OpenAIError while building the client, long before any 404, so CI
went red. OpenAIError covers both and still rejects a TypeError from a refactor.
2026-08-20 18:09:42 -07:00
yuneng-jiang
3357ec8d34
test: run the 30 test files stranded in the second mirror (#37595)
* test: run the 30 test files stranded in the second mirror

tests/litellm sat beside tests/test_litellm, which is the mirror the repo
convention names, and no job collected it. The allowlist called the directory
unresolved and assumed it was a duplicate. It is not: 30 of its 34 files have no
counterpart in the real mirror, so they are tests nobody has run since they were
written, not copies of tests that run elsewhere.

Moving them in is byte-identical, and it is what makes them run. Every one is
now claimed by a shard's test-path rather than by an allowlist entry, and the
216 tests they hold pass. Directories that needed to become packages did, since
several files are named test_transformation.py and pytest cannot import two of
those from non-package directories in one session.

Never running is why three assertions had drifted away from the code:

  * nvidia.nemotron-super-3-120b max_output_tokens, 32000 -> 32768
  * sambanova/MiniMax-M2.7 max_input_tokens, 204800 -> 196608
  * the Vertex text-to-speech handler moved from data= to json=, so the test
    reads the decoded body off the json kwarg instead of parsing the data one

The first two follow model_prices_and_context_window.json, which the catalog
sync keeps current; the third follows the handler. In all three the test was the
stale side.

The lint workflow ran test_no_hardcoded_secrets.py by path and now points at the
new one.

Four files stay behind. Each shares a filename with a live test whose contents
are disjoint from it, so landing those means merging test bodies, which is a
content review rather than a move. The allowlist entry now names those four and
records how many tests each would bring, in place of calling the whole
directory unresolved.

* fix(ci): keep the secret scan out of the mirror's conftest

The secret-scan job runs pytest under uv run --no-project, so its environment
holds pytest and nothing else. That worked while the file sat in tests/litellm,
which has no conftest, and broke the moment it moved into tests/test_litellm,
whose conftest imports litellm on collection: ModuleNotFoundError: No module
named 'dotenv', before a single test ran.

The file is a repo-wide static scan that imports only base64, os, re and pytest,
so it belongs with the other repo-wide checks in tests/code_coverage_tests,
which has no conftest, rather than in the package mirror. Installing the full
dependency set into a 15-second job to satisfy a conftest it does not use would
be the wrong trade.

Verified with the job's exact command:
  uv run --no-project --with 'pytest==9.0.2' pytest \
    tests/code_coverage_tests/test_no_hardcoded_secrets.py -q
  1 passed in 0.47s
2026-08-20 10:59:43 -07:00
Yassin Kortam
9b7ed77fcc
fix(azure): rename max_tokens to max_completion_tokens for gpt-5-chat deployments (#36857)
Azure rejects the legacy `max_tokens` key for the whole gpt-5 name family, but
`AzureOpenAIGPT5Config.is_model_gpt_5_model` deliberately excludes `gpt-5-chat*`
so those deployments fall through to `AzureOpenAIConfig`, which sends `max_tokens`
verbatim and gets a 400 back on every request that carries it, `/health` probes
included.

One predicate was answering two independent questions. Split it: the new
`AzureOpenAIConfig.requires_max_completion_tokens` covers the whole gpt-5 name
family and drives only the rename, while `is_model_gpt_5_model` keeps keying
reasoning_effort, the temperature clamp and the dropped penalties off the
reasoning question, so #13781 stays fixed.
2026-08-17 11:27:55 -07:00