Skip to content

fix(bedrock_mantle): send max_completion_tokens for gpt-5.6 and gpt-6 chat completions - #43982

Merged
mateo-berri merged 11 commits into
mainfrom
litellm_bedrock_mantle_gpt_max_completion_tokens
Oct 11, 2026
Merged

mateo-berri merged 11 commits into
mainfrom
litellm_bedrock_mantle_gpt_max_completion_tokens

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Mantle chat completions rejects max_tokens for gpt-5.6 and gpt-6 with a 400
  • A chat-mode bedrock_mantle gpt-5.6 deployment sent max_tokens for any output limit
  • A client max_completion_tokens was rewritten to max_tokens and failed the same way

How it solves it:

  • GPT reasoning-series models (gpt-5.x, gpt-6) on Mantle chat now send max_completion_tokens
  • A deployment or client max_tokens is carried over as max_completion_tokens, value unchanged
  • Every other Mantle chat model (gpt-oss, gemma, grok) keeps sending max_tokens as before

Decisions: the rule is keyed on the model name (the same GPT reasoning-series check the native OpenAI config uses), not on the openai/v1 base path that #43581 keys its Mantle override on, because the customer's deployment runs on a /v1 base and Mantle accepts max_completion_tokens for every GPT model tested while gpt-5.4, gpt-oss and gemma-4 accept both names. When a request carries both max_tokens and max_completion_tokens, the explicit max_completion_tokens wins and max_tokens is dropped (the alternative, sending the smaller of the two, was not taken because the request already named the parameter this family accepts). Values are never scaled or clamped

User Flow

Before: a platform engineer whose apps call a Bedrock Mantle gpt-5.6 deployment through chat completions gets a 400 the moment any output limit is set, on the deployment or on the request

  1. The proxy admin registers the deployment with POST https://litellm-domain/model/new: bedrock_mantle/openai.gpt-5.6-terra, custom_llm_provider: bedrock_mantle, model_info.mode: chat, and max_tokens: 1000 in its params
  2. An app sends POST https://litellm-domain/v1/chat/completions with that model, stream: true, stream_options: {"include_usage": true}, and a function tool, no limit of its own
  3. It gets HTTP 400 Unsupported parameter: 'max_tokens' is not supported with this model. instead of a stream, and the same 400 comes back when the deployment has no limit and the app sends max_tokens: 1000 or max_completion_tokens: 1000 with stream: false
  4. The only way to get a completion is to remove the limit from the deployment and from every request

After: the same deployment and the same requests return completions, with the limit still applied

  1. The proxy admin registers the same deployment with POST https://litellm-domain/model/new, max_tokens: 1000 in its params
  2. The app sends the same POST https://litellm-domain/v1/chat/completions with stream: true, stream_options: {"include_usage": true}, and a function tool
  3. It gets HTTP 200 and a streamed tool call that ends with a usage chunk, and max_tokens: 1000 or max_completion_tokens: 1000 from the app with stream: false returns HTTP 200 with a completion whose usage stays under 1000 output tokens
  4. The deployment keeps its limit and no request has to drop its own

Relevant issues

Related: #44307 (merged) gave the Bedrock runtime chat path the same max_tokens to max_completion_tokens rename for gpt-5.6+; this PR is the standalone fix for the Mantle chat path. #43581 carried both inside a larger stacked change and was closed unmerged

Linear ticket

Resolves LIT-9089

Pre-Submission checklist

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

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/unit/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

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

Screenshots / Proof of Fix

Last updated: 1e8767c. This tip merges main (705bd84) into the branch so the lint workflow's OpenAPI docs step, which checks out the PR head, finds scripts/openapi_docs_gate.py (added to main today in #45843); the merge is clean and changes nothing under litellm/llms/bedrock_mantle, and 008996b before it changed only the e2e test file (the three tests now drive the proxy through the suite's OpenAI SDK fixture and declare their Subject metadata, closing the two Greptile threads). The product behavior is still the one 62a88f7 shipped. QA ran on d4d2694 (two 2-worker proxies, the Before leg at the merge base 988cc7d) and carries; the e2e step below was re-run from this merged tree against the same head proxy, and the 63 unit tests pass at this tip. /live-pr-risk ran at d4d2694 against 988cc7d and the audit matrix (round 2 of 3) at d4d2694 against 988cc7d, both below; /live-pr-risk also drove its 71 cells on the head merged with main at ff60d27, and of the 11 main commits since then only c1e3d07 touches litellm/llms/bedrock, in the guardrail, not the Mantle chat path. d4d2694 merged main (988cc7d) into the branch, resolving the conflicts in tests/e2e/models.py (both sides' deployment fields kept) and tests/integration/_support/process.py (main's version taken whole, so the wire test's chaos budget now reads main's graceful_stop_seconds()), and typed the override's signature and its one parent call so the lint gates stay at main's counts. Greptile: 4/5 at d4d2694 (Reviews (6), 2026-10-10 22:04Z), with its two P2 threads on the e2e test closed by 008996b (outdated, left for the bot to resolve). Bare @greptileai at 008996b (2026-10-10 23:20Z) and twice at 1e8767c (00:12Z and 17:57Z on 10-11) drew no answer, and no PR in this repo has had a Greptile review since that 22:04Z edit (new PRs opened 16:39Z on 10-11 have none, eight triggers by five people today are unanswered), so the silence is repo-wide and is recorded here, never counted as a review of the two later commits (tests-only and a main merge)

Shared setup: each leg is a LiteLLM proxy booted from the named commit with --num_workers 2, Postgres with STORE_MODEL_IN_DB=True, and a real Bedrock Mantle bearer token for us-east-1 (real calls, real spend). Both legs register the same deployments through the proxy, then run the same requests in the same order

curl -s http://127.0.0.1:$PORT/model/new -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json" -d '{
  "model_name": "gpt-5.6-terra-chat",
  "litellm_params": {"model": "bedrock_mantle/openai.gpt-5.6-terra", "custom_llm_provider": "bedrock_mantle", "reasoning_effort": "none",
                     "api_key": "os.environ/AWS_BEARER_TOKEN_BEDROCK", "api_base": "https://bedrock-mantle.us-east-1.api.aws/openai/v1"},
  "model_info": {"mode": "chat", "supported_endpoints": ["/v1/chat/completions", "/v1/responses"]}}'

