Skip to main content

gproxy_protocol/adapt/generate/stream/event/
responses.rs

1use super::*;
2use crate::{
3    transform::generate::stream::responses::{ResponsesStreamCollector, ResponsesStreamLimits},
4    wire::openai::responses::{self as r, stream as s},
5};
6
7impl sealed::Event for s::StreamEvent {}
8impl NativeEvent for s::StreamEvent {
9    type Full = r::GenerateContentResponseBody;
10    type Collector = ResponsesStreamCollector;
11    fn responses_history(value: &Self::Full) -> Option<&r::GenerateContentResponseBody> {
12        Some(value)
13    }
14    const DIALECT: Dialect = Dialect::OpenAi;
15    fn collector(_: IdentityFlow, _: TargetIdPolicy, limits: EventLimits) -> Self::Collector {
16        ResponsesStreamCollector::new(ResponsesStreamLimits {
17            max_bytes: limits.max_bytes,
18
19            max_text_bytes: limits.max_bytes,
20            max_json_bytes: limits.max_bytes,
21        })
22    }
23    fn collect(collector: &mut Self::Collector, event: Self) -> Result<(), TransformError> {
24        collector.push(event)
25    }
26    fn collected(
27        collector: Self::Collector,
28    ) -> Result<Converted<Collected<Self::Full>>, TransformError> {
29        let value = collector.finish()?;
30        Ok(Converted {
31            value: Collected::native(value.value),
32            report: value.report,
33        })
34    }
35    fn event_name(&self) -> Option<&'static str> {
36        Some(match self {
37            Self::Unknown => return None,
38            Self::Keepalive => "keepalive",
39            Self::Metadata(_) => "response.metadata",
40            Self::Created(_) => "response.created",
41            Self::Queued(_) => "response.queued",
42            Self::InProgress(_) => "response.in_progress",
43            Self::Completed(_) => "response.completed",
44            Self::Failed(_) => "response.failed",
45            Self::Incomplete(_) => "response.incomplete",
46            Self::OutputItemAdded(_) => "response.output_item.added",
47            Self::OutputItemDone(_) => "response.output_item.done",
48            Self::ContentPartAdded(_) => "response.content_part.added",
49            Self::ContentPartDone(_) => "response.content_part.done",
50            Self::OutputTextDelta(_) => "response.output_text.delta",
51            Self::OutputTextDone(_) => "response.output_text.done",
52            Self::OutputTextAnnotationAdded(_) => "response.output_text.annotation.added",
53            Self::RefusalDelta(_) => "response.refusal.delta",
54            Self::RefusalDone(_) => "response.refusal.done",
55            Self::ReasoningTextDelta(_) => "response.reasoning_text.delta",
56            Self::ReasoningTextDone(_) => "response.reasoning_text.done",
57            Self::ReasoningSummaryPartAdded(_) => "response.reasoning_summary_part.added",
58            Self::ReasoningSummaryPartDone(_) => "response.reasoning_summary_part.done",
59            Self::ReasoningSummaryTextDelta(_) => "response.reasoning_summary_text.delta",
60            Self::ReasoningSummaryTextDone(_) => "response.reasoning_summary_text.done",
61            Self::FunctionCallArgumentsDelta(_) => "response.function_call_arguments.delta",
62            Self::FunctionCallArgumentsDone(_) => "response.function_call_arguments.done",
63            Self::CustomToolInputDelta(_) => "response.custom_tool_call_input.delta",
64            Self::CustomToolInputDone(_) => "response.custom_tool_call_input.done",
65            Self::CodeInterpreterCodeDelta(_) => "response.code_interpreter_call_code.delta",
66            Self::CodeInterpreterCodeDone(_) => "response.code_interpreter_call_code.done",
67            Self::AudioDelta(_) => "response.audio.delta",
68            Self::AudioDone(_) => "response.audio.done",
69            Self::AudioTranscriptDelta(_) => "response.audio.transcript.delta",
70            Self::AudioTranscriptDone(_) => "response.audio.transcript.done",
71            Self::ImagePartial(_) => "response.image_generation_call.partial_image",
72            Self::ImageCall(_) => "response.image_generation_call.in_progress",
73            Self::ImageGenerating(_) => "response.image_generation_call.generating",
74            Self::ImageCompleted(_) => "response.image_generation_call.completed",
75            Self::CodeInterpreterInProgress(_) => "response.code_interpreter_call.in_progress",
76            Self::CodeInterpreterInterpreting(_) => "response.code_interpreter_call.interpreting",
77            Self::CodeInterpreterCompleted(_) => "response.code_interpreter_call.completed",
78            Self::FileSearchInProgress(_) => "response.file_search_call.in_progress",
79            Self::FileSearchSearching(_) => "response.file_search_call.searching",
80            Self::FileSearchCompleted(_) => "response.file_search_call.completed",
81            Self::WebSearchInProgress(_) => "response.web_search_call.in_progress",
82            Self::WebSearchSearching(_) => "response.web_search_call.searching",
83            Self::WebSearchCompleted(_) => "response.web_search_call.completed",
84            Self::McpArgumentsDelta(_) => "response.mcp_call_arguments.delta",
85            Self::McpArgumentsDone(_) => "response.mcp_call_arguments.done",
86            Self::McpInProgress(_) => "response.mcp_call.in_progress",
87            Self::McpCompleted(_) => "response.mcp_call.completed",
88            Self::McpFailed(_) => "response.mcp_call.failed",
89            Self::McpListToolsInProgress(_) => "response.mcp_list_tools.in_progress",
90            Self::McpListToolsCompleted(_) => "response.mcp_list_tools.completed",
91            Self::McpListToolsFailed(_) => "response.mcp_list_tools.failed",
92            Self::Error(_) => "error",
93        })
94    }
95    fn is_terminal(&self) -> bool {
96        matches!(
97            self,
98            Self::Completed(_) | Self::Incomplete(_) | Self::Failed(_) | Self::Error(_)
99        )
100    }
101    fn tool_declarations(
102        &self,
103        _complete_tool_names: bool,
104    ) -> Vec<(String, ToolCallKind, Option<String>)> {
105        let items: Vec<&r::ResponseOutputItem> = match self {
106            Self::Created(v) => v.response.output.iter().collect(),
107            Self::Queued(v) => v.response.output.iter().collect(),
108            Self::InProgress(v) => v.response.output.iter().collect(),
109            Self::Completed(v) => v.response.output.iter().collect(),
110            Self::Incomplete(v) => v.response.output.iter().collect(),
111            Self::OutputItemAdded(v) | Self::OutputItemDone(v) => vec![&v.item],
112            _ => Vec::new(),
113        };
114        items
115            .into_iter()
116            .filter_map(|item| match item {
117                r::ResponseOutputItem::FunctionCall(v) => Some((
118                    v.call_id.clone(),
119                    ToolCallKind::Function,
120                    (!v.name.is_empty()).then(|| {
121                        v.namespace.as_ref().map_or_else(
122                            || v.name.clone(),
123                            |ns| crate::transform::generate::client_tools::qualified(ns, &v.name),
124                        )
125                    }),
126                )),
127                r::ResponseOutputItem::ShellCall(v) => Some((
128                    v.call_id.clone(),
129                    ToolCallKind::Function,
130                    Some(crate::transform::generate::client_tools::SHELL.into()),
131                )),
132                r::ResponseOutputItem::ApplyPatchCall(v) => Some((
133                    v.call_id.clone(),
134                    ToolCallKind::Function,
135                    Some(crate::transform::generate::client_tools::PATCH.into()),
136                )),
137                r::ResponseOutputItem::ToolSearchCall(v)
138                    if v.execution == crate::wire::openai::responses::ToolExecution::Client =>
139                {
140                    v.call_id.clone().map(|id| {
141                        (
142                            id,
143                            ToolCallKind::Function,
144                            Some(crate::transform::generate::client_tools::SEARCH.into()),
145                        )
146                    })
147                }
148                r::ResponseOutputItem::CustomToolCall(v) => Some((
149                    v.call_id.clone(),
150                    ToolCallKind::Custom,
151                    (!v.name.is_empty()).then(|| v.name.clone()),
152                )),
153                _ => None,
154            })
155            .collect()
156    }
157}