Skip to content

Commit eb044f0

Browse files
committed
fix(api): emit max_completion_tokens for gpt-5* on OpenAI-compat path — closes ROADMAP #35
gpt-5.x models reject requests with max_tokens and require max_completion_tokens. Detect wire model starting with 'gpt-5' and switch the JSON key accordingly. Older models (gpt-4o etc.) continue to receive max_tokens unchanged. Two regression tests added: - gpt5_uses_max_completion_tokens_not_max_tokens - non_gpt5_uses_max_tokens 140 api tests pass, cargo fmt clean.
1 parent 75476c9 commit eb044f0

1 file changed

Lines changed: 51 additions & 1 deletion

File tree

rust/crates/api/src/providers/openai_compat.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,9 +759,18 @@ fn build_chat_completion_request(request: &MessageRequest, config: OpenAiCompatC
759759
// Strip routing prefix (e.g., "openai/gpt-4" → "gpt-4") for the wire.
760760
let wire_model = strip_routing_prefix(&request.model);
761761

762+
// gpt-5* requires `max_completion_tokens`; older OpenAI models accept both.
763+
// We send the correct field based on the wire model name so gpt-5.x requests
764+
// don't fail with "unknown field max_tokens".
765+
let max_tokens_key = if wire_model.starts_with("gpt-5") {
766+
"max_completion_tokens"
767+
} else {
768+
"max_tokens"
769+
};
770+
762771
let mut payload = json!({
763772
"model": wire_model,
764-
"max_tokens": request.max_tokens,
773+
max_tokens_key: request.max_tokens,
765774
"messages": messages,
766775
"stream": request.stream,
767776
});
@@ -1451,4 +1460,45 @@ mod tests {
14511460
assert!(payload.get("presence_penalty").is_none());
14521461
assert!(payload.get("stop").is_none());
14531462
}
1463+
1464+
#[test]
1465+
fn gpt5_uses_max_completion_tokens_not_max_tokens() {
1466+
// gpt-5* models require `max_completion_tokens`; legacy `max_tokens` causes
1467+
// a request-validation failure. Verify the correct key is emitted.
1468+
let request = MessageRequest {
1469+
model: "gpt-5.2".to_string(),
1470+
max_tokens: 512,
1471+
messages: vec![],
1472+
stream: false,
1473+
..Default::default()
1474+
};
1475+
let payload = build_chat_completion_request(&request, OpenAiCompatConfig::openai());
1476+
assert_eq!(
1477+
payload["max_completion_tokens"],
1478+
json!(512),
1479+
"gpt-5.2 should emit max_completion_tokens"
1480+
);
1481+
assert!(
1482+
payload.get("max_tokens").is_none(),
1483+
"gpt-5.2 must not emit max_tokens"
1484+
);
1485+
}
1486+
1487+
#[test]
1488+
fn non_gpt5_uses_max_tokens() {
1489+
// Older OpenAI models expect `max_tokens`; verify gpt-4o is unaffected.
1490+
let request = MessageRequest {
1491+
model: "gpt-4o".to_string(),
1492+
max_tokens: 512,
1493+
messages: vec![],
1494+
stream: false,
1495+
..Default::default()
1496+
};
1497+
let payload = build_chat_completion_request(&request, OpenAiCompatConfig::openai());
1498+
assert_eq!(payload["max_tokens"], json!(512));
1499+
assert!(
1500+
payload.get("max_completion_tokens").is_none(),
1501+
"gpt-4o must not emit max_completion_tokens"
1502+
);
1503+
}
14541504
}

0 commit comments

Comments
 (0)