gpt-5.6-terra-chat-deploymax is the same deployment with "max_tokens": 1000 added to litellm_params (the customer's shape). The controls gpt-5.4-chat (bedrock_mantle/openai.gpt-5.4) and gpt-oss-120b-chat (bedrock_mantle/openai.gpt-oss-120b) are registered the same way

Before (988cc7d)

OpenAI SDK: deployment max_tokens, stream + stream_options + tools (the customer's shape)

  1. from openai import OpenAI
    client = OpenAI(base_url="http://127.0.0.1:57674/v1", api_key=LITELLM_MASTER_KEY)
    stream = client.chat.completions.create(
        model="gpt-5.6-terra-chat-deploymax",
        messages=[{"role": "user", "content": "What is the weather in Paris?"}],
        stream=True, stream_options={"include_usage": True},
        tools=[{"type": "function", "function": {"name": "get_weather",
                "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}])
    for chunk in stream: print(chunk)
  2. BadRequestError: Error code: 400 - {'error': {'message': 'litellm.BadRequestError: BedrockException - {"error":{"code":"unsupported_parameter","message":"Unsupported parameter: \'max_tokens\' is not supported with this model.","param":"max_tokens","type":"invalid_request_error"}}\n\nLiteLLM: model group \'gpt-5.6-terra-chat-deploymax\' failed with the error above. No fallback was attempted.', 'type': 'invalid_request_error', 'param': None, 'code': '400'}}
    
    (no chunk arrived; the SDK raised before the first delta)

OpenAI SDK: client max_tokens, then client max_completion_tokens

  1. client.chat.completions.create(model="gpt-5.6-terra-chat", stream=False, max_tokens=1000,
                                   messages=[{"role": "user", "content": "Say hello in one short sentence."}])
    client.chat.completions.create(model="gpt-5.6-terra-chat", stream=False, max_completion_tokens=1000,
                                   messages=[{"role": "user", "content": "Say hello in one short sentence."}])
  2. [max_tokens=1000] BadRequestError: Error code: 400 - {'error': {'message': 'litellm.BadRequestError: BedrockException - {"error":{"code":"unsupported_parameter","message":"Unsupported parameter: \'max_tokens\' is not supported with this model.","param":"max_tokens","type":"invalid_request_error"}}\n\nLiteLLM: model group \'gpt-5.6-terra-chat\' failed with the error above. No fallback was attempted.', 'type': 'invalid_request_error', 'param': None, 'code': '400'}}
    [max_completion_tokens=1000] BadRequestError: Error code: 400 - {'error': {'message': 'litellm.BadRequestError: BedrockException - {"error":{"code":"unsupported_parameter","message":"Unsupported parameter: \'max_tokens\' is not supported with this model.","param":"max_tokens","type":"invalid_request_error"}}\n\nLiteLLM: model group \'gpt-5.6-terra-chat\' failed with the error above. No fallback was attempted.', 'type': 'invalid_request_error', 'param': None, 'code': '400'}}
    

curl /v1/chat/completions: deployment limit, stream

  1. curl -s -N -w "\nHTTP %{http_code}\n" http://127.0.0.1:57674/v1/chat/completions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json" \
      -d '{"model":"gpt-5.6-terra-chat-deploymax","messages":[{"role":"user","content":"What is the weather in Paris?"}],"stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"get_weather","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}}]}'
  2. {"error":{"message":"litellm.BadRequestError: BedrockException - {\"error\":{\"code\":\"unsupported_parameter\",\"message\":\"Unsupported parameter: 'max_tokens' is not supported with this model.\",\"param\":\"max_tokens\",\"type\":\"invalid_request_error\"}}\n\nLiteLLM: model group 'gpt-5.6-terra-chat-deploymax' failed with the error above. No fallback was attempted.","type":"invalid_request_error","param":null,"code":"400"}}
    HTTP 400
    

curl /v1/messages: max_tokens 1000

  1. curl -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:57674/v1/messages -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json" \
      -d '{"model":"gpt-5.6-terra-chat","max_tokens":1000,"messages":[{"role":"user","content":"Say hello in one short sentence."}]}'
  2. {"type":"error","error":{"type":"invalid_request_error","message":"litellm.BadRequestError: BedrockException - {\"error\":{\"code\":\"unsupported_parameter\",\"message\":\"Unsupported parameter: 'max_tokens' is not supported with this model.\",\"param\":\"max_tokens\",\"type\":\"invalid_request_error\"}}\n\nLiteLLM: model group 'gpt-5.6-terra-chat' failed with the error above. No fallback was attempted."}}
    HTTP 400
    

curl /v1/responses: max_output_tokens 1000

  1. curl -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:57674/v1/responses -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json" \
      -d '{"model":"gpt-5.6-terra-chat","input":"Say hello in one short sentence.","max_output_tokens":1000}'
  2. {"id":"resp_jkcDZX-RIh3r-JG56AVKKvVxLUKeIcHMJRV5RU0mosz2FFdbj1BaMkb97XNMoTRGKslaF2J5O1uJjTpdBU1EFo1jp0iAJKCG-...","created_at":1791669123,"error":null,"incomplete_details":null,"instructions":null,"metadata":{},"model":"gpt-5.6-terra-chat","object":"response","output":[{"id":"msg_188e5e1653395575b19b1ee05c8161b1","content":[{"annotations":[],"text":"Hello!","type":"output_text","logprobs":[]}],"role":"assistant","status":"completed","type":"message","phase":"final_answer"}],"parallel_tool_calls":true,"temperature":1.0,"tool_choice":"auto","tools":[],"top_p":0.98,"max_output_tokens":1000,"previous_response_id":null,"reasoning":{"summary":null,"effort":"none","context":"all_turns"},"status":"completed","text":{"format":{"type":"text"},"verbosity":"medium"},"truncation":"disabled","usage":{"input_tokens":13,"input_tokens_details":{"audio_tokens":null,"cached_tokens":0,"cached_tokens_details":null,"image_tokens":null,"text_tokens":null,"video_tokens":null,"cache_write_tokens":0},"output_tokens":6,"output_tokens_details":{"audio_tokens":null,"reasoning_tokens":0,"text_tokens":null},"total_tokens":19,"cost":null},"user":null,"store":true, ...
    HTTP 200
    

Controls: gpt-5.4 and gpt-oss-120b with max_tokens 50 and max_completion_tokens 50

  1. for m in gpt-5.4-chat gpt-oss-120b-chat; do for p in max_tokens max_completion_tokens; do
      curl -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:57674/v1/chat/completions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json" \
        -d "{\"model\":\"$m\",\"messages\":[{\"role\":\"user\",\"content\":\"Say hello in one short sentence.\"}],\"stream\":false,\"$p\":50}"
    done; done
  2. gpt-5.4-chat max_tokens=50:            {"id":"chatcmpl-iejknn6pat2325d33x6olerporwwrsrx5vvy6jetjtxtqvggrsra","created":1791669124,"model":"gpt-5.4-chat","object":"chat.completion","choices":[{"finish_reason":"stop","index":0,"message":{"content":"Hello!","role":"assistant","annotations":[]}}],"usage":{"completion_tokens":6,"prompt_tokens":13,"total_tokens":19, ...}}   HTTP 200
    gpt-5.4-chat max_completion_tokens=50: {"id":"chatcmpl-zve2ux7zx62dm3cxppwggtr77d7k7zedprhiqc5mwe32jyiiyofq","created":1791669125,"model":"gpt-5.4-chat","object":"chat.completion","choices":[{"finish_reason":"stop","index":0,"message":{"content":"Hello!","role":"assistant","annotations":[]}}],"usage":{"completion_tokens":6,"prompt_tokens":13,"total_tokens":19, ...}}   HTTP 200
    gpt-oss-120b-chat max_tokens=50:            {"error":{"message":"litellm.BadRequestError: BedrockException - {\"error\":{\"code\":\"validation_error\",\"message\":\"model `openai.gpt-oss-120b` isn't supported on this route\",\"param\":null,\"type\":\"invalid_request_error\"}}\n\nLiteLLM: model group 'gpt-oss-120b-chat' failed with the error above. No fallback was attempted.","type":"invalid_request_error","param":null,"code":"400"}}   HTTP 400
    gpt-oss-120b-chat max_completion_tokens=50: {"error":{"message":"litellm.BadRequestError: BedrockException - {\"error\":{\"code\":\"validation_error\",\"message\":\"model `openai.gpt-oss-120b` isn't supported on this route\",\"param\":null,\"type\":\"invalid_request_error\"}}\n\nLiteLLM: model group 'gpt-oss-120b-chat' failed with the error above. No fallback was attempted.","type":"invalid_request_error","param":null,"code":"400"}}   HTTP 400
    
    (one direct call to https://bedrock-mantle.us-east-1.api.aws/openai/v1/chat/completions with the same bearer token, model: openai.gpt-oss-120b, max_tokens: 50, no proxy in between, answered the identical body {"error":{"code":"validation_error","message":"model \openai.gpt-oss-120b` isn't supported on this route", ...}}` with HTTP 400, so the gpt-oss control is refused by Mantle itself today)

e2e test file against this proxy

The test file does not exist on this tree, so the head tree's tests/e2e/llm_translation/test_bedrock_mantle_output_limit_e2e.py was copied into this scratch directory only (nothing copied into the tree) and run with the tree's .venv and the tree's own harness (tests/e2e/conftest.py and llm_translation/conftest.py registered with -p, LITELLM_PROXY_URL and LITELLM_MASTER_KEY pointed at this proxy). The PR also edits tests/e2e/models.py (adds "chat" to ModelMode, supported_endpoints to ModelInfoBody, and max_tokens, max_completion_tokens, custom_llm_provider, reasoning_effort to LiteLLMParamsBody), and this tree's harness lacks those, so every test fails inside the harness's pydantic validation of ModelInfoBody(mode="chat", ...) before a single request reaches the proxy. This step therefore measures the harness gap, not the proxy

  1. cd tests/e2e && LITELLM_PROXY_URL=http://127.0.0.1:57674 LITELLM_MASTER_KEY=$LITELLM_MASTER_KEY PYTHONPATH=$PWD:$PWD/llm_translation \
      ../../.venv/bin/python -m pytest -c pytest.ini --rootdir . -p conftest -p llm_translation.conftest <scratch>/test_bedrock_mantle_output_limit_e2e.py -q -rA
  2. E       pydantic_core._pydantic_core.ValidationError: 1 validation error for ModelInfoBody
    E       mode
    E         Input should be 'batch', 'realtime' or 'image_generation' [type=literal_error, input_value='chat', input_type=str]
    FAILED ::TestBedrockMantleChatOutputLimit::test_client_max_tokens_returns_completion
    FAILED ::TestBedrockMantleChatOutputLimit::test_client_max_completion_tokens_returns_completion
    FAILED ::TestBedrockMantleChatOutputLimit::test_deployment_max_tokens_streams_tool_call_completion
    3 failed in 1.01s
    

After (d4d2694)

OpenAI SDK: deployment max_tokens, stream + stream_options + tools (the customer's shape)

from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:52737/v1", api_key=LITELLM_KEY)
stream = client.chat.completions.create(
model="gpt-5.6-terra-chat-deploymax",
messages=[{"role": "user", "content": "What is the weather in Paris?"}],
stream=True, stream_options={"include_usage": True},
tools=[{"type": "function", "function": {"name": "get_weather",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}])

run as: cd /private/tmp/claude-501/-Users-mateo-Development-litellm/89961715-811c-4684-82f6-82c7d78ce259/scratchpad/lit-9089/wt && .venv/bin/python -P /private/tmp/claude-501/-Users-mateo-Development-litellm/89961715-811c-4684-82f6-82c7d78ce259/scratchpad/lit-9089/qa-after/sdk_steps.py 1 (LITELLM_KEY read from $LITELLM_MASTER_KEY)

2. ```
##### step 1: gpt-5.6-terra-chat-deploymax, stream=True, stream_options include_usage, get_weather tool
tool_call delta: {'index': 0, 'id': 'call_0', 'function': {'arguments': '', 'name': 'get_weather'}, 'type': 'function'}
tool_call delta: {'index': 0, 'function': {'arguments': '{"'}, 'type': 'function'}
tool_call delta: {'index': 0, 'function': {'arguments': 'city'}, 'type': 'function'}
tool_call delta: {'index': 0, 'function': {'arguments': '":"'}, 'type': 'function'}
tool_call delta: {'index': 0, 'function': {'arguments': 'Paris'}, 'type': 'function'}
tool_call delta: {'index': 0, 'function': {'arguments': '"}'}, 'type': 'function'}
usage chunk: {"completion_tokens": 18, "prompt_tokens": 42, "total_tokens": 60, "completion_tokens_details": {"accepted_prediction_tokens": 0, "audio_tokens": 0, "reasoning_tokens": 0, "rejected_prediction_tokens": 0}, "prompt_tokens_details": {"audio_tokens": 0, "cached_tokens": 0, "cache_write_tokens": 0, "cache_creation_tokens": 0}, "cost": 0.00033}
HTTP 200 stream: 8 chunks, tool_calls=['get_weather'], finish_reason=['tool_calls'], usage.completion_tokens=18

OpenAI SDK: client max_tokens, then client max_completion_tokens

client.chat.completions.create(model="gpt-5.6-terra-chat", stream=False, max_tokens=1000,
messages=[{"role": "user", "content": "Say hello in one short sentence."}])
client.chat.completions.create(model="gpt-5.6-terra-chat", stream=False, max_completion_tokens=1000,
messages=[{"role": "user", "content": "Say hello in one short sentence."}])

run as: cd /private/tmp/claude-501/-Users-mateo-Development-litellm/89961715-811c-4684-82f6-82c7d78ce259/scratchpad/lit-9089/wt && .venv/bin/python -P /private/tmp/claude-501/-Users-mateo-Development-litellm/89961715-811c-4684-82f6-82c7d78ce259/scratchpad/lit-9089/qa-after/sdk_steps.py 2

2. ```
##### step 2: gpt-5.6-terra-chat, stream=False, max_tokens=1000
HTTP 200: content='Hello!', finish_reason=stop, usage.completion_tokens=6
usage: {"completion_tokens": 6, "prompt_tokens": 13, "total_tokens": 19, "completion_tokens_details": {"accepted_prediction_tokens": 0, "audio_tokens": 0, "reasoning_tokens": 0, "rejected_prediction_tokens": 0}, "prompt_tokens_details": {"audio_tokens": 0, "cached_tokens": 0, "cache_write_tokens": 0, "cache_creation_tokens": 0}}
##### step 2: gpt-5.6-terra-chat, stream=False, max_completion_tokens=1000
HTTP 200: content='Hello!', finish_reason=stop, usage.completion_tokens=6
usage: {"completion_tokens": 6, "prompt_tokens": 13, "total_tokens": 19, "completion_tokens_details": {"accepted_prediction_tokens": 0, "audio_tokens": 0, "reasoning_tokens": 0, "rejected_prediction_tokens": 0}, "prompt_tokens_details": {"audio_tokens": 0, "cached_tokens": 0, "cache_write_tokens": 0, "cache_creation_tokens": 0}}

curl /v1/chat/completions: deployment limit, stream

curl -s -N -w "\nHTTP %{http_code}\n" http://127.0.0.1:52737/v1/chat/completions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json"
-d '{"model":"gpt-5.6-terra-chat-deploymax","messages":[{"role":"user","content":"What is the weather in Paris?"}],"stream":true,"stream_options":{"include_usage":true},"tools":[{"type":"function","function":{"name":"get_weather","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}}]}'

2. ```
data: {"id":"chatcmpl-7unfxczxsct55o6zudpxzfsz3kvc7prdrwpngsmiusgvbkrdiqva","created":1791668957,"model":"gpt-5.6-terra-chat-deploymax","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","tool_calls":[{"id":"call_0","function":{"arguments":"","name":"get_weather"},"type":"function","index":0}]}}],"service_tier":"default"}
data: {"id":"chatcmpl-7unfxczxsct55o6zudpxzfsz3kvc7prdrwpngsmiusgvbkrdiqva","created":1791668957,"model":"gpt-5.6-terra-chat-deploymax","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"tool_calls":[{"function":{"arguments":"{\""},"type":"function","index":0}]}}],"service_tier":"default"}
data: {"id":"chatcmpl-7unfxczxsct55o6zu
...
pxzfsz3kvc7prdrwpngsmiusgvbkrdiqva","object":"chat.completion.chunk","created":1791668957,"model":"gpt-5.6-terra-chat-deploymax","service_tier":"default","choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}]}
data: {"id":"chatcmpl-7unfxczxsct55o6zudpxzfsz3kvc7prdrwpngsmiusgvbkrdiqva","created":1791668957,"model":"gpt-5.6-terra-chat-deploymax","object":"chat.completion.chunk","choices":[{"index":0,"delta":{}}],"usage":{"completion_tokens":18,"prompt_tokens":42,"total_tokens":60,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0,"cache_write_tokens":0,"cache_creation_tokens":0},"cost":0.00033},"service_tier":"default"}
data: [DONE]
HTTP 200

curl /v1/messages: max_tokens 1000

curl -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:52737/v1/messages -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json"
-d '{"model":"gpt-5.6-terra-chat","max_tokens":1000,"messages":[{"role":"user","content":"Say hello in one short sentence."}]}'

2. ```
{"id":"chatcmpl-4k6ns2s3n6pnoyjlnhqg3fz332h4bbh7wx2kodxedzhaebe54tca","type":"message","role":"assistant","model":"gpt-5.6-terra-chat","stop_sequence":null,"usage":{"input_tokens":13,"output_tokens":6},"content":[{"type":"text","text":"Hello!"}],"stop_reason":"end_turn","stop_details":null}
HTTP 200

curl /v1/responses: max_output_tokens 1000

curl -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:52737/v1/responses -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json"
-d '{"model":"gpt-5.6-terra-chat","input":"Say hello in one short sentence.","max_output_tokens":1000}'

2. ```
{"id":"resp_0mKoN3p8DbyMlZth8mf4eCXTW-7yGuKs8gfekkEJivmY54_yxu9YBaNoYSXnqisUVpZ0R_jCVLNTBtQAjEoqbYuOCTBvLpz2RSyf4BZDwJ98fOXU8G2m1SaQu39L6EPUJsy3udwTbm9sWxtjnxfsiahuh6vqqm8Rp8usb5xRUGGtAQJIW8HnIa9Ofqtb4dO2osqoXQ56N5BF1OjfWvOB5Xn6rV-tITkjHwtdGNoNZw8V16Ik9tCagUe3na-8PckTdcWKNq27yyK-s28sv3YXkLQDd4TLIijP-SBsDTtl88PW19NabJaP7qG1h1qgprsM-0Kgf1ZsfSq9dzeOcfIcZJM3l7a--kWkVq1xUmnTpHJ4Di3NAp-1H0E4-CJJynVkgZdjH5jAsdXIYhnOl9jsKeIk02MuV7cpcmjaLcOTwKSjplj48K1kS5W6","created_at":1791668958,"error":null,"incomplete_details":null,"instructions":null,"metadata":{},"model":"gpt-5.6-terra-chat","object":"response","output":[{"id":"msg_7f4548f126a0575e9a46336d89f024ef","content":[{"annotations":[],"text":"Hello!",
...
t":"none","context":"all_turns"},"status":"completed","text":{"format":{"type":"text"},"verbosity":"medium"},"truncation":"disabled","usage":{"input_tokens":13,"input_tokens_details":{"audio_tokens":null,"cached_tokens":0,"cached_tokens_details":null,"image_tokens":null,"text_tokens":null,"video_tokens":null,"cache_write_tokens":0},"output_tokens":6,"output_tokens_details":{"audio_tokens":null,"reasoning_tokens":0,"text_tokens":null},"total_tokens":19,"cost":null},"user":null,"store":true,"background":false,"billing":{"payer":"developer"},"completed_at":1791668958,"frequency_penalty":0.0,"max_tool_calls":null,"presence_penalty":0.0,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","safety_identifier":null,"service_tier":"default","top_logprobs":0,"moderation":null}
HTTP 200

(from the JSON above: status=completed, max_output_tokens=1000, usage.output_tokens=6, output text='Hello!')

Controls: gpt-5.4 and gpt-oss-120b with max_tokens 50 and max_completion_tokens 50

for m in gpt-5.4-chat gpt-oss-120b-chat; do for p in max_tokens max_completion_tokens; do
curl -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:52737/v1/chat/completions -H "Authorization: Bearer $LITELLM_MASTER_KEY" -H "Content-Type: application/json"
-d "{"model":"$m","messages":[{"role":"user","content":"Say hello in one short sentence."}],"stream":false,"$p":50}"
done; done

2. ```
##### gpt-5.4-chat max_tokens=50
{"id":"chatcmpl-63wvjyxhwdbr2bnio5bmcjixhtt2nndxr3azk43x7wyfiljontsa","created":1791668960,"model":"gpt-5.4-chat","object":"chat.completion","choices":[{"finish_reason":"stop","index":0,"message":{"content":"Hello!","role":"assistant","annotations":[]}}],"usage":{"completion_tokens":6,"prompt_tokens":13,"total_tokens":19,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0,"cache_write_tokens":0,"cache_creation_tokens":0}},"service_tier":"default"}
HTTP 200
##### gpt-5.4-chat max_completion_tokens=50
{"id":"chatcmpl-urci6qxgeyvzjlhwglyz2z7aj4fivteqdfrpfbn47rt7ufqc74wq","created":1791668961,"model":"gpt-5.4-chat","object":"chat.completion","choices":[{"finish_reason":"stop","index":0,"message":{"content":"Hello!","role":"assistant","annotations":[]}}],"usage":{"completion_tokens":6,"prompt_tokens":13,"total_tokens":19,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0,"cache_write_tokens":0,"cache_creation_tokens":0}},"service_tier":"default"}
HTTP 200
##### gpt-oss-120b-chat max_tokens=50
{"error":{"message":"litellm.BadRequestError: BedrockException - {\"error\":{\"code\":\"validation_error\",\"message\":\"model `openai.gpt-oss-120b` isn't supported on this route\",\"param\":null,\"type\":\"invalid_request_error\"}}\n\nLiteLLM: model group 'gpt-oss-120b-chat' failed with the error above. No fallback was attempted.","type":"invalid_request_error","param":null,"code":"400"}}
HTTP 400
##### gpt-oss-120b-chat max_completion_tokens=50
{"error":{"message":"litellm.BadRequestError: BedrockException - {\"error\":{\"code\":\"validation_error\",\"message\":\"model `openai.gpt-oss-120b` isn't supported on this route\",\"param\":null,\"type\":\"invalid_request_error\"}}\n\nLiteLLM: model group 'gpt-oss-120b-chat' failed with the error above. No fallback was attempted.","type":"invalid_request_error","param":null,"code":"400"}}
HTTP 400

Diagnostic, outside the brief's steps, to tell a vendor change from a proxy one (same bearer token, no proxy; the token is read from the rig env and never shown):
3. ```bash
for p in max_tokens max_completion_tokens; do
curl -s -w "\nHTTP %{http_code}\n" https://bedrock-mantle.us-east-1.api.aws/openai/v1/chat/completions -H "Authorization: Bearer $AWS_BEARER_TOKEN_BEDROCK" -H "Content-Type: application/json"
-d "{"model":"openai.gpt-oss-120b","messages":[{"role":"user","content":"Say hello in one short sentence."}],"$p":50}"
done
curl -s -o /dev/null -w "HTTP %{http_code}\n" https://bedrock-mantle.us-east-1.api.aws/openai/v1/chat/completions -H "Authorization: Bearer $AWS_BEARER_TOKEN_BEDROCK" -H "Content-Type: application/json"
-d '{"model":"openai.gpt-5.4","messages":[{"role":"user","content":"Say hello in one short sentence."}],"max_tokens":50}'

4. ```
##### direct Mantle /openai/v1/chat/completions openai.gpt-oss-120b max_tokens=50 (no proxy)
{"error":{"code":"validation_error","message":"model `openai.gpt-oss-120b` isn't supported on this route","param":null,"type":"invalid_request_error"}}
HTTP 400
##### direct Mantle /openai/v1/chat/completions openai.gpt-oss-120b max_completion_tokens=50 (no proxy)
{"error":{"code":"validation_error","message":"model `openai.gpt-oss-120b` isn't supported on this route","param":null,"type":"invalid_request_error"}}
HTTP 400
##### direct Mantle /openai/v1/chat/completions openai.gpt-5.4 max_tokens=50 (no proxy, sanity)
HTTP 200

The PR's diff against its merge base 988cc7dc94 touches litellm/llms/bedrock_mantle/chat/transformation.py (+24) and tests only, with no line naming gpt-oss or a route

e2e test file against this proxy

cd /private/tmp/claude-501/-Users-mateo-Development-litellm/89961715-811c-4684-82f6-82c7d78ce259/scratchpad/lit-9089/wt && LITELLM_PROXY_URL=http://127.0.0.1:45463 LITELLM_MASTER_KEY=$LITELLM_MASTER_KEY .venv/bin/python -m pytest tests/e2e/llm_translation/test_bedrock_mantle_output_limit_e2e.py -q -rA -p no:cacheprovider

2. ```
...                                                                      [100%]
==================================== PASSES ====================================
=========================== short test summary info ============================
PASSED tests/e2e/llm_translation/test_bedrock_mantle_output_limit_e2e.py::TestBedrockMantleChatOutputLimit::test_client_max_tokens_returns_completion
PASSED tests/e2e/llm_translation/test_bedrock_mantle_output_limit_e2e.py::TestBedrockMantleChatOutputLimit::test_client_max_completion_tokens_returns_completion
PASSED tests/e2e/llm_translation/test_bedrock_mantle_output_limit_e2e.py::TestBedrockMantleChatOutputLimit::test_deployment_max_tokens_streams_tool_call_completion
3 passed in 56.82s

Not verified: the customer's GovCloud /v1 route (the Medium caveat below); gpt-oss-120b, which Mantle refuses on this route today, on both legs and when called directly

Seen by the legs, none of it caused by this PR:

  • gpt-oss-120b control 400s on both legs; Mantle refuses it directly
  • /v1/responses with max_output_tokens is 200 on both legs
  • A new registration shows on one worker for about 20-30 s
  • /model/new echoes encrypted litellm_params values in its 200 body
  • The e2e file dies in the base harness's pydantic before any request

/live-pr-risk: d4d2694 against merge base 988cc7d

Verdict: no dependent path breaks. The only difference on the wire between base and head is max_tokens becoming max_completion_tokens on Mantle chat completions for GPT reasoning-series names (the gpt-5 and gpt-6 markers), which turns every base-side Unsupported parameter: 'max_tokens' 400 on gpt-5.6 and gpt-6 chat-mode deployments into a 200 and leaves every other request identical in key set, values, header key set, status, and count, plus one narrow bridged edge under allowed_openai_params named below. The head merged locally with main at ff60d27b60 (merge commit a5e62b2136, a clean merge with no change under litellm/llms/bedrock_mantle) answers all 71 cells with the same status as the head; main has since gained 906f927 (auth sentinel) and 0126c8d (sagemaker request body), neither touching this path

Method: an A/B rig on the GCE box (lit9089ab), the merge base and the head as two proxies on the same 30-deployment config and Postgres layout, each with a forwarding recorder in front of the real us-east-1 Mantle host for its /openai/v1 base and another for its /v1 base, and one in front of the real Langfuse host, so every outbound request was diffed base vs head on key set, values, header key set, status, and count. Three passes. First 71 cells (chat with max_tokens, max_completion_tokens, both, none, null, a duplicated raw key, streaming, streaming with tools under a deployment-level max_tokens: 64, a region-prefixed model string, a /v1 base, allowed_openai_params, additional_drop_params, use_chat_completions_api, a deployment without api_base, gpt-5.4, gpt-6, gpt-5.5 and gpt-oss controls, /v1/messages stream and non-stream, /v1/responses native and emulated over chat, a Langfuse-traced call read back through the Langfuse API, key generate, use, model-not-allowed, info, delete and use-after-delete, the spend row of every call, /health, /health/readiness, /health/liveliness, /v1/models, max_tokens: 50 against every bedrock_mantle/openai.gpt* cost-map id, and max_tokens as a string, a list, 0, -5, 40.0 and 10^9). Then 36 cells for the dependents the graph walk added (every cost-map id driven on the native chat path with an explicit custom_llm_provider, a bridged gpt-5.5 with allowed_openai_params: ["max_tokens"] and both client keys, /v1/completions, a virtual key with tpm_limit: 400 so the v3 rate limiter injects its own cap, /utils/transform_request with an uncatalogued openai.gpt-5.7-preview, Langfuse readback of a call that succeeds on both sides). Then the 71 cells again on the head merged with main. Credentials were the us-east-1 Mantle bearer token and the Langfuse keys from the main checkout's .env; nothing mocked, no fixtures, every upstream call billed

Breaking: none. Nothing went from 200 to a failure, and no status moved on any gpt-oss, gpt-5.4, gpt-5.5, Responses, Messages, key, spend, health, or models cell. The only status moves are 400 to 200 on the gpt-5.6 and gpt-6 chat-mode deployments, each with max_tokens on the base wire and max_completion_tokens with the same value on the head wire: chat with max_tokens, with both keys, streaming (4 chunks, stop), streaming with tools under the deployment's max_tokens: 64 (6 tool deltas, tool_calls), the region-prefixed id, the deployment with no api_base, the duplicated raw key, /v1/messages stream and non-stream, /v1/responses emulated over chat with max_output_tokens, /v1/completions with max_tokens: 32 (an OpenAIException 400 on base), the key-scoped call, the Langfuse-traced call, the tpm_limit: 400 key's call with no client limit (the limiter's injected max_tokens: 100 arrives as max_completion_tokens: 100), and the native sweep of gpt-5.6-luna, gpt-5.6-sol, gpt-5.6-terra, gpt-6-astra, gpt-6-luna, gpt-6-sol and gpt-6.1-sol. gpt-5.4 takes either name on Mantle (max_tokens: 50 200 on base, max_completion_tokens: 50 200 on head). The gpt-oss request bodies on /v1 are identical multisets on both sides (the per-leg difference is only which random prompt the health check drew), and their stop vs length finish reasons at the same limit are upstream nondeterminism, since gpt-oss spends 27 to 50 tokens reasoning on a one-word prompt. Attempt counts match on every cell (57 and 57 upstream calls in the extra pass, one per request, no retries), the header key set is accept, authorization, content-type, user-agent on every recorded request on both sides, and the Langfuse export (POST /api/public/otel/v1/traces) answered 200 to every batch on both sides with the same header key set

Backward incompatible: four contract changes, none of which fails a caller that works today. First, Mantle's validation error for a malformed limit on a gpt-5.6 chat deployment now quotes max_completion_tokens (Invalid type for 'max_completion_tokens', integer below minimum value, exceeds model maximum (1050000), the same 400 on both sides; the PR body's Low caveat, shipped as is). Second, additional_drop_params: ["max_completion_tokens"] with a client max_tokens no longer removes the limit on gpt-5.x, because the drop runs before the rename: that cell went from the 400 to a 200 with the limit on the wire (Low caveat, shipped as is). Third, allowed_openai_params: ["max_tokens"] re-adds the raw key after mapping, so the head sends both max_tokens and max_completion_tokens. On a native chat deployment that keeps the 400 it had on the base (the client cell, the both-keys cell, and the /health probe all 400 on both sides; Low caveat, shipped as is). On a bridged deployment with a client sending both keys the Responses bridge's last-key-wins loop now takes the max_tokens value: for gpt-5.5 with max_tokens: 50 and max_completion_tokens: 200 the base sent max_output_tokens: 200 and the head sent max_output_tokens: 50, both 200 to the client (cell bridged55.allowed.both, confirmed by the time-ordered recorder entries; without the allowlist both sides send 200). This edge needs an operator to allowlist a parameter that was never rejected and a client to send both keys at once, so it ships with the caveat above. Fourth, the operator-visible flips that follow from the fix: /health now reports the gpt-5.6 and gpt-6 chat-mode deployments healthy (base 10 healthy and 20 unhealthy on the 30-deployment config, head 26 and 4, the four being the allowed_openai_params deployment, the /v1-base deployment that Mantle rejects on both sides, and the two ids this account cannot call), the spend rows for those calls flip from failure rows (spend 0, 14 total tokens) to success rows (spend above 0, 18 tokens), and /utils/transform_request for an uncatalogued bedrock_mantle/openai.gpt-5.7-preview returns max_completion_tokens: 64 where it returned max_tokens: 64 (gpt-oss keeps max_tokens: 64 on both sides)

Regression risk, dependents this run could not drive: MCP sampling (sampling_handler.py sets max_tokens on the sampled chat call), modify_params in utils.py, and user_max_tokens (dead code, only ever set to None) inject or carry max_tokens the same way the health check and the v3 rate limiter do, and those two are wire-verified (the probe's 16 and the limiter's 100 arrive renamed), so the untested ones share the class and the direction, a 400 on base becoming a 200. The customer's GovCloud /v1 route (our GovCloud SigV4 credentials get 401 access_denied for gpt-5.6 and 200 for gpt-oss, so the rename on a /v1 base is covered only by the us-east-1 /v1 cell, where Mantle rejects gpt-5.6 with its own validation error on both sides and the body key sets match). openai.gpt-daybreak-blue-5.6-sol, whose name carries neither gpt-5 nor gpt-6, keeps max_tokens on the head (recorded on both sides, 404 "does not exist" from this account), which matters only if that model exists elsewhere, rejects max_tokens, and runs on the native chat path rather than its cost-map responses mode. Langfuse's modelParameters keeps the client's pre-mapping max_tokens: 40 on both sides (vendor-side readback on the base for gpt-oss and on the head for gpt-oss and gpt-5.6), so a dashboard reading that field sees no change and a reader expecting the wire key there finds it on neither side

Dependency graph: BedrockMantleChatConfig.map_openai_params has one dispatch, the custom_llm_provider == "bedrock_mantle" branch of get_optional_params through ProviderConfigManager.get_bedrock_mantle_config (verified live through /v1/chat/completions, /v1/completions, /v1/messages via the completion adapter, /v1/responses emulated over chat, and /utils/transform_request), no subclass, no other override, no other caller passing replace_max_completion_tokens_with_max_tokens, and bedrock_mantle_chat_config routes Claude ids to the Claude config before this code (unchanged). Callers that feed it a limit, with status: the client (verified live), the deployment's litellm_params.max_tokens (verified, 64 renamed), /health (verified, 16 renamed), the v3 rate limiter cap (verified, 100 renamed), allowed_openai_params and additional_drop_params (verified, above), MCP sampling and modify_params (untested, same class), user_max_tokens (dead). Paths the override does not reach, verified unchanged on the wire: the native /v1/responses path (max_output_tokens: 40 on both sides), the chat-to-Responses bridge for every cost-map id in responses mode (gpt-5.4, gpt-5.5, 5.6-luna, 5.6-terra, 6-astra, 6-luna, 6-sol and 6.1-sol all 200 with max_output_tokens: 50 on both sides, with the one allowed_openai_params edge above), gpt-oss on /v1 and /openai/v1 (identical bodies), OTel and Langfuse (pre-mapping params, Langfuse verified at the vendor). Claude ids on Mantle are a separate config and were not driven. Tests: the unit cases in tests/unit/llms/bedrock_mantle/, the integration wire test in tests/integration/providers/, and the e2e test in tests/e2e/llm_translation/ cover the client path (tested); the proxy-side injectors, /v1/completions, the bridged allowed_openai_params edge, and /utils/transform_request have no test naming them and were covered live here (verified-live); MCP sampling and modify_params are untested and unreachable in this rig without an MCP server or a modify_params hook (unreachable)

Not verified: the customer's GovCloud /v1 route for gpt-5.6 (401 access_denied on our GovCloud account with both SigV4 key sets; only gpt-oss is entitled there); openai.gpt-5.6-cyber and openai.gpt-daybreak-blue-5.6-sol, which us-east-1 answers with 404 "does not exist" on both sides (cyber is renamed on the head, daybreak is not); MCP sampling, modify_params, and user_max_tokens, reasoned above and not driven; Claude ids on Mantle, which this PR does not touch

Audit matrix

Audit round 2 of 3, at the PR tip d4d2694 against the merge base 988cc7d (round 1 ran at 62a88f7 against 0f6a06a and is replaced by this run). The integration cells this PR adds in tests/integration/providers/test_bedrock_mantle_output_limit_wire.py ran through tests/integration/run.py providers on a rig shaped like CircleCI's, built on the GCE box by remote_rig.py: an owned Postgres database, an owned Redis and the scripted upstream from tests/integration/_support/upstream.py playing Mantle per leg (it rejects max_tokens for gpt-5.6 and gpt-6 with the vendor's recorded 400 and accepts max_completion_tokens; gpt-5.4 and gpt-oss take both), and a two-worker proxy per leg booted from its own worktree, nothing mocked inside it. The product diff against the merge base is litellm/llms/bedrock_mantle/chat/transformation.py alone, so every cell and the chaos step reran on both legs at the tip. The base run and the two head runs ran side by side as lanes with the same seeds, 1 pytest worker each (CircleCI's setting for the group), and the head runs have the same collected and passed selections, no skips, no retries; the two owned-proxy chaos cells (C4, C5) ran inside the full runs. Cells drive the proxy through the OpenAI SDK, the Anthropic SDK, and raw httpx and assert the caller's response, the exact body the scripted peer received (limit key and value, model, path, attempt count), and the spend row by response id

run side pytest workers started (UTC) result
run 1 base 988cc7d 1 2026-10-10 22:10:53 27 failed, 9 passed in 165 s
run 1 head d4d2694 1 2026-10-10 22:10:53 36 passed in 227 s
run 2 head d4d2694 1 2026-10-10 22:14:44 36 passed in 254 s
The 36 cells, base 988cc7d against head d4d2694 twice
id surface scenario test base head verdict last ran at
H1 /v1/chat/completions non-stream, OpenAI SDK sync D1, client max_tokens 1000 test_openai_sdk_max_tokens_reaches_mantle_as_max_completion_tokens red, as expected (400 unsupported_parameter from the peer) green x2: 200, wire max_completion_tokens 1000 and no max_tokens, success spend row PASS d4d2694
H2 /v1/chat/completions stream, AsyncOpenAI D1, client max_completion_tokens 1000 test_async_openai_sdk_stream_keeps_max_completion_tokens red, as expected (400 unsupported_parameter from the peer) green x2: 200 stream to [DONE], wire max_completion_tokens PASS d4d2694
H3 /v1/chat/completions stream + tools + stream_options, raw httpx D2 (deployment max_tokens 1000), client sends no limit test_stream_with_tools_carries_the_deployment_limit_as_max_completion_tokens red, as expected (400 unsupported_parameter from the peer) green x2: 200, wire max_completion_tokens 1000, tools and stream_options intact PASS d4d2694
H4 /v1/messages non-stream, Anthropic SDK sync D1, max_tokens 1000 test_anthropic_sdk_messages_max_tokens_reaches_mantle_as_max_completion_tokens red, as expected (400 unsupported_parameter from the peer) green x2: 200 message, wire chat/completions max_completion_tokens 1000 PASS d4d2694
H5 /v1/messages stream, AsyncAnthropic D1, max_tokens 1000 test_async_anthropic_sdk_messages_stream_reaches_mantle_as_max_completion_tokens red, as expected (400 unsupported_parameter from the peer) green x2: 200 SSE relayed, wire max_completion_tokens PASS d4d2694
H6 /v1/responses non-stream and stream, raw httpx D1, max_output_tokens 1000 (diff not on this path) test_responses_max_output_tokens_is_forwarded_natively_on_both_legs green green x2: 200 both modes, wire /openai/v1/responses max_output_tokens 1000, no chat limit key, both spend rows PASS d4d2694
H7 /v1/chat/completions, raw httpx D3 region-prefixed model on a /v1 base, client max_tokens 1000 test_region_prefixed_deployment_on_a_v1_base_sends_the_bare_model_with_max_completion_tokens red, as expected (400 unsupported_parameter from the peer) green x2: 200, wire /v1/chat/completions model openai.gpt-5.6-terra, max_completion_tokens PASS d4d2694
H8 /v1/chat/completions, raw httpx D4 gpt-6-sol, client max_tokens test_gpt6_deployment_sends_max_completion_tokens red, as expected (400 unsupported_parameter from the peer) green x2: 200, max_completion_tokens PASS d4d2694
H9 /v1/chat/completions, raw httpx D5 gpt-oss control, client max_tokens 50 test_gpt_oss_control_keeps_the_openai_compatible_max_tokens[max_tokens] green green x2: 200, wire max_tokens 50 (unchanged) PASS d4d2694
H10 /v1/chat/completions, raw httpx D5 gpt-oss control, client max_completion_tokens 50 test_gpt_oss_control_keeps_the_openai_compatible_max_tokens[max_completion_tokens] green green x2: 200, wire max_tokens 50 (OpenAI-compatible rewrite, unchanged) PASS d4d2694
H11 /v1/chat/completions, raw httpx D6 gpt-5.4, client max_tokens 50 test_gpt54_control_joins_the_renamed_series red, as expected (base sends max_tokens; the PR names the whole GPT reasoning series) green x2: 200, wire max_completion_tokens 50 PASS d4d2694
H12 /v1/chat/completions, raw httpx D1, client max_tokens 50 and max_completion_tokens 1000 test_explicit_max_completion_tokens_wins_over_max_tokens red, as expected (400 unsupported_parameter from the peer) green x2: 200, wire max_completion_tokens 1000 only PASS d4d2694
H13 /v1/chat/completions, raw httpx D2 deployment max_tokens 1000, client max_tokens 50 test_request_limit_wins_over_the_deployment_limit red, as expected (400 unsupported_parameter from the peer) green x2: 200, wire max_completion_tokens 50 PASS d4d2694
H14 /v1/chat/completions, raw httpx D1, same body twice with Cache-Control no-cache test_identical_requests_with_no_cache_are_attempted_and_logged_once_each red, as expected (400 unsupported_parameter from the peer) green x2: two 200s, two peer attempts, two success rows with distinct ids PASS d4d2694
S1 /v1/chat/completions sad, raw httpx D1, max_tokens "abc" test_hostile_max_tokens_is_rejected_by_mantle_under_its_new_name[string] red, as expected (peer 400 names max_tokens) green x2: 400 invalid_type naming max_completion_tokens reaches the caller, one attempt PASS d4d2694
S2 /v1/chat/completions sad, raw httpx D1, max_tokens [1000] test_hostile_max_tokens_is_rejected_by_mantle_under_its_new_name[list] red, as expected (peer 400 names max_tokens) green x2: 400 invalid_type naming max_completion_tokens PASS d4d2694
S3 /v1/chat/completions sad, raw httpx D1, max_tokens "" test_hostile_max_tokens_is_rejected_by_mantle_under_its_new_name[empty] red, as expected (peer 400 names max_tokens) green x2: 400 invalid_type naming max_completion_tokens PASS d4d2694
S4 /v1/chat/completions sad, raw httpx D1, max_tokens 5 KB string test_hostile_max_tokens_is_rejected_by_mantle_under_its_new_name[5kb] red, as expected (peer 400 names max_tokens) green x2: 400 invalid_type, proxy healthy after PASS d4d2694
S5 /v1/chat/completions sad, raw bytes D1, duplicate JSON key max_tokens 50 then 1000 test_duplicate_max_tokens_keys_in_raw_json_send_the_last_value_renamed red, as expected (400 unsupported_parameter from the peer) green x2: 200, wire max_completion_tokens 1000 (last key wins) PASS d4d2694
S6 /v1/chat/completions sad, raw httpx D1, max_tokens 0 test_out_of_range_max_tokens_is_rejected_by_mantle_under_its_new_name[0] red, as expected (peer 400 names max_tokens) green x2: 400 integer_below_min_value naming max_completion_tokens PASS d4d2694
S6 /v1/chat/completions sad, raw httpx D1, max_tokens -5 test_out_of_range_max_tokens_is_rejected_by_mantle_under_its_new_name[-5] red, as expected (peer 400 names max_tokens) green x2: 400 integer_below_min_value naming max_completion_tokens PASS d4d2694
S7 /v1/chat/completions sad, raw httpx D1 with a wrong deployment bearer, peer 401 test_wrong_deployment_bearer_reaches_the_caller_as_401_after_one_attempt green green x2: 401 reaches the caller, one attempt (unchanged) PASS d4d2694
S8 /v1/chat/completions sad, raw httpx unknown model openai.gpt-5.6-cyber, peer 404 test_unknown_mantle_model_reaches_the_caller_as_404_after_one_attempt green green x2: 404 reaches the caller, one attempt (unchanged) PASS d4d2694
S9 /v1/chat/completions sad, raw httpx D1 allowed_openai_params [max_tokens], max_tokens 50 test_allowed_openai_params_max_tokens_is_sent_verbatim_beside_the_renamed_limit red, as expected (base sends max_tokens only) green x2: 400, wire carries both max_tokens and max_completion_tokens (Low caveat row) PASS d4d2694
S10 /v1/chat/completions sad, raw httpx D1 additional_drop_params [max_completion_tokens], max_tokens 50 test_additional_drop_params_max_completion_tokens_drops_before_the_rename red, as expected (400 unsupported_parameter from the peer) green x2: 200, wire max_completion_tokens 50 (the drop runs before the rename; Low caveat row) PASS d4d2694
S11 /v1/chat/completions sad, raw httpx unrelated key scoped to D5 after the sad cells test_unrelated_key_scoped_to_the_control_deployment_keeps_working green green x2: 200 (unchanged) PASS d4d2694
S12 /v1/chat/completions sad, raw httpx bad proxy bearer test_unauthenticated_request_never_reaches_mantle green green x2: 401 from the proxy, no peer attempt (unchanged) PASS d4d2694
E1 /v1/chat/completions edge, raw httpx D1, max_tokens null test_null_or_missing_max_tokens_sends_no_limit[null] green green x2: 200, no limit key on the wire (unchanged) PASS d4d2694
E2 /v1/chat/completions edge, raw httpx D1, no limit at all test_null_or_missing_max_tokens_sends_no_limit[missing] green green x2: 200, no limit key on the wire (unchanged) PASS d4d2694
E3 /v1/chat/completions edge, response cache D1, identical body twice without no-cache test_identical_requests_without_no_cache_serve_the_second_from_cache red, as expected (400 unsupported_parameter from the peer) green x2: first 200 from the peer, second 200 from cache with no second attempt PASS d4d2694
E4 /v1/chat/completions edge, /model/update under traffic D2 deployment max_tokens 1000 flipped to 2000 test_model_update_of_the_deployment_limit_stays_renamed_and_converges red, as expected (400 unsupported_parameter from the peer) green x2: every wire body carries max_completion_tokens, never max_tokens; value becomes 2000 within the DB refresh PASS d4d2694
C1 chat + /v1/messages chaos, half streaming D1, burst of 30 concurrent requests test_concurrent_chat_and_messages_burst_is_renamed_and_logged_once_each red, as expected (30 x 400) green x2: 30 x 200, each marker once, 30 renamed wire bodies, 30 success rows PASS d4d2694
C2 /v1/chat/completions chaos D1, peer 503 for a third of a 12-request burst test_peer_503s_reach_their_callers_once_while_the_rest_are_renamed_and_served red, as expected green x2: 503s reach their callers after one attempt (num_retries 0), the rest 200, recovery 200 PASS d4d2694
C3 /v1/chat/completions chaos, stream D1, 10 concurrent slow streams test_slow_mantle_streams_are_forwarded_once_renamed red, as expected green x2: each forwarded once to [DONE] with its own marker, renamed wire bodies PASS d4d2694
C4 /v1/chat/completions chaos, owned 2-worker proxy D7 YAML deployment, SIGKILL one worker mid-burst of 20 test_worker_sigkill_mid_burst_leaves_the_sibling_renaming_the_yaml_limit red, as expected green x2: survivor serves its share, follow-up 200 with max_completion_tokens 1000 PASS d4d2694
C5 /v1/chat/completions chaos, owned proxy restart mid-burst of 20 held requests test_proxy_restart_mid_burst_loses_only_in_flight_requests_and_renames_after_restart red, as expected green x2: in-flight loss counted, follow-up after restart 200 renamed PASS d4d2694

The rows only real Mantle can prove ran once per leg as live cells (live_cells.py, included this time) through a head and a merge-base proxy on the same config, with the request, the proxy response, the vendor response id, and the spend row saved per row in audit-live-artifacts.tar.gz under <leg>/<row>.json, credentials redacted. L1 runs the customer's deployment shape against us-east-1 with the account's Mantle bearer; L2 to L5 run SigV4 deployments against the us-gov-west-1 /v1 base with the QA account's IAM keys

The 5 live cells, base 988cc7d against head d4d2694
id route and credential request base head head response id head spend row verdict
L1 us-east-1 /openai/v1, bearer token D1 shape, chat max_tokens 50 400 200 chatcmpl-ocpiyyw4yjua7cch6rtre3szr3quqbnwax3z4m3ry36u7ifhn7jq success, spend 0.0001232 PASS
L2 us-gov-west-1 /v1, SigV4 (the customer's route) D3 shape, chat max_tokens 50 401 401 no response id (status 401, call 45a66e34-f9dd-4446-98fe-3a66652cb451) failure, spend 0.0 BLOCKED (account entitlement)
L3 us-gov-west-1 /v1, SigV4 D2 shape, stream + tools + stream_options, no client limit 401 401 no response id (status 401, call 7b34d89f-60c4-4917-9dce-4092ed22b9d9) failure, spend 0.0 BLOCKED (account entitlement)
L4 us-gov-west-1 /v1, SigV4 /v1/messages max_tokens 50 401 401 no response id (status 401, call ac8b2e9d-c889-4013-a0b9-0df1c2caeede) failure, spend 0.0 BLOCKED (account entitlement)
L5 us-gov-west-1 /v1, SigV4, control gpt-oss-120b, chat max_tokens 50 200 200 chatcmpl-50724608-99c1-4916-b9c7-1de7e5b72675 success, spend 5.058e-05 PASS
  • d4d2694 passes /audit. Every deterministic cell and L1 and L5 pass; L2, L3 and L4 (the customer's own us-gov-west-1 route for gpt-5.6-terra) stay BLOCKED BY HUMAN because no credential we hold is entitled to that model there: Mantle answers 401 access_denied ... contact AWS Sales on both legs, and the GovCloud QA account's Bedrock catalog in us-gov-west-1 and us-gov-east-1 lists only the two gpt-oss models. H7 proves the wire shape of that deployment (bare model on the /v1 base, max_completion_tokens), L1 proves real Mantle accepts max_completion_tokens for gpt-5.6-terra, and L5 proves the SigV4 GovCloud route serves on both legs

Type

🐛 Bug Fix

Caveats (if any)

High

  • A gpt-5.x Mantle deployment left in its default Responses-bridged mode with allowed_openai_params: ["max_tokens"], called by a client that sends both max_tokens: 50 and max_completion_tokens: 200, now forwards max_output_tokens: 50 where main forwarded 200 (both HTTP 200). The allow-list re-adds the raw max_tokens after the rename moved the explicit value to max_completion_tokens, and the bridge takes the last key it sees, so the TLDR's "explicit max_completion_tokens wins" rule flips on this one path. Decision: ship with it. It takes an operator allow-listing a parameter Mantle never rejected plus a client sending two different limits in one request, the limit applied is still one the client sent, and the fix belongs in the bridge (prefer max_completion_tokens when both keys arrive), which every provider shares and which would re-run every live gate here for no change on the customer's path, so it is filed as a follow-up

Medium

  • The customer's own route, bedrock_mantle/us-gov-west-1/openai.gpt-5.6-terra on the us-gov-west-1 /v1 base with SigV4, was not observed end to end on either leg: no account we hold is entitled to gpt-5.6-terra in GovCloud (Mantle answers 401 access_denied ... contact AWS Sales, and the GovCloud account's Bedrock catalog in us-gov-west-1 and us-gov-east-1 lists only the gpt-oss models), so the audit's rows L2 to L4 stay BLOCKED BY HUMAN. The rename is wire-verified for that exact deployment shape against the scripted peer (H7), real Mantle accepts max_completion_tokens for gpt-5.6-terra in us-east-1 (L1), and the SigV4 GovCloud route itself serves on both legs (L5); the rename does not depend on the base path or the region

Low

  • Chat-mode gpt-5.4 and gpt-5.5 Mantle deployments, which accepted max_tokens before, now send max_completion_tokens as well, because the rename keys on the GPT-5 and GPT-6 series rather than on the names that refuse max_tokens. That is the decision recorded in the TLDR: every gpt-5.x and gpt-6 model this account can call in us-east-1 answered 200 with the new key on the head leg, gpt-5.4 and gpt-5.5 answered 200 with either name on both legs, and a list of the names that refuse max_tokens would re-break each new GPT release until someone added it. The GovCloud gpt-5.4 rows carry the same credential gap as the Medium above (every gpt-5.x name is 401 there with every key we hold)
  • Mantle's validation error for a malformed max_tokens (a string, a list, 0, a negative number) now says Invalid type for 'max_completion_tokens' or Invalid 'max_completion_tokens', naming the parameter the proxy actually sent. Rewriting a provider's error text back to the caller's spelling would hide what went over the wire, so this stays as is
  • allowed_openai_params: ["max_tokens"] on a gpt-5.x Mantle chat deployment re-adds the raw max_tokens after mapping and the upstream 400 stays, exactly as before this PR. That allow-list exists for parameters a provider config does not support, and max_tokens is supported here, so nobody needs it on these deployments; special-casing it is net-negative
  • additional_drop_params: ["max_completion_tokens"] together with a client max_tokens no longer strips the limit, because dropping runs before the rename; the request now succeeds with the limit applied where it returned the 400 before. The native OpenAI GPT-5 path has the same order (gpt-5.4 with that drop list and a client max_tokens: 50 also sends max_completion_tokens: 50), an operator who wants no limit at all drops max_tokens, so re-running the drop after mapping for one provider is net-negative
  • The rename repeats what with_max_completion_tokens does for the Bedrock runtime chat path (feat(bedrock): serve gpt-5.6+ chat completions natively by default, with chat_completions/ opt-in for gpt-oss and grok #44307): the same one-line comprehension, keyed by the same series helper. Folding the Mantle override onto that helper changes no behavior and would re-run every live gate at this tip, so it is left for a follow-up
  • The After leg's proxy booted from d4d2694, two commits behind the tip: 008996b is tests-only and 1e8767c merges main with no change under litellm/llms/bedrock_mantle or in the Mantle branch of get_optional_params, so the proof carries under /qa's no-op rule and the tip itself never served a request
  • OpenTelemetry and Arize spans take llm.request.max_tokens from the mapped max_tokens key only, so these gpt-5.x Mantle chat calls log no request limit on the span, the same gap every native OpenAI gpt-5 call has today; reading max_completion_tokens too is a two-integration follow-up, not this PR

QA runbook

  • tests/e2e/llm_translation/test_bedrock_mantle_output_limit_e2e.py::TestBedrockMantleChatOutputLimit::test_client_max_tokens_returns_completion - a chat-mode gpt-5.6 Mantle deployment returns a completion when the client sends max_tokens
    • POST /model/new with the master key: litellm_params model: bedrock_mantle/openai.gpt-5.6-terra, custom_llm_provider: bedrock_mantle, api_key: os.environ/AWS_BEARER_TOKEN_BEDROCK, api_base: https://bedrock-mantle.us-east-1.api.aws/openai/v1, reasoning_effort: none; model_info mode: chat, supported_endpoints: ["/v1/chat/completions", "/v1/responses"] (needs STORE_MODEL_IN_DB=True and a Mantle bearer token for us-east-1)
    • POST /v1/chat/completions with that model, one user message, and max_tokens: 1000
    • Expect HTTP 200 with a chat completion, not a 400 naming max_tokens
    • Sanity check: this test makes sense to add and is not hand-wavey (e.g., assert actual expected spend instead of just spend > 0) or potentially flaky
  • tests/e2e/llm_translation/test_bedrock_mantle_output_limit_e2e.py::TestBedrockMantleChatOutputLimit::test_client_max_completion_tokens_returns_completion - the same deployment returns a completion when the client sends max_completion_tokens
    • Register the deployment as above
    • POST /v1/chat/completions with that model, one user message, and max_completion_tokens: 1000
    • Expect HTTP 200 with a chat completion
    • Sanity check: this test makes sense to add and is not hand-wavey (e.g., assert actual expected spend instead of just spend > 0) or potentially flaky
  • tests/e2e/llm_translation/test_bedrock_mantle_output_limit_e2e.py::TestBedrockMantleChatOutputLimit::test_deployment_max_tokens_streams_tool_call_completion - a deployment-level max_tokens no longer breaks a streamed tool-call request that carries no limit of its own
    • Register the deployment as above with max_tokens: 1000 added to litellm_params
    • POST /v1/chat/completions with that model, the user message "What is the weather in Paris?", stream: true, stream_options: {"include_usage": true}, one get_weather function tool taking a city, and tool_choice: required
    • Expect HTTP 200, an SSE stream that reaches [DONE] with no error chunk, whose tool-call deltas assemble to a get_weather call with a non-empty city argument
    • Sanity check: this test makes sense to add and is not hand-wavey (e.g., assert actual expected spend instead of just spend > 0) or potentially flaky

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

  • d4d2694 passes /live-pr-risk


Note

Medium Risk
Changes the upstream request shape for Mantle chat on GPT reasoning-series models; scoped to one provider config but affects all output-limit paths (client, deployment, messages adapter).

Overview
Fixes Bedrock Mantle chat completions for GPT reasoning-series models (gpt-5.x, gpt-6) by overriding BedrockMantleChatConfig.map_openai_params: after normal OpenAI mapping, max_tokens is renamed to max_completion_tokens and the old key is dropped, using the same is_gpt_reasoning_series_name check as native OpenAI. Non–reasoning Mantle models (e.g. gpt-oss) keep max_tokens behavior unchanged.

Adds unit coverage via get_optional_params, a live e2e suite for client/deployment limits and streamed tool calls, and a large integration wire matrix (SDKs, /v1/messages, concurrency, chaos) that asserts the upstream body. Test harness tweaks: configurable proxy ready/stop deadlines and richer e2e model registration types (max_tokens, chat mode, supported_endpoints).

Reviewed by Cursor Bugbot for commit 76b96e5. Bugbot is set up for automated code reviews on this repo. Configure here.

@devin-ai-integration

devin-ai-integration Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@greptile-apps

greptile-apps Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[Medium impact] Adjusts how output limits are sent to certain AI models.

The product fix appears sound, but the live tests must meet the repository's SDK and metadata requirements before merging.

Findings

  1. P2 Tests bypass the customer SDK ▶
  2. P2 Tests lack required metadata ▶

Summary

Mantle chat requests now send max_completion_tokens for GPT reasoning-series models.

  • GPT reasoning-series Mantle chat requests send output limits as max_completion_tokens.

Reviews (6) · Last reviewed commit: "refactor(bedrock_mantle): type the max_c..." · Reviewed by Greptile

Comment thread tests/e2e/llm_translation/test_bedrock_mantle_output_limit_e2e.py Outdated
@codecov

codecov Bot commented Oct 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed

codspeed Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

⚠️ 1 benchmark spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

✅ 31 untouched benchmarks


Comparing litellm_bedrock_mantle_gpt_max_completion_tokens (1e8767c) with main (9e2aae8)

Open in CodSpeed

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

Comment thread tests/e2e/llm_translation/test_bedrock_mantle_output_limit_e2e.py Outdated

@cursor cursor Bot left a comment •

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.

Stale Bugbot comment from a previous run.

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment •

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.

Stale Bugbot comment from a previous run.

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment •

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.

Stale Bugbot comment from a previous run.

Comment thread litellm/llms/bedrock_mantle/chat/transformation.py Outdated
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment •

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.

Stale Bugbot comment from a previous run.

…e_gpt_max_completion_tokens

# Conflicts:
#	tests/e2e/models.py
#	tests/integration/_support/process.py
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

Comment on lines +117 to +120
result = client.proxy.transport.send(
"/chat/completions",
headers=client.proxy.transport.bearer(resources.key()),
json=ChatBody(model=model, messages=_prompt(), max_tokens=OUTPUT_LIMIT),

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 Tests bypass the customer SDK

All three new chat tests use transport.send or chat_stream instead of the suite's sdk fixture. The repository requires LLM calls in llm_translation/ to use the real provider SDKs so tests check the responses and exceptions customers receive. Switch all three calls to sdk before merging to meet this requirement.

File Used: tests/e2e/AGENTS.md (source)



class TestBedrockMantleChatOutputLimit:
def test_client_max_tokens_returns_completion(self, client: PassthroughClient, resources: ResourceManager) -> None:

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 Tests lack required metadata

None of the three new tests declares Subject. The repository requires every test under tests/e2e/, except the load suite, to declare at least its domain. Add @meta(Subject(...)) to each test before merging so their results identify what they test and meet this requirement.

File Used: tests/e2e/AGENTS.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

… fixture and tag their subject

The three live tests in tests/e2e/llm_translation/test_bedrock_mantle_output_limit_e2e.py
called the proxy through a hand-built client; they now use the suite's sdk fixture
(SdkClients.openai with NO_PROXY_CACHE) and declare @meta(Subject(...)) for the
bedrock_mantle provider, the chat completions route, and the stream and non-stream modes,
the way tests/e2e/AGENTS.md asks. The streamed tool-call assertion reads the tool name
and arguments from the deltas and validates the arguments as JSON
@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri

Copy link
Copy Markdown
Contributor

bugbot run

@cursor cursor Bot left a comment

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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 1e8767c. Configure here.

@mateo-berri

Copy link
Copy Markdown
Contributor

@greptileai

@mateo-berri mateo-berri left a comment

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.

LGTM

@mateo-berri
mateo-berri merged commit c0ca4a5 into main Oct 11, 2026
90 of 91 checks passed
@mateo-berri
mateo-berri deleted the litellm_bedrock_mantle_gpt_max_completion_tokens branch October 11, 2026 18:28

This branch was successfully deployed

1 active deployment
e2e-changed — 1e8767c0 Deployed Oct 11, 2026 by mateo-berri via oauth #4504
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.

1 participant