Skip to content

Map top_p to topP in the Bedrock Titan and AI21 completions adapters - #25795

Merged
tanghaoji merged 3 commits into
mlflow:masterfrom
Yash-Chindam:fix/25571-bedrock-topp
Sep 12, 2026
Merged

tanghaoji merged 3 commits into
mlflow:masterfrom
Yash-Chindam:fix/25571-bedrock-topp

Conversation

@Yash-Chindam

@Yash-Chindam Yash-Chindam commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Related Issues/PRs

Relates to #25571

What changes are proposed in this pull request?

Both AWSTitanAdapter.completions_to_model and AI21Adapter.completions_to_model build their Bedrock request body with rename_payload_keys, which passes through any key absent from its mapping unchanged. Neither mapping covered top_p, so a caller-supplied value reached Bedrock under the literal key top_p. Titan's textGenerationConfig and Jurassic's request body both name that field topP, so Bedrock did not recognise the key and silently ignored it — the request succeeded and returned a normal completion, just without the nucleus sampling the caller asked for.

This adds "top_p": "topP" to both mappings. The outbound body, produced by running the adapters:

Adapter Before After
Titan (textGenerationConfig) {'maxTokenCount': 1000, 'top_p': 0.9} {'maxTokenCount': 1000, 'topP': 0.9}
AI21 (request body) {'maxTokens': 1000, 'top_p': 0.9} {'maxTokens': 1000, 'topP': 0.9}

On top_k — flagging a deviation from the triage note so it is cheap to redirect

The triage note on #25571 suggested mapping top_k -> topK alongside top_p. I did not, because I could not find a topK field on either model family to map onto:

  • Titan's textGenerationConfig accepts only maxTokenCount, stopSequences, temperature and topP.
  • Jurassic exposes topKReturn, which controls how many alternative tokens are reported, not top-k sampling. Mapping top_k onto it would change the response payload rather than the sampling behaviour.
  • Corroborating from inside this repo: the Converse path in this same file builds inferenceConfig from temperature/topP/maxTokens/stopSequences and omits topK (bedrock.py:408-421), and no Bedrock code path in the repo maps topK anywhere. GeminiAdapter does map top_k -> topK, because Gemini genuinely supports it.

Renaming top_k -> topK here would swap one key Bedrock ignores for another while making the code look like it handles the parameter — the same silent-drop failure mode the issue is about. So instead of deleting the two # TODO handle top_p, top_k, etc. comments as though they were fully addressed, I replaced them with notes recording why top_k and the penalty parameters stay unmapped.

That leaves a decision I would rather you make than guess at. top_k is currently accepted and ignored; the alternative is to reject it with a 422, which this file already does for n != 1 on Titan (and AnthropicAdapter does for n, and for temperature+top_p together). I left that out because it turns a currently-succeeding request into a hard error and CONTRIBUTING.md asks for backwards-compatible changes — but it is a small follow-up in either direction, and I am happy to add it here if you prefer. I have kept this as "Relates to" rather than "Closes" so #25571 does not auto-close while that is open.

How is this PR tested?

  • Existing unit/integration tests
  • New unit/integration tests

Two new cases in bedrock_model_provider_fixtures (Titan and Jurassic, each sending top_p). These run through the existing test_bedrock_request_response, which drives the real AmazonBedrockProvider.completions and asserts the exact body handed to Bedrock via mock_request.assert_called_once_with(model_request) — so they pin the outbound key name, not just the adapter's return value. Each fixture runs against all four bedrock_aws_configs.

I also confirmed the new tests actually catch the bug, by reverting only the two mapping entries and re-running. They fail with exactly the right message, and the six pre-existing cases still pass:

AssertionError: expected call not found.
Expected: _request({'inputText': 'This is a test', 'textGenerationConfig': {'maxTokenCount': 1000, 'topP': 0.9}})
  Actual: _request({'inputText': 'This is a test', 'textGenerationConfig': {'maxTokenCount': 1000, 'top_p': 0.9}})
A note on my local verification, in the interest of transparency

I could not run the suite through pytest locally. Every test in tests/gateway/providers/test_bedrock.py errors at setup with RuntimeError: Failed to start server, including tests this PR does not touch. The cause is environmental and specific to my machine: mlflow is not registered as an installed distribution in my interpreter, so IS_TRACING_SDK_ONLY is true, which makes the session fixture at tests/conftest.py:815 autouse; it shells out to uv run ... mlflow server, and my uv build cannot parse exclude-newer = "P7D" in pyproject.toml.

So I verified by importing the real fixture table and reproducing the exact assertions from test_bedrock_request_response without the conftest in the way. All 8 applicable fixtures x 4 AWS configs pass (32/32; Cohere skipped, as it is in the suite):

[3] AMAZON     amazon.titan-tg1-large   -> PASS
[4] AMAZON     amazon.titan-tg1-large   -> PASS  <-- top_p case
      outbound body: {'inputText': 'This is a test', 'textGenerationConfig': {'maxTokenCount': 1000, 'topP': 0.9}}
[5] AI21       ai21.j2-ultra            -> PASS
[6] AI21       ai21.j2-ultra            -> PASS  <-- top_p case
      outbound body: {'prompt': 'This is a test', 'maxTokens': 1000, 'topP': 0.9}
