diff --git a/crates/language_models/src/provider/copilot_chat.rs b/crates/language_models/src/provider/copilot_chat.rs index bfac215b6c1..f42fad657c4 100644 --- a/crates/language_models/src/provider/copilot_chat.rs +++ b/crates/language_models/src/provider/copilot_chat.rs @@ -773,7 +773,6 @@ impl CopilotResponsesEventMapper { copilot_responses::StreamEvent::Completed { response } => { let mut events = Vec::new(); - events.extend(self.capture_reasoning_items_from_output(&response.output)); if let Some(usage) = response.usage { events.push(Ok(LanguageModelCompletionEvent::UsageUpdate(TokenUsage { input_tokens: usage.input_tokens.unwrap_or(0), @@ -805,7 +804,6 @@ impl CopilotResponsesEventMapper { }; let mut events = Vec::new(); - events.extend(self.capture_reasoning_items_from_output(&response.output)); if let Some(usage) = response.usage { events.push(Ok(LanguageModelCompletionEvent::UsageUpdate(TokenUsage { input_tokens: usage.input_tokens.unwrap_or(0), @@ -847,28 +845,6 @@ impl CopilotResponsesEventMapper { } } - fn capture_reasoning_items_from_output( - &mut self, - output: &[copilot_responses::ResponseOutputItem], - ) -> Vec> { - let mut events = Vec::new(); - for item in output { - if let copilot_responses::ResponseOutputItem::Reasoning { - id, - summary: _, - encrypted_content, - } = item - { - if let Some(reasoning_item) = - reasoning_input_item_from_output(&id, encrypted_content.clone()) - { - events.extend(self.capture_reasoning_item(reasoning_item)); - } - } - } - events - } - fn capture_reasoning_item( &mut self, reasoning_item: copilot_responses::ResponseReasoningInputItem, @@ -1597,6 +1573,60 @@ mod tests { } } + #[test] + fn responses_stream_ignores_reasoning_items_repeated_in_completed_output() { + let events = vec![ + responses::StreamEvent::OutputItemDone { + output_index: 0, + sequence_number: None, + item: responses::ResponseOutputItem::Reasoning { + id: "r1".into(), + summary: Some(Vec::new()), + encrypted_content: Some("ENC1".into()), + }, + }, + responses::StreamEvent::Completed { + response: responses::Response { + output: vec![ + responses::ResponseOutputItem::Reasoning { + id: "r1".into(), + summary: Some(Vec::new()), + encrypted_content: Some("ENC1".into()), + }, + responses::ResponseOutputItem::Reasoning { + id: "r2".into(), + summary: Some(Vec::new()), + encrypted_content: Some("ENC2".into()), + }, + ], + ..Default::default() + }, + }, + ]; + + let mapped = map_events(events); + let reasoning_details = mapped + .iter() + .filter_map(|event| match event { + LanguageModelCompletionEvent::ReasoningDetails(details) => Some(details), + _ => None, + }) + .collect::>(); + + assert_eq!( + reasoning_details, + vec![&json!({ + "reasoning_items": [ + { + "id": "r1", + "summary": [], + "encrypted_content": "ENC1" + } + ] + })] + ); + } + #[test] fn into_copilot_responses_replays_reasoning_details() { let model = test_responses_model();