try something 3

This commit is contained in:
jif-oai
2025-11-12 14:06:04 +00:00
parent 3d58659451
commit c3480d94a1
4 changed files with 191 additions and 44 deletions

View File

@@ -1,10 +1,14 @@
use async_trait::async_trait;
use codex_otel::otel_event_manager::OtelEventManager;
use codex_protocol::models::ContentItem;
use codex_protocol::models::ReasoningItemContent;
use codex_protocol::models::ResponseItem;
use serde_json::Value;
use tokio::sync::mpsc;
use tracing::debug;
use crate::client::WireResponseDecoder;
use crate::error::Error;
use crate::error::Result;
use crate::stream::WireEvent;
@@ -16,15 +20,19 @@ struct FunctionCallState {
arguments: String,
}
#[derive(Default)]
pub struct WireChatSseDecoder {
fn_call_state: FunctionCallState,
created_emitted: bool,
assistant_started: bool,
assistant_text: String,
reasoning_started: bool,
reasoning_text: String,
}
impl WireChatSseDecoder {
pub fn new() -> Self {
Self {
fn_call_state: FunctionCallState::default(),
}
Self::default()
}
}
@@ -36,11 +44,11 @@ impl WireResponseDecoder for WireChatSseDecoder {
tx: &mpsc::Sender<crate::error::Result<WireEvent>>,
_otel: &OtelEventManager,
) -> Result<()> {
// Chat sends a terminal "[DONE]" frame; ignore it.
let Ok(parsed_chunk) = serde_json::from_str::<Value>(json) else {
// Chat sends a terminal "[DONE]" frame; ignore it. Treat other parse errors as failures.
let parsed_chunk = serde_json::from_str::<Value>(json).map_err(|err| {
debug!("failed to parse Chat SSE JSON: {}", json);
return Ok(());
};
Error::Other(format!("failed to parse Chat SSE JSON: {err}"))
})?;
let choices = parsed_chunk
.get("choices")
@@ -49,10 +57,29 @@ impl WireResponseDecoder for WireChatSseDecoder {
.unwrap_or_default();
for choice in choices {
if !self.created_emitted {
let _ = tx.send(Ok(WireEvent::Created)).await;
self.created_emitted = true;
}
if let Some(delta) = choice.get("delta") {
if let Some(content) = delta.get("content").and_then(|c| c.as_array()) {
for piece in content {
if let Some(text) = piece.get("text").and_then(|t| t.as_str()) {
if !self.assistant_started {
self.assistant_started = true;
let message = ResponseItem::Message {
id: None,
role: "assistant".to_string(),
content: vec![ContentItem::OutputText {
text: String::new(),
}],
};
let value = serde_json::to_value(message)
.unwrap_or_else(|_| Value::String(String::new()));
let _ = tx.send(Ok(WireEvent::OutputItemAdded(value))).await;
}
self.assistant_text.push_str(text);
let _ = tx
.send(Ok(WireEvent::OutputTextDelta(text.to_string())))
.await;
@@ -80,6 +107,19 @@ impl WireResponseDecoder for WireChatSseDecoder {
if let Some(reasoning) = delta.get("reasoning_content").and_then(|c| c.as_array()) {
for entry in reasoning {
if let Some(text) = entry.get("text").and_then(|t| t.as_str()) {
if !self.reasoning_started {
self.reasoning_started = true;
let reasoning_item = ResponseItem::Reasoning {
id: String::new(),
summary: vec![],
content: None,
encrypted_content: None,
};
let value = serde_json::to_value(reasoning_item)
.unwrap_or_else(|_| Value::String(String::new()));
let _ = tx.send(Ok(WireEvent::OutputItemAdded(value))).await;
}
self.reasoning_text.push_str(text);
let _ = tx
.send(Ok(WireEvent::ReasoningContentDelta(text.to_string())))
.await;
@@ -88,23 +128,69 @@ impl WireResponseDecoder for WireChatSseDecoder {
}
}
if let Some(finish_reason) = choice.get("finish_reason").and_then(|f| f.as_str())
&& finish_reason == "tool_calls"
&& self.fn_call_state.active
{
let function_name = self.fn_call_state.name.take().unwrap_or_default();
let call_id = self.fn_call_state.call_id.take().unwrap_or_default();
let arguments = self.fn_call_state.arguments.clone();
self.fn_call_state = FunctionCallState::default();
if let Some(finish_reason) = choice.get("finish_reason").and_then(|f| f.as_str()) {
match finish_reason {
"tool_calls" if self.fn_call_state.active => {
let function_name = self.fn_call_state.name.take().unwrap_or_default();
let call_id = self.fn_call_state.call_id.take().unwrap_or_default();
let arguments = self.fn_call_state.arguments.clone();
self.fn_call_state = FunctionCallState::default();
let item = serde_json::json!({
"type": "function_call",
"id": call_id,
"call_id": call_id,
"name": function_name,
"arguments": arguments,
});
let _ = tx.send(Ok(WireEvent::OutputItemDone(item))).await;
let item = serde_json::json!({
"type": "function_call",
"id": call_id,
"call_id": call_id,
"name": function_name,
"arguments": arguments,
});
let _ = tx.send(Ok(WireEvent::OutputItemDone(item))).await;
}
"stop" | "length" => {
if self.reasoning_started {
let mut content = Vec::new();
if !self.reasoning_text.is_empty() {
content.push(ReasoningItemContent::ReasoningText {
text: self.reasoning_text.clone(),
});
}
let reasoning_item = ResponseItem::Reasoning {
id: String::new(),
summary: vec![],
content: Some(content),
encrypted_content: None,
};
let value = serde_json::to_value(reasoning_item)
.unwrap_or_else(|_| Value::String(String::new()));
let _ = tx.send(Ok(WireEvent::OutputItemDone(value))).await;
}
if self.assistant_started {
let message = ResponseItem::Message {
id: None,
role: "assistant".to_string(),
content: vec![ContentItem::OutputText {
text: self.assistant_text.clone(),
}],
};
let value = serde_json::to_value(message)
.unwrap_or_else(|_| Value::String(String::new()));
let _ = tx.send(Ok(WireEvent::OutputItemDone(value))).await;
}
let _ = tx
.send(Ok(WireEvent::Completed {
response_id: String::new(),
token_usage: None,
}))
.await;
self.assistant_started = false;
self.assistant_text.clear();
self.reasoning_started = false;
self.reasoning_text.clear();
}
_ => {}
}
}
}