fix(ai): omit maxTokens for Nova 2 at high reasoning effort (#51150)

This commit is contained in:
Aiden Cline
2026-09-24 10:45:43 -05:00
committed by GitHub
Unverified
parent c1f50659a7
commit 6afcc887c8
2 changed files with 34 additions and 2 deletions
+15 -2
View File
@@ -430,10 +430,23 @@ const lowerSystem = (breakpoints: BedrockCache.Breakpoints, system: ReadonlyArra
return content.length === 0 ? undefined : content
}
// Nova 2 rejects `maxTokens` at high reasoning effort, where its output can exceed the field's maximum. Other models
// that take `reasoningConfig`, such as Grok on Bedrock, accept it.
const isNova2 = (model: LanguageModel) => /\bamazon\.nova-2-/.test(model.id)
const isHighReasoningEffort = Schema.is(
Schema.Struct({
additionalModelRequestFields: Schema.Struct({
reasoningConfig: Schema.Struct({ maxReasoningEffort: Schema.Literal("high") }),
}),
}),
)
const fromRequest = Effect.fn("BedrockConverse.fromRequest")(function* (request: LLMRequest) {
const toolChoice = request.toolChoice ? yield* lowerToolChoice(request.toolChoice) : undefined
const flattened = ProviderShared.flattenToolRequest(request)
const generation = request.generation
const maxTokens =
isNova2(request.model) && isHighReasoningEffort(request.http?.body) ? undefined : generation?.maxTokens
// Bedrock-Claude shares Anthropic's 4-breakpoint cap. Spend the budget in
// tools → system → messages order to favour the highest-impact prefixes.
const breakpoints = BedrockCache.breakpoints(request.model.id)
@@ -455,14 +468,14 @@ const fromRequest = Effect.fn("BedrockConverse.fromRequest")(function* (request:
}
const inferenceConfig = (() => {
if (
generation?.maxTokens === undefined &&
maxTokens === undefined &&
generation?.temperature === undefined &&
generation?.topP === undefined &&
(generation?.stop === undefined || generation.stop.length === 0)
)
return undefined
return {
maxTokens: generation?.maxTokens,
maxTokens,
temperature: generation?.temperature,
topP: generation?.topP,
stopSequences: generation?.stop,
@@ -225,6 +225,25 @@ describe("Bedrock Converse route", () => {
}),
)
it.effect("omits maxTokens only for Nova 2 at high reasoning effort", () =>
Effect.gen(function* () {
const inferenceConfig = (modelID: string, maxReasoningEffort: string) =>
compileRequest(
LLMRequest.update(baseRequest, {
model: AmazonBedrock.model(modelID, {
baseURL: "https://bedrock-runtime.test",
apiKey: "test-bearer",
body: { additionalModelRequestFields: { reasoningConfig: { type: "enabled", maxReasoningEffort } } },
}),
}),
).pipe(Effect.map((prepared) => prepared.body.inferenceConfig))
expect(yield* inferenceConfig("us.amazon.nova-2-lite-v1:0", "high")).toEqual({ temperature: 0 })
expect(yield* inferenceConfig("us.amazon.nova-2-lite-v1:0", "low")).toEqual({ maxTokens: 64, temperature: 0 })
expect(yield* inferenceConfig("us.xai.grok-4.6", "high")).toEqual({ maxTokens: 64, temperature: 0 })
}),
)
it.effect("omits additionalModelRequestFields when topK is unset", () =>
Effect.gen(function* () {
const prepared = yield* compileRequest(baseRequest)