Skip to content

[Infra] Merge internal dev branch with main - #25036

Merged
ishaan-berri merged 15 commits into
mainfrom
litellm_internal_dev_04_02_2026
Apr 2, 2026
Merged

[Infra] Merge internal dev branch with main#25036
ishaan-berri merged 15 commits into
mainfrom
litellm_internal_dev_04_02_2026

Conversation

@yuneng-berri

Copy link
Copy Markdown
Contributor

Relevant issues

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🚄 Infrastructure

Changes

milan-berri and others added 15 commits March 20, 2026 01:48
custom_auth_run_common_checks only runs common_checks (team/user/project model checks).
Custom auth now also enforces key-level model restrictions via can_key_call_model.

Move the custom-auth key-access regression tests to test_user_api_key_auth.py and keep test_custom_auth_end_user_budget.py focused on end-user budget behavior.

Made-with: Cursor
Keep key-level model allowlist enforcement in custom auth behind `custom_auth_run_common_checks` to preserve backwards compatibility, and update tests to verify default non-enforcement and opt-in enforcement behavior.

Made-with: Cursor
…tate

Patch `proxy_server.general_settings` to an empty dict in the default custom-auth key-access test so it remains deterministic under shared module state.

Made-with: Cursor
Tighten custom auth regression tests by asserting exact can_key_call_model args and remove an unused common_checks mock from the default behavior path.

Made-with: Cursor
Add generic docs for running JWT and OAuth2 together, including routing_overrides YAML examples and list-based selector behavior for iss/client_id/aud.

Made-with: Cursor
feat(auth): add JWT claim routing overrides for OAuth2 validation
…allowlist

fix(proxy): enforce key-level model restrictions for custom auth
…ponse-parsing

fix(agentcore): parse A2A JSON-RPC responses in AgentCore provider
…g-roles-tool-calls

fix(prompt-templates): ensure_alternating_roles handles tool-call chains
@CLAassistant

CLAassistant commented Apr 2, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@vercel

vercel Bot commented Apr 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Apr 2, 2026 11:35pm

Request Review

@gitguardian

gitguardian Bot commented Apr 2, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 2 secrets following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secrets in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
29203053 Triggered Generic Password 6058de4 .circleci/config.yml View secret
29375658 Triggered JSON Web Token 6058de4 tests/test_litellm/proxy/auth/test_handle_jwt.py View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secrets safely. Learn here the best practices.
  3. Revoke and rotate these secrets.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@codspeed-hq

codspeed-hq Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing litellm_internal_dev_04_02_2026 (1d26232) with main (f11cdf7)

Open in CodSpeed

@ishaan-berri
ishaan-berri merged commit 3604b60 into main Apr 2, 2026
65 of 115 checks passed
@ishaan-berri
ishaan-berri deleted the litellm_internal_dev_04_02_2026 branch April 2, 2026 23:38
@greptile-apps

greptile-apps Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR merges an internal dev branch into main, landing four distinct improvements: (1) JWT routing overrides for the auth layer so that JWT-shaped machine tokens can be directed to OAuth2 introspection instead of the JWT path based on unverified claims (iss, client_id, aud); (2) a refactor that extracts _enforce_key_and_fallback_model_access and wires it into custom-auth post-checks; (3) a behavior fix in _insert_assistant_continue_message that now skips tool messages and assistant(tool_calls) when detecting consecutive user turns (matching strict llama.cpp-style templates); and (4) a new AmazonAgentCoreConfig for Bedrock AgentCore with SSE/JSON streaming, A2A JSON-RPC support, and SigV4/Bearer dual authentication.

Key items to verify before merging:

  • The _insert_assistant_continue_message change is a backwards-incompatible behavior change: users with ensure_alternating_roles=True and agentic tool-calling patterns will now receive injected assistant_continue messages that were not there before, with no opt-out flag. Per project policy this kind of change should be gated behind a user-controlled flag.
  • Three existing tests have had their expected outputs reversed (no longer asserting "do not split tool-call chains") — confirm the old contract was not a deliberate design guarantee before treating the new expected outputs as authoritative.
  • _should_route_jwt_to_oauth2_override accesses jwt_handler.litellm_jwtauth which is set in update_environment(), not __init__; a missing getattr guard could produce an opaque 500 on first request if setup is incomplete.
  • The new Bedrock AgentCore sign_request logs the first 50 characters of the bearer token at DEBUG level; consider removing the token value from the log message.

Confidence Score: 4/5

  • Mostly safe; the JWT routing addition is well-tested and architecturally sound, but the _insert_assistant_continue_message behavior change is backwards-incompatible for users with tool-calling patterns and lacks a feature flag.
  • The auth routing work is solid (unverified claims used only for routing, not authentication; good mock test coverage). The AgentCore transformation is self-contained. The main concern keeping this from 5/5 is the _insert_assistant_continue_message behavior change: existing callers with ensure_alternating_roles=True and tool-call chains will silently get extra injected messages, violating the backward-compatibility rule. The litellm_jwtauth attribute guard is also a latent error surface worth closing.
  • litellm/litellm_core_utils/prompt_templates/common_utils.py (backward-compat behavior change); litellm/proxy/auth/user_api_key_auth.py (potential AttributeError on uninitialized handler)

Important Files Changed

Filename Overview
litellm/proxy/auth/user_api_key_auth.py Adds JWT routing-override helpers (_routing_selector_matches_claim, _matches_routing_override, _should_route_jwt_to_oauth2_override) and wires them into the OAuth2/JWT auth gate; also extracts _enforce_key_and_fallback_model_access and extends _run_post_custom_auth_checks to enforce it when custom_auth_run_common_checks is True.
litellm/proxy/auth/handle_jwt.py Promotes jwt/PyJWK imports to module level, extracts SUPPORTED_JWT_ALGORITHMS as class constant, and adds get_unverified_claims() static method for claim-based routing decisions.
litellm/proxy/_types.py Adds JWTRoutingOverride Pydantic model (iss, client_id, aud, path) and routing_overrides field to LiteLLM_JWTAuth.
litellm/litellm_core_utils/prompt_templates/common_utils.py Refactors _insert_assistant_continue_message to use _counts_for_alternation, now inserting assistant_continue across tool-call chains — a backward-incompatible behavior change without a feature flag.
litellm/llms/bedrock/chat/agentcore/transformation.py New AmazonAgentCoreConfig: handles SigV4/Bearer auth, SSE + JSON streaming, A2A JSON-RPC parsing, and transforms AgentCore responses to LiteLLM ModelResponse format.
tests/llm_translation/test_prompt_factory.py Three existing tests have their expected outputs updated to reflect new assistant_continue injection behavior; several new tests added verifying tool-chain scenarios.
tests/test_litellm/proxy/auth/test_user_api_key_auth.py Adds thorough mock-only tests for JWT routing override: matching, non-matching client_id, list selectors, and backward-compat (OAuth2-only).
tests/test_litellm/llms/bedrock/chat/agentcore/test_agentcore_transformation.py Adds four new unit tests for A2A JSON-RPC response parsing strategies (nested message, direct parts, multi-parts, empty fallback).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming Request with Bearer Token] --> B{enable_oauth2_auth?}
    B -- No --> G{enable_jwt_auth?}
    B -- Yes --> C{is_llm_api_route or is_info_route?}
    C -- No --> G
    C -- Yes --> D{enable_jwt_auth AND token is JWT-shaped?}
    D -- No --> E[OAuth2 Introspection\nOauth2Handler.check_oauth2_token]
    D -- Yes --> F{_should_route_jwt_to_oauth2_override?\ncheck unverified claims vs routing_overrides}
    F -- No match --> G
    F -- Match found --> E
    G -- No --> H[Virtual Key / DB Auth Path]
    G -- Yes --> I{token is JWT-shaped?}
    I -- No --> H
    I -- Yes --> J[JWT Auth\nJWTAuthManager.auth_builder]
    J --> K[UserAPIKeyAuth]
    E --> K
    H --> K
Loading

Comments Outside Diff (1)

  1. litellm/llms/bedrock/chat/agentcore/transformation.py, line 134-138 (link)

    P2 JWT/Bearer token value partially exposed in debug log

    The first 50 characters of the bearer token are written to the log at DEBUG level. A typical JWT begins with eyJhbGciOi... (the base64-encoded header), so the first 50 chars can leak the signing algorithm and token type. While DEBUG is not normally enabled in production, a truncated token value is still sensitive. Consider logging only a non-sensitive indicator (e.g. whether a token was provided) instead:

Reviews (1): Last reviewed commit: "Merge remote-tracking branch 'origin' in..." | Re-trigger Greptile

Comment on lines 337 to 370
"""
Add assistant continuation messages between consecutive user messages.

Only checks directly adjacent messages to preserve backward compatibility.
Skips tool messages and assistant messages with tool calls in the
alternation check, matching strict templates like llama.cpp.
"""
if not ensure_alternating_roles or len(messages) <= 1:
return messages

continue_message = assistant_continue_message or DEFAULT_ASSISTANT_CONTINUE_MESSAGE

# Find indexes where assistant_continue should be inserted (before that index)
insert_before_indexes: set = set()

for i in range(len(messages)):
curr = messages[i]
if _counts_for_alternation(curr) and curr["role"] == "user":
# Look backwards for the previous counted message
j = i - 1
while j >= 0:
if _counts_for_alternation(messages[j]):
if messages[j]["role"] == "user":
insert_before_indexes.add(i)
break
j -= 1

# Build the result with assistant_continue inserted at the right positions
modified_messages: List[AllMessageValues] = []
for i, message in enumerate(messages):
if (
i < len(messages) - 1
and message.get("role") == "user"
and messages[i + 1].get("role") == "user"
):
modified_messages.append(message)
if i in insert_before_indexes:
modified_messages.append(continue_message)
else:
modified_messages.append(message)
modified_messages.append(message)

return modified_messages

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Backward-incompatible behavior change without a feature flag

The previous implementation only inserted assistant_continue between two directly adjacent user messages. The new implementation uses _counts_for_alternation to skip tool messages and assistant(tool_calls) messages, meaning a pattern like:

[user, assistant(tool_calls), tool, user]

now gets assistant_continue inserted before the second user message — behavior that did not exist before. Three existing tests (test_ensure_alternating_roles_does_not_split_tool_call_chain, test_ensure_alternating_roles_assistant_tool_call_then_assistant, test_ensure_alternating_roles_trailing_tool_call_assistant) were updated to match the new behavior, confirming this is intentional.

However, per the project rule on backwards-incompatible changes, existing users who have ensure_alternating_roles=True and agentic/tool-calling message patterns will silently receive extra injected assistant_continue messages, potentially breaking their existing model calls without any migration path.

The safer approach would be to introduce this under a flag (e.g. ensure_alternating_roles="strict" or a separate skip_tool_messages_in_alternation param) so existing users are not affected.

Rule Used: What: avoid backwards-incompatible changes without... (source)


def test_ensure_alternating_roles_does_not_split_tool_call_chain():
"""Tool-call chains [user, assistant(tc), tool, user] are preserved as-is."""
def test_ensure_alternating_roles_inserts_assistant_continue_across_tool_chain():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Test semantics reversed — previously documented intentional "do not split" behavior

The original test was named test_ensure_alternating_roles_does_not_split_tool_call_chain and its docstring read: "Tool-call chains [user, assistant(tc), tool, user] are preserved as-is." This name and docstring were documenting a deliberate design contract.

The rename to test_ensure_alternating_roles_inserts_assistant_continue_across_tool_chain and the addition of the injected assistant_continue to the expected output reverses that documented contract. While this may be an intentional behavior improvement, it's worth confirming that no downstream callers depended on the old guarantee before treating the changed expected output as correct.

This also applies to test_ensure_alternating_roles_assistant_tool_call_then_assistant and test_ensure_alternating_roles_trailing_tool_call_assistant, both of which add a new {"role": "assistant", "content": "Please continue."} line to their expected message lists.

Rule Used: What: Flag any modifications to existing tests and... (source)

Comment on lines +173 to +191
def _should_route_jwt_to_oauth2_override(token: str, jwt_handler: JWTHandler) -> bool:
routing_overrides = jwt_handler.litellm_jwtauth.routing_overrides
if not routing_overrides:
return False

token_claims = jwt_handler.get_unverified_claims(token=token)
if token_claims is None:
return False

for override in routing_overrides:
if override.path == "oauth2" and _matches_routing_override(
token_claims=token_claims, override=override
):
verbose_proxy_logger.debug(
"JWT routing override matched. Routing token to OAuth2 introspection."
)
return True

return False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 litellm_jwtauth may be unset at first request

The litellm_jwtauth attribute is assigned in JWTHandler.update_environment(), not in __init__, so it may not exist if the handler was constructed but environment setup was skipped. Accessing a missing attribute will raise AttributeError and surface as an opaque 500 to the caller.

A getattr(jwt_handler, "litellm_jwtauth", None) guard at the top of this function would safely return False in that scenario.

fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
* fix(proxy): enforce key-level model allowlist for custom auth

custom_auth_run_common_checks only runs common_checks (team/user/project model checks).
Custom auth now also enforces key-level model restrictions via can_key_call_model.

Move the custom-auth key-access regression tests to test_user_api_key_auth.py and keep test_custom_auth_end_user_budget.py focused on end-user budget behavior.

Made-with: Cursor

* fix(proxy): gate custom-auth key model checks behind opt-in

Keep key-level model allowlist enforcement in custom auth behind `custom_auth_run_common_checks` to preserve backwards compatibility, and update tests to verify default non-enforcement and opt-in enforcement behavior.

Made-with: Cursor

* test(proxy): isolate custom auth default check from shared settings state

Patch `proxy_server.general_settings` to an empty dict in the default custom-auth key-access test so it remains deterministic under shared module state.

Made-with: Cursor

* test(proxy): strengthen custom auth post-check assertions

Tighten custom auth regression tests by asserting exact can_key_call_model args and remove an unused common_checks mock from the default behavior path.

Made-with: Cursor

* fix(agentcore): parse A2A JSON-RPC responses in AgentCore provider

* fix(prompt-templates): ensure_alternating_roles handles tool-call chains

* feat(auth): add JWT claim routing overrides for OAuth2 validation

Made-with: Cursor

* docs(auth): document JWT-to-OAuth2 routing overrides

Add generic docs for running JWT and OAuth2 together, including routing_overrides YAML examples and list-based selector behavior for iss/client_id/aud.

Made-with: Cursor

---------

Co-authored-by: Milan <milan@berri.ai>
Co-authored-by: michelligabriele <gabriele.michelli@icloud.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants