From 5acb0d4d85ffa0b4b41082cd83a3e1001192ecca Mon Sep 17 00:00:00 2001 From: Hare Date: Tue, 5 May 2026 12:30:29 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20Reasoning=E3=81=AE=E6=B0=B8=E7=B6=9A?= =?UTF-8?q?=E5=8C=96=E3=81=AE=E3=82=B9=E3=82=AD=E3=83=BC=E3=83=9E=E3=81=AE?= =?UTF-8?q?=E3=83=9F=E3=82=B9=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scheme/openai_responses/request.rs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/llm-worker/src/llm_client/scheme/openai_responses/request.rs b/crates/llm-worker/src/llm_client/scheme/openai_responses/request.rs index e66a3f0c..51b38ae1 100644 --- a/crates/llm-worker/src/llm_client/scheme/openai_responses/request.rs +++ b/crates/llm-worker/src/llm_client/scheme/openai_responses/request.rs @@ -100,7 +100,11 @@ pub(crate) enum InputItem { Reasoning { #[serde(skip_serializing_if = "Option::is_none")] id: Option, - #[serde(skip_serializing_if = "Vec::is_empty")] + /// Responses API は reasoning item に `summary` フィールドを必須で + /// 要求する(中身が空でも `[]` として送る必要がある)。GPT-5 など + /// summary を返さないモデル + reasoning effort 指定なしのターンでは + /// summary text が一切付かないので、ここを skip すると 400 + /// "Missing required parameter: 'input[N].summary'" で弾かれる。 summary: Vec, #[serde(skip_serializing_if = "Vec::is_empty")] content: Vec, @@ -473,6 +477,29 @@ mod tests { } } + #[test] + fn reasoning_summary_field_is_always_serialized() { + // Responses API は reasoning item に `summary` を必須で要求する。 + // summary が空でも wire 上に `summary: []` として残らないと、 + // ChatGPT backend (codex-oauth) が + // 400 invalid_request_error: Missing required parameter: + // 'input[N].summary'. + // で弾く。GPT-5 + reasoning effort 未指定のターンでは summary text + // が付かないことがあるため、空のままでも skip しないこと。 + let scheme = OpenAIResponsesScheme::new(); + let item = Item::reasoning("").with_encrypted_content("ENC"); + let req = Request::new().user("hi").item(item); + let body = scheme.build_request("gpt-5", &req, &cap_with_reasoning()); + let json = serde_json::to_value(&body).unwrap(); + let reasoning_item = &json["input"][1]; + assert_eq!(reasoning_item["type"], "reasoning"); + assert!( + reasoning_item.get("summary").is_some(), + "summary key must be present even when empty, got: {reasoning_item}" + ); + assert_eq!(reasoning_item["summary"], serde_json::json!([])); + } + #[test] fn reasoning_effort_projected_when_supported() { let scheme = OpenAIResponsesScheme::new();