refactor: build structured-output GenerateParams via struct update

Replaces the per-field if-let cascade in the structured completion path
with a single struct-update expression. The cascade had to be extended
by hand for every request field and silently dropped stop_sequences and
provider_options, which the non-structured path already forwarded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-07-24 08:40:55 -04:00
parent 9c403108b4
commit 0cd22ebd75
No known key found for this signature in database
2 changed files with 18 additions and 20 deletions

View file

@ -139,25 +139,23 @@ async fn create_completion(
let msg_id = Ulid::new().to_string(); let msg_id = Ulid::new().to_string();
if let Some(schema) = req.schema { if let Some(schema) = req.schema {
// Structured output uses generate_object for JSON parsing logic // Structured output uses generate_object for JSON parsing logic.
let mut params = // tools/tool_choice are not forwarded: GenerateParams carries
GenerateParams::new(&request.model, std::sync::Arc::new(client.clone())) // executable Arc<Tool>s, not wire ToolDefinitions, and
.messages(request.messages); // generate_object sets response_format from the schema itself.
if let Some(ref p) = request.provider { let params = GenerateParams {
params = params.provider(p); messages: Some(request.messages),
} provider: request.provider,
if let Some(temp) = request.temperature { temperature: request.temperature,
params = params.temperature(temp); top_p: request.top_p,
} max_tokens: request.max_tokens,
if let Some(max_tokens) = request.max_tokens { stop_sequences: request.stop_sequences,
params = params.max_tokens(max_tokens); reasoning_effort: request.reasoning_effort,
} speed: request.speed,
if let Some(top_p) = request.top_p { metadata: request.metadata,
params = params.top_p(top_p); provider_options: request.provider_options,
} ..GenerateParams::new(request.model, std::sync::Arc::new(client.clone()))
if let Some(reasoning_effort) = request.reasoning_effort { };
params = params.reasoning_effort(reasoning_effort);
}
match generate_object(params, schema).await { match generate_object(params, schema).await {
Ok(result) => { Ok(result) => {
// `result.finish_reason` / `result.usage` resolve through // `result.finish_reason` / `result.usage` resolve through

View file

@ -15431,7 +15431,7 @@ async fn create_completion_structured_output_forwards_reasoning_effort() {
let response = app.oneshot(req).await.unwrap(); let response = app.oneshot(req).await.unwrap();
let body = response_json!(response, StatusCode::OK).await; let body = response_json!(response, StatusCode::OK).await;
assert_eq!(body["output"], json!({"answer": 42})); assert_eq!(body["output"], json!({"answer": 42}));
completion.assert_calls(1); completion.assert();
} }
#[tokio::test] #[tokio::test]