[7] AI21       ai21.j2-mid              -> PASS

total=32 passed=32 skipped=0

CI is the real check for the pytest run.

ruff check and ruff format --check pass on both changed files. Commits are DCO signed off.

Does this PR require documentation update?

  • No.

Does this PR require updating the MLflow Skills repository?

  • No.

Release Notes

Is this a user-facing change?

  • Yes. Give a description of this change to be included in the release notes for MLflow users.

Fixed top_p being silently ignored for Amazon Titan and AI21 Jurassic models on the AI Gateway's llm/v1/completions endpoint. The parameter was forwarded to Bedrock under its OpenAI-style top_p name instead of the topP name those models expect, so requests succeeded while ignoring the caller's nucleus-sampling setting.

What component(s), interfaces, languages, and integrations does this PR affect?

Components

  • area/gateway: MLflow AI Gateway client APIs, server, and third-party integrations

How should the PR be classified in the release notes? Choose one:

  • rn/bug-fix - A user-facing bug fix worth mentioning in the release notes

Is this PR a critical bugfix or security fix that should go into the next patch release?

  • This PR is critical and needs to be in the next patch release
  • This PR can wait for the next minor release

Disclosure: I used an AI assistant (Claude) to help trace the call paths, implement the fix, and draft this description. Every file/line reference and every result above was produced by running the code, and I reviewed the change before submitting.

🤖 Generated with Claude Code

@github-actions github-actions Bot added the community Community/external contribution label Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor
Install mlflow from this PR

Install mlflow from this PR

# mlflow
pip install git+https://github.com/mlflow/mlflow.git@refs/pull/25795/merge
# mlflow-skinny
pip install git+https://github.com/mlflow/mlflow.git@refs/pull/25795/merge#subdirectory=libs/skinny

For Databricks, use the following command:

%sh curl -LsSf https://raw.githubusercontent.com/mlflow/mlflow/HEAD/dev/install-skinny.sh | sh -s pull/25795/merge

PR author's recent activity

In the last 14 days, @Yash-Chindam opened 15 PRs across 4 repos:

Repository Open Closed Merged Total
mlflow/mlflow 5 0 1 6
github/spec-kit 1 2 3 6
obra/superpowers 2 0 0 2
Yash-Chindam/Yash-Chindam 0 0 1 1

@github-actions github-actions Bot added team-review Trigger a team review request size/M labels Sep 11, 2026
@github-actions github-actions Bot added area/gateway MLflow AI Gateway client APIs, server, and third-party integrations rn/bug-fix Mention under Bug Fixes in Changelogs. labels Sep 11, 2026
Both adapters built their request body with `rename_payload_keys`, which passes
through any key absent from its mapping unchanged. Neither mapping covered
`top_p`, so a caller-supplied value was forwarded to Bedrock as the literal key
`top_p`. Titan's `textGenerationConfig` and Jurassic's request body both name
the field `topP`, so Bedrock did not recognise it and silently ignored it: the
request succeeded and returned a normal completion, just without the nucleus
sampling the caller asked for.

`top_k` is deliberately left unmapped. Titan's `textGenerationConfig` accepts
only maxTokenCount, stopSequences, temperature and topP, and Jurassic's
`topKReturn` controls how many alternative tokens are reported rather than
top-k sampling. Renaming `top_k` to `topK` would swap one key Bedrock ignores
for another while making the code look like it handles the parameter. The
Converse path in this same file omits topK from `inferenceConfig` for the same
reason. Both TODO comments are replaced with notes recording this.

Relates to mlflow#25571. Deliberately not "Closes": the `top_k` half of that issue is
a question for the maintainers, so the issue should not auto-close on merge.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yash-Chindam <[email protected]>
@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Documentation preview for 2d1bda8 is available at:

More info
  • Ignore this comment if this PR does not change the documentation.
  • The preview is updated when a new commit is pushed to this PR.
  • This comment was created by this workflow run.
  • The documentation was built by this workflow run.

@tanghaoji tanghaoji left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Yash-Chindam Thanks for spliting the PR and the careful analysis. The top_p mappings and regression tests look correct, and I agree that mapping top_k to either topK or topKReturn would be incorrect.
One boundary case should be handled before landing: MLflow accepts top_p=0, but Titan requires topP to be strictly greater than zero. With this change, that request reaches Bedrock and can surface as an uncaught provider validation error. Could you add Titan-specific validation returning 422 and a test for top_p=0?

Titan's `textGenerationConfig` requires `topP` to be strictly greater than
zero, but MLflow's request schema accepts `top_p=0`. Now that `top_p` is
actually forwarded as `topP`, such a request reaches Bedrock and surfaces as
an uncaught provider validation error, so reject it up front with a 422 the
way the adapter already does for `n != 1`.

Co-Authored-By: Claude Opus 5 <[email protected]>
Signed-off-by: Yash-Chindam <[email protected]>

@tanghaoji tanghaoji left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM! Thanks for addressing the comment

@tanghaoji
tanghaoji merged commit 7fc307a into mlflow:master Sep 12, 2026
51 of 53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/gateway MLflow AI Gateway client APIs, server, and third-party integrations community Community/external contribution rn/bug-fix Mention under Bug Fixes in Changelogs. size/M team-review Trigger a team review request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants