fix: harden concurrency limits and high-RPM runtime paths

Bound request, stream, queue, and shutdown resource lifetimes. Reduce scheduler and Redis hot-path work and isolate database maintenance. Include regression coverage, load probes, and concurrency audit results.
This commit is contained in:
elky
2026-09-10 08:14:58 +08:00
parent 361952ada9
commit ecc16673eb
149 changed files with 27963 additions and 1926 deletions
@@ -24,6 +24,8 @@ struct GeminiProviderToolResultState {
#[derive(Default)]
pub struct GeminiProviderState {
terminal_observation_only: bool,
observed_tool_calls: bool,
response_id: Option<String>,
model: Option<String>,
started: bool,
@@ -37,6 +39,13 @@ pub struct GeminiProviderState {
}
impl GeminiProviderState {
pub(crate) fn terminal_observation() -> Self {
Self {
terminal_observation_only: true,
..Self::default()
}
}
fn identity(&self, report_context: &Value) -> (String, String) {
resolve_identity(
self.response_id.as_deref(),
@@ -133,14 +142,21 @@ impl GeminiProviderState {
let Some(part_object) = part.as_object() else {
continue;
};
let reasoning_signature = part_object
.get("thoughtSignature")
.or_else(|| part_object.get("thought_signature"))
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned);
let reasoning_signature = if self.terminal_observation_only {
None
} else {
part_object
.get("thoughtSignature")
.or_else(|| part_object.get("thought_signature"))
.and_then(Value::as_str)
.map(str::trim)
.filter(|value| !value.is_empty())
.map(ToOwned::to_owned)
};
if let Some(text) = render_gemini_part_as_text(part_object) {
if self.terminal_observation_only {
continue;
}
let is_reasoning = part_object
.get("thought")
.and_then(Value::as_bool)
@@ -193,6 +209,9 @@ impl GeminiProviderState {
.or_else(|| part_object.get("function_response"))
.and_then(Value::as_object)
{
if self.terminal_observation_only {
continue;
}
let tool_use_id = function_response
.get("id")
.and_then(Value::as_str)
@@ -240,6 +259,9 @@ impl GeminiProviderState {
else {
if let Some(content_part) = canonical_content_part_from_gemini_part(part_object)
{
if self.terminal_observation_only {
continue;
}
let should_emit = self
.content_parts
.get(&index)
@@ -260,6 +282,10 @@ impl GeminiProviderState {
}
continue;
};
if self.terminal_observation_only {
self.observed_tool_calls = true;
continue;
}
let tool_state = self.tool_calls.entry(index).or_default();
tool_state.call_id = function_call
.get("id")
@@ -329,7 +355,7 @@ impl GeminiProviderState {
if let Some(finish_reason) =
candidate_object.get("finishReason").and_then(Value::as_str)
{
let has_tool_calls = !self.tool_calls.is_empty();
let has_tool_calls = self.observed_tool_calls || !self.tool_calls.is_empty();
let mut finish_reason =
normalize_openai_finish_reason(map_gemini_stream_finish_reason(finish_reason));
if has_tool_calls && finish_reason.as_deref().is_none_or(|value| value == "stop") {
@@ -936,6 +962,232 @@ mod tests {
format!("data: {}\n", value).into_bytes()
}
fn terminal_frames(frames: Vec<CanonicalStreamFrame>) -> Vec<CanonicalStreamFrame> {
frames
.into_iter()
.filter(|frame| {
matches!(
frame.event,
CanonicalStreamEvent::Start
| CanonicalStreamEvent::Finish { .. }
| CanonicalStreamEvent::UnknownEvent(_)
)
})
.collect()
}
fn observation_record(parts: Vec<Value>, finish_reason: Option<&str>) -> Value {
let mut record = json!({
"responseId": "resp_observation",
"modelVersion": "gemini-2.5-pro",
"candidates": [{"content": {"parts": parts}}],
"usageMetadata": {
"promptTokenCount": 22,
"cachedContentTokenCount": 7,
"candidatesTokenCount": 13,
"thoughtsTokenCount": 5,
"totalTokenCount": 40
}
});
if let Some(finish_reason) = finish_reason {
record["candidates"][0]["finishReason"] = json!(finish_reason);
}
record
}
fn assert_terminal_record_matches(
normal: &mut GeminiProviderState,
observer: &mut GeminiProviderState,
record: Value,
) -> Vec<CanonicalStreamFrame> {
let context = json!({"mapped_model": "fallback-model"});
let expected = terminal_frames(
normal
.push_line(&context, data_line(record.clone()))
.expect("normal provider parser"),
);
let actual = observer
.push_line(&context, data_line(record))
.expect("terminal provider parser");
assert_eq!(
actual, expected,
"terminal frames must retain their full payloads"
);
actual
}
fn assert_observer_has_no_content_buffers(observer: &GeminiProviderState) {
assert!(observer.text_parts.is_empty());
assert!(observer.reasoning_parts.is_empty());
assert!(observer.reasoning_signatures.is_empty());
assert!(observer.content_parts.is_empty());
assert!(observer.tool_calls.is_empty());
assert!(observer.tool_results.is_empty());
}
#[test]
fn gemini_terminal_observation_does_not_retain_long_stream_content() {
let mut normal = GeminiProviderState::default();
let mut observer = GeminiProviderState::terminal_observation();
let media = "YQ==".repeat(4_096);
for index in 0..32 {
let text = format!("record-{index}:{}", "x".repeat(16_384));
assert_terminal_record_matches(
&mut normal,
&mut observer,
observation_record(
vec![
json!({"text": text}),
json!({"text": text, "thought": true, "thoughtSignature": media}),
json!({"functionCall": {"id": format!("call-{index}"), "name": "lookup", "args": {"value": text}}}),
json!({"functionResponse": {"name": "lookup", "response": {"result": text}}}),
json!({"inlineData": {"mimeType": "image/png", "data": media}}),
json!({"inline_data": {"mime_type": "audio/wav", "data": media}}),
json!({"inlineData": {"mimeType": "application/pdf", "data": media}}),
],
None,
),
);
assert_observer_has_no_content_buffers(&observer);
assert!(observer.observed_tool_calls);
}
assert!(!normal.text_parts.is_empty());
assert!(!normal.reasoning_parts.is_empty());
assert!(!normal.reasoning_signatures.is_empty());
assert!(!normal.content_parts.is_empty());
assert!(!normal.tool_calls.is_empty());
assert!(normal
.tool_results
.values()
.any(|state| state.content.len() > 16_384));
let frames = assert_terminal_record_matches(
&mut normal,
&mut observer,
observation_record(Vec::new(), Some("STOP")),
);
assert!(frames.iter().any(|frame| matches!(
&frame.event,
CanonicalStreamEvent::Finish { finish_reason: Some(reason), usage: Some(usage) }
if reason == "tool_calls" && usage.input_tokens == 22 && usage.output_tokens == 18
&& usage.cache_read_tokens == 7 && usage.reasoning_tokens == 5 && usage.total_tokens == 40
)));
assert_observer_has_no_content_buffers(&observer);
}
#[test]
fn gemini_terminal_observation_matches_known_and_unknown_part_classification() {
let parts = vec![
Value::Null,
json!({}),
json!({"text": ""}),
json!({"text": 17}),
json!({"text": "", "thought_signature": "signature"}),
json!({"thoughtSignature": "signature"}),
json!({"executableCode": {"language": "python", "code": "print(1)"}}),
json!({"codeExecutionResult": {"output": "1"}}),
json!({"functionResponse": {}}),
json!({"function_response": {"response": ["result"]}}),
json!({"functionCall": {}}),
json!({"functionCall": null}),
json!({"inlineData": {"mimeType": "image/png", "data": "YQ=="}}),
json!({"inline_data": {"mime_type": "audio/wav", "data": "YQ=="}}),
json!({"inlineData": {"mimeType": "application/pdf", "data": "YQ=="}}),
json!({"inlineData": {"mimeType": "", "data": "YQ=="}}),
json!({"inlineData": {"mimeType": "image/png", "data": ""}}),
json!({"file_data": {"file_uri": "gs://test/file", "mime_type": "application/pdf"}}),
json!({"fileData": {"fileUri": ""}}),
json!({"futurePart": {"kept": true}}),
];
for part in parts {
let mut normal = GeminiProviderState::default();
let mut observer = GeminiProviderState::terminal_observation();
assert_terminal_record_matches(
&mut normal,
&mut observer,
json!({"responseId": "outer", "response": observation_record(vec![part], Some("STOP"))}),
);
assert_eq!(observer.response_id.as_deref(), Some("resp_observation"));
assert_observer_has_no_content_buffers(&observer);
}
}
#[test]
fn gemini_terminal_observation_preserves_finish_reasons_and_eof() {
for has_tool in [false, true] {
for reason in [
None,
Some("STOP"),
Some("MAX_TOKENS"),
Some("RECITATION"),
Some("FUTURE_REASON"),
] {
let mut normal = GeminiProviderState::default();
let mut observer = GeminiProviderState::terminal_observation();
let part = if has_tool {
json!({"functionCall": {"name": "lookup", "args": {"query": "test"}}})
} else {
json!({"text": "partial output"})
};
assert_terminal_record_matches(
&mut normal,
&mut observer,
observation_record(vec![part], None),
);
if let Some(reason) = reason {
assert_terminal_record_matches(
&mut normal,
&mut observer,
observation_record(Vec::new(), Some(reason)),
);
}
let context = json!({});
assert_eq!(
observer.finish(&context).expect("observer EOF"),
terminal_frames(normal.finish(&context).expect("normal EOF"))
);
assert!(observer
.finish(&context)
.expect("idempotent EOF")
.is_empty());
assert_observer_has_no_content_buffers(&observer);
}
}
}
#[test]
fn gemini_terminal_observation_preserves_failure_payloads_with_usage() {
for reason in [
"MALFORMED_FUNCTION_CALL",
"UNEXPECTED_TOOL_CALL",
"TOO_MANY_TOOL_CALLS",
"MISSING_THOUGHT_SIGNATURE",
"MALFORMED_RESPONSE",
] {
for content in [
Value::Null,
json!({}),
json!({"parts": []}),
json!({"parts": [{"text": ""}]}),
] {
let mut normal = GeminiProviderState::default();
let mut observer = GeminiProviderState::terminal_observation();
let mut record = observation_record(Vec::new(), Some(reason));
record["candidates"][0]["content"] = content;
record["candidates"][0]["finishMessage"] = json!("provider failure details");
let frames = assert_terminal_record_matches(&mut normal, &mut observer, record);
assert!(frames.iter().any(|frame| matches!(
&frame.event,
CanonicalStreamEvent::UnknownEvent(payload)
if payload["response"]["error"]["code"] == reason
&& payload["response"]["error"]["message"] == "provider failure details"
&& payload["response"]["usage"]["input_tokens"] == 22
)));
assert!(observer.finish(&json!({})).expect("failed EOF").is_empty());
assert_observer_has_no_content_buffers(&observer);
}
}
}
#[test]
fn gemini_provider_state_emits_unknown_events_for_unknown_parts() {
let mut state = GeminiProviderState::default();
@@ -1,6 +1,7 @@
use std::collections::{BTreeMap, BTreeSet};
use serde_json::{json, Map, Value};
use sha2::{Digest, Sha256};
use crate::formats::openai::namespace::NamespaceToolAliases;
use crate::formats::openai::responses::{
@@ -33,6 +34,7 @@ struct OpenAIChatProviderToolState {
#[derive(Default)]
pub struct OpenAIChatProviderState {
terminal_only: bool,
response_id: Option<String>,
model: Option<String>,
actual_service_tier: Option<String>,
@@ -59,6 +61,7 @@ struct OpenAIResponsesProviderToolResultState {
#[derive(Default)]
pub struct OpenAIResponsesProviderState {
terminal_only: bool,
response_id: Option<String>,
model: Option<String>,
actual_service_tier: Option<String>,
@@ -71,11 +74,24 @@ pub struct OpenAIResponsesProviderState {
tool_results: BTreeMap<usize, OpenAIResponsesProviderToolResultState>,
tool_index_by_key: BTreeMap<String, usize>,
image_item_keys: BTreeSet<String>,
opaque_completed_item_keys: BTreeSet<String>,
opaque_completed_item_keys: BTreeSet<OpenAIResponsesOutputItemKey>,
last_tool_index: Option<usize>,
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
enum OpenAIResponsesOutputItemKey {
Full(String),
Digest([u8; 32]),
}
impl OpenAIChatProviderState {
pub(crate) fn terminal_observation() -> Self {
Self {
terminal_only: true,
..Self::default()
}
}
pub(crate) fn actual_service_tier(&self) -> Option<&str> {
self.actual_service_tier.as_deref()
}
@@ -243,12 +259,14 @@ impl OpenAIChatProviderState {
recognized_delta = true;
if !content.is_empty() {
self.ensure_started(report_context, &mut out);
let (id, model) = self.identity(report_context);
out.push(CanonicalStreamFrame {
id,
model,
event: CanonicalStreamEvent::TextDelta(content.to_string()),
});
if !self.terminal_only {
let (id, model) = self.identity(report_context);
out.push(CanonicalStreamFrame {
id,
model,
event: CanonicalStreamEvent::TextDelta(content.to_string()),
});
}
}
} else if delta.contains_key("content") {
recognized_delta = true;
@@ -258,12 +276,16 @@ impl OpenAIChatProviderState {
recognized_delta = true;
if !reasoning_content.is_empty() {
self.ensure_started(report_context, &mut out);
let (id, model) = self.identity(report_context);
out.push(CanonicalStreamFrame {
id,
model,
event: CanonicalStreamEvent::ReasoningDelta(reasoning_content.to_string()),
});
if !self.terminal_only {
let (id, model) = self.identity(report_context);
out.push(CanonicalStreamFrame {
id,
model,
event: CanonicalStreamEvent::ReasoningDelta(
reasoning_content.to_string(),
),
});
}
}
} else if delta.contains_key("reasoning_content") {
recognized_delta = true;
@@ -272,68 +294,71 @@ impl OpenAIChatProviderState {
if let Some(tool_calls) = delta.get("tool_calls").and_then(Value::as_array) {
recognized_delta = true;
self.ensure_started(report_context, &mut out);
let (id, model) = self.identity(report_context);
for tool_call in tool_calls {
let Some(tool_call_object) = tool_call.as_object() else {
continue;
};
let index = tool_call_object
.get("index")
.and_then(Value::as_u64)
.map(|value| value as usize)
.unwrap_or(0);
let state = self.tool_calls.entry(index).or_default();
if let Some(call_id) = tool_call_object.get("id").and_then(Value::as_str) {
state.id = Some(call_id.to_string());
}
let mut arguments = None;
if let Some(function) =
tool_call_object.get("function").and_then(Value::as_object)
{
if let Some(name) = function.get("name").and_then(Value::as_str) {
state.name = Some(name.to_string());
if !self.terminal_only {
let (id, model) = self.identity(report_context);
for tool_call in tool_calls {
let Some(tool_call_object) = tool_call.as_object() else {
continue;
};
let index = tool_call_object
.get("index")
.and_then(Value::as_u64)
.map(|value| value as usize)
.unwrap_or(0);
let state = self.tool_calls.entry(index).or_default();
if let Some(call_id) = tool_call_object.get("id").and_then(Value::as_str) {
state.id = Some(call_id.to_string());
}
arguments = function
.get("arguments")
.and_then(Value::as_str)
.filter(|arguments| !arguments.is_empty());
}
if !state.started_emitted {
if let Some(arguments) = arguments {
state.pending_arguments.push_str(arguments);
}
if let (Some(call_id), Some(name)) = (state.id.clone(), state.name.clone())
let mut arguments = None;
if let Some(function) =
tool_call_object.get("function").and_then(Value::as_object)
{
out.push(CanonicalStreamFrame {
id: id.clone(),
model: model.clone(),
event: CanonicalStreamEvent::ToolCallStart {
index,
call_id,
name,
},
});
state.started_emitted = true;
if !state.pending_arguments.is_empty() {
if let Some(name) = function.get("name").and_then(Value::as_str) {
state.name = Some(name.to_string());
}
arguments = function
.get("arguments")
.and_then(Value::as_str)
.filter(|arguments| !arguments.is_empty());
}
if !state.started_emitted {
if let Some(arguments) = arguments {
state.pending_arguments.push_str(arguments);
}
if let (Some(call_id), Some(name)) =
(state.id.clone(), state.name.clone())
{
out.push(CanonicalStreamFrame {
id: id.clone(),
model: model.clone(),
event: CanonicalStreamEvent::ToolCallArgumentsDelta {
event: CanonicalStreamEvent::ToolCallStart {
index,
arguments: std::mem::take(&mut state.pending_arguments),
call_id,
name,
},
});
state.started_emitted = true;
if !state.pending_arguments.is_empty() {
out.push(CanonicalStreamFrame {
id: id.clone(),
model: model.clone(),
event: CanonicalStreamEvent::ToolCallArgumentsDelta {
index,
arguments: std::mem::take(&mut state.pending_arguments),
},
});
}
}
} else if let Some(arguments) = arguments {
out.push(CanonicalStreamFrame {
id: id.clone(),
model: model.clone(),
event: CanonicalStreamEvent::ToolCallArgumentsDelta {
index,
arguments: arguments.to_string(),
},
});
}
} else if let Some(arguments) = arguments {
out.push(CanonicalStreamFrame {
id: id.clone(),
model: model.clone(),
event: CanonicalStreamEvent::ToolCallArgumentsDelta {
index,
arguments: arguments.to_string(),
},
});
}
}
} else if delta.contains_key("tool_calls") {
@@ -389,6 +414,13 @@ impl OpenAIChatProviderState {
}
impl OpenAIResponsesProviderState {
pub(crate) fn terminal_observation() -> Self {
Self {
terminal_only: true,
..Self::default()
}
}
pub(crate) fn actual_service_tier(&self) -> Option<&str> {
self.actual_service_tier.as_deref()
}
@@ -494,6 +526,10 @@ impl OpenAIResponsesProviderState {
if text.is_empty() {
return;
}
if self.terminal_only {
self.ensure_started(report_context, out);
return;
}
self.text_parts.entry(key).or_default().push_str(text);
self.ensure_started(report_context, out);
let (id, model) = self.identity(report_context);
@@ -511,6 +547,12 @@ impl OpenAIResponsesProviderState {
key: String,
text: &str,
) {
if self.terminal_only {
if !text.is_empty() {
self.ensure_started(report_context, out);
}
return;
}
let missing = {
let current = self.text_parts.entry(key).or_default();
let missing = if text.starts_with(current.as_str()) {
@@ -543,6 +585,12 @@ impl OpenAIResponsesProviderState {
out: &mut Vec<CanonicalStreamFrame>,
reasoning: &str,
) {
if self.terminal_only {
if !reasoning.is_empty() {
self.ensure_started(report_context, out);
}
return;
}
let missing = if reasoning.starts_with(&self.reasoning) {
reasoning[self.reasoning.len()..].to_string()
} else if self.reasoning == reasoning {
@@ -573,6 +621,10 @@ impl OpenAIResponsesProviderState {
if text.is_empty() {
return;
}
if self.terminal_only {
self.ensure_started(report_context, out);
return;
}
let missing = {
let current = self.reasoning_parts.entry(summary_index).or_default();
let missing = if text.starts_with(current.as_str()) {
@@ -608,6 +660,9 @@ impl OpenAIResponsesProviderState {
out: &mut Vec<CanonicalStreamFrame>,
index: usize,
) {
if self.terminal_only {
return;
}
let (id, model) = self.identity(report_context);
let Some(state) = self.tool_calls.get_mut(&index) else {
return;
@@ -806,12 +861,13 @@ impl OpenAIResponsesProviderState {
if let Some(name) = incoming_chat_name {
state.name = name;
}
let completed_arguments = item
.get("arguments")
.and_then(Value::as_str)
.unwrap_or_default()
.to_string();
Self::merge_tool_call_arguments(state, &completed_arguments);
if !self.terminal_only {
let completed_arguments = item
.get("arguments")
.and_then(Value::as_str)
.unwrap_or_default();
Self::merge_tool_call_arguments(state, completed_arguments);
}
self.emit_ready_function_call(report_context, out, index);
}
@@ -832,10 +888,14 @@ impl OpenAIResponsesProviderState {
.filter(|value| !value.is_empty())
.unwrap_or("custom_tool")
.to_string();
let arguments = tool_arguments_from_maybe_json_string(
item.get("input").or_else(|| item.get("arguments")),
"input",
);
let arguments = if self.terminal_only {
String::new()
} else {
tool_arguments_from_maybe_json_string(
item.get("input").or_else(|| item.get("arguments")),
"input",
)
};
self.emit_generic_tool_call_item(report_context, out, item, output_index, name, arguments);
}
@@ -852,16 +912,20 @@ impl OpenAIResponsesProviderState {
"shell_call" => "shell",
_ => return,
};
let arguments = tool_arguments_from_named_fields(
item,
&[
"action",
"environment",
"status",
"created_by",
"max_output_length",
],
);
let arguments = if self.terminal_only {
String::new()
} else {
tool_arguments_from_named_fields(
item,
&[
"action",
"environment",
"status",
"created_by",
"max_output_length",
],
)
};
self.emit_generic_tool_call_item(
report_context,
out,
@@ -882,7 +946,11 @@ impl OpenAIResponsesProviderState {
if item.get("type").and_then(Value::as_str) != Some("apply_patch_call") {
return;
}
let arguments = tool_arguments_from_named_fields(item, &["operation", "status"]);
let arguments = if self.terminal_only {
String::new()
} else {
tool_arguments_from_named_fields(item, &["operation", "status"])
};
self.emit_generic_tool_call_item(
report_context,
out,
@@ -903,10 +971,14 @@ impl OpenAIResponsesProviderState {
if item.get("type").and_then(Value::as_str) != Some("computer_call") {
return;
}
let arguments = tool_arguments_from_named_fields(
item,
&["action", "actions", "pending_safety_checks", "status"],
);
let arguments = if self.terminal_only {
String::new()
} else {
tool_arguments_from_named_fields(
item,
&["action", "actions", "pending_safety_checks", "status"],
)
};
self.emit_generic_tool_call_item(
report_context,
out,
@@ -941,10 +1013,20 @@ impl OpenAIResponsesProviderState {
.unwrap_or(state.call_id.as_str())
.to_string();
state.name = name;
Self::merge_tool_call_arguments(state, &arguments);
if !self.terminal_only {
Self::merge_tool_call_arguments(state, &arguments);
}
self.emit_ready_tool_call(report_context, out, index);
}
fn tool_result_content(&self, value: Option<&Value>) -> String {
if self.terminal_only {
String::new()
} else {
openai_tool_result_content_from_value(value)
}
}
fn emit_missing_tool_result(
&mut self,
report_context: &Value,
@@ -955,6 +1037,9 @@ impl OpenAIResponsesProviderState {
content: &str,
) {
self.ensure_started(report_context, out);
if self.terminal_only {
return;
}
let state = self.tool_results.entry(index).or_default();
let missing = if !state.emitted {
content.to_string()
@@ -1005,7 +1090,7 @@ impl OpenAIResponsesProviderState {
Some(format!("function_call_output:{tool_use_id}")),
output_index,
);
let content = openai_tool_result_content_from_value(
let content = self.tool_result_content(
item.get("output")
.or_else(|| item.get("content"))
.or_else(|| item.get("delta")),
@@ -1047,7 +1132,7 @@ impl OpenAIResponsesProviderState {
.to_string();
let index =
self.tool_index_for_key(Some(format!("{item_type}:{tool_use_id}")), output_index);
let content = openai_tool_result_content_from_value(
let content = self.tool_result_content(
item.get("output")
.or_else(|| item.get("content"))
.or_else(|| item.get("delta")),
@@ -1110,6 +1195,24 @@ impl OpenAIResponsesProviderState {
if item.get("type").and_then(Value::as_str) != Some("reasoning") {
return;
}
if self.terminal_only {
if item
.get("summary")
.and_then(Value::as_array)
.is_some_and(|summary| {
summary.iter().any(|part| {
part.get("type").and_then(Value::as_str) == Some("summary_text")
&& part
.get("text")
.and_then(Value::as_str)
.is_some_and(|text| !text.is_empty())
})
})
{
self.ensure_started(report_context, out);
}
return;
}
let mut completed_reasoning = String::new();
for raw_summary in item
.get("summary")
@@ -1159,6 +1262,10 @@ impl OpenAIResponsesProviderState {
if !has_image_payload {
return;
}
if self.terminal_only {
self.ensure_started(report_context, out);
return;
}
let index = output_index.unwrap_or(self.image_item_keys.len());
let key = item
.get("id")
@@ -1180,6 +1287,42 @@ impl OpenAIResponsesProviderState {
});
}
fn retained_output_item_key(&self, item: &Map<String, Value>) -> OpenAIResponsesOutputItemKey {
let item_type = item.get("type").and_then(Value::as_str).unwrap_or_default();
if self.terminal_only {
struct DigestWriter(Sha256);
impl std::io::Write for DigestWriter {
fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
self.0.update(bytes);
Ok(bytes.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
// Hash exactly the normal key's bytes, without retaining or first
// serializing an entire encrypted/opaque output item into a string.
let mut writer = DigestWriter(Sha256::new());
writer.0.update(item_type.as_bytes());
if let Some(item_id) = item.get("id").and_then(Value::as_str) {
writer.0.update(b":id:");
writer.0.update(item_id.as_bytes());
} else if let Some(content) = item.get("encrypted_content").and_then(Value::as_str) {
writer.0.update(b":encrypted_content:");
writer.0.update(content.as_bytes());
} else {
writer.0.update(b":");
serde_json::to_writer(&mut writer, item)
.expect("JSON value serialization into a digest cannot fail");
}
return OpenAIResponsesOutputItemKey::Digest(writer.0.finalize().into());
}
OpenAIResponsesOutputItemKey::Full(Self::output_item_key(item))
}
fn output_item_key(item: &Map<String, Value>) -> String {
let item_type = item.get("type").and_then(Value::as_str).unwrap_or_default();
if let Some(item_id) = item.get("id").and_then(Value::as_str) {
@@ -1290,10 +1433,13 @@ impl OpenAIResponsesProviderState {
}
if final_item {
self.opaque_completed_item_keys
.insert(Self::output_item_key(item));
let key = self.retained_output_item_key(item);
self.opaque_completed_item_keys.insert(key);
}
self.ensure_started(report_context, out);
if self.terminal_only {
return;
}
let (id, model) = self.identity(report_context);
out.push(CanonicalStreamFrame {
id,
@@ -1325,7 +1471,7 @@ impl OpenAIResponsesProviderState {
if !self.emit_output_item(report_context, out, item, Some(output_index), true)
&& !self
.opaque_completed_item_keys
.contains(&Self::output_item_key(item))
.contains(&self.retained_output_item_key(item))
{
out.push(self.unknown_frame(report_context, Value::Object(item.clone())));
}
@@ -1504,6 +1650,9 @@ impl OpenAIResponsesProviderState {
.map(|value| value as usize)
.unwrap_or(0);
self.ensure_started(report_context, &mut out);
if self.terminal_only {
return Ok(out);
}
self.reasoning.push_str(piece);
self.reasoning_parts
.entry(summary_index)
@@ -1543,6 +1692,9 @@ impl OpenAIResponsesProviderState {
);
}
self.ensure_started(report_context, &mut out);
if self.terminal_only {
return Ok(out);
}
let (id, model) = self.identity(report_context);
out.push(CanonicalStreamFrame {
id,
@@ -1595,7 +1747,9 @@ impl OpenAIResponsesProviderState {
.unwrap_or("custom_tool")
.to_string();
}
state.arguments.push_str(delta);
if !self.terminal_only {
state.arguments.push_str(delta);
}
self.emit_ready_tool_call(report_context, &mut out, index);
}
"response.custom_tool_call_input.done" => {
@@ -1623,11 +1777,13 @@ impl OpenAIResponsesProviderState {
.unwrap_or("custom_tool")
.to_string();
}
let arguments = tool_arguments_from_maybe_json_string(
Some(&Value::String(input.to_string())),
"input",
);
Self::merge_tool_call_arguments(state, &arguments);
if !self.terminal_only {
let arguments = tool_arguments_from_maybe_json_string(
Some(&Value::String(input.to_string())),
"input",
);
Self::merge_tool_call_arguments(state, &arguments);
}
self.emit_ready_tool_call(report_context, &mut out, index);
}
"response.function_call_arguments.delta" => {
@@ -1654,7 +1810,9 @@ impl OpenAIResponsesProviderState {
if let Some(call_id) = value.get("call_id").and_then(Value::as_str) {
state.call_id = call_id.to_string();
}
state.arguments.push_str(delta);
if !self.terminal_only {
state.arguments.push_str(delta);
}
self.emit_ready_function_call(report_context, &mut out, index);
}
"response.function_call_arguments.done" => {
@@ -1722,7 +1880,9 @@ impl OpenAIResponsesProviderState {
if let Some(name) = incoming_chat_name {
state.name = name;
}
Self::merge_tool_call_arguments(state, arguments);
if !self.terminal_only {
Self::merge_tool_call_arguments(state, arguments);
}
self.emit_ready_function_call(report_context, &mut out, index);
}
"response.function_call_output.delta" | "response.function_call_output.done" => {
@@ -1743,7 +1903,7 @@ impl OpenAIResponsesProviderState {
Some(format!("function_call_output:{tool_use_id}")),
output_index,
);
let content = openai_tool_result_content_from_value(
let content = self.tool_result_content(
value
.get("delta")
.or_else(|| value.get("output"))
@@ -1781,7 +1941,7 @@ impl OpenAIResponsesProviderState {
.map(|value| value as usize);
let index = self
.tool_index_for_key(Some(format!("{item_type}:{tool_use_id}")), output_index);
let content = openai_tool_result_content_from_value(
let content = self.tool_result_content(
value
.get("delta")
.or_else(|| value.get("output"))
@@ -3754,6 +3914,276 @@ mod tests {
format!("data: {}\n", value).into_bytes()
}
fn terminal_frames(frames: Vec<CanonicalStreamFrame>) -> Vec<CanonicalStreamFrame> {
frames
.into_iter()
.filter(|frame| {
matches!(
frame.event,
CanonicalStreamEvent::Start
| CanonicalStreamEvent::UnknownEvent(_)
| CanonicalStreamEvent::Finish { .. }
)
})
.collect()
}
fn assert_no_responses_observation_content(state: &OpenAIResponsesProviderState) {
assert!(state.text_parts.is_empty());
assert_eq!(state.reasoning.capacity(), 0);
assert!(state.reasoning_parts.is_empty());
assert!(state
.tool_calls
.values()
.all(|tool| tool.arguments.capacity() == 0));
assert!(state.tool_results.is_empty());
assert!(state.image_item_keys.is_empty());
assert!(state
.opaque_completed_item_keys
.iter()
.all(|key| matches!(key, OpenAIResponsesOutputItemKey::Digest(_))));
}
#[test]
fn openai_responses_terminal_observation_does_not_retain_long_stream_content() {
let context = json!({"provider_api_format": "openai:responses"});
let mut observed = OpenAIResponsesProviderState::terminal_observation();
let mut full = OpenAIResponsesProviderState::default();
let initial = json!({
"type": "response.output_item.added", "output_index": 0,
"item": {"type": "function_call", "id": "fc_1", "call_id": "call_1", "name": "read", "arguments": ""}
});
assert_eq!(
observed.push_event(&context, &initial).unwrap(),
terminal_frames(full.push_event(&context, &initial).unwrap())
);
let text = "x".repeat(1024);
let events = [
json!({"type": "response.output_text.delta", "delta": text, "output_index": 3}),
json!({"type": "response.reasoning_summary_text.delta", "delta": text, "summary_index": 0}),
json!({"type": "response.function_call_arguments.delta", "delta": text, "output_index": 0}),
json!({"type": "response.custom_tool_call_input.delta", "delta": text, "output_index": 1}),
json!({"type": "response.function_call_output.delta", "delta": text, "output_index": 2, "call_id": "call_1"}),
];
for iteration in 0..2048 {
for event in &events {
let frames = observed.push_event(&context, event).unwrap();
assert!(frames.is_empty());
if iteration < 32 {
assert_eq!(
frames,
terminal_frames(full.push_event(&context, event).unwrap())
);
}
}
assert_no_responses_observation_content(&observed);
}
assert_eq!(observed.tool_calls.len(), 2);
assert_eq!(observed.tool_index_by_key, full.tool_index_by_key);
assert!(full.text_parts.values().any(|text| text.len() == 32 * 1024));
assert_eq!(full.reasoning.len(), 32 * 1024);
assert_eq!(full.reasoning_parts[&0].len(), 32 * 1024);
assert_eq!(full.tool_calls[&0].arguments.len(), 32 * 1024);
assert!(!full.tool_results.is_empty());
assert_eq!(
observed.finish(&context).unwrap(),
full.finish(&context).unwrap()
);
}
#[test]
fn openai_responses_terminal_observation_skips_completed_content_and_images() {
let context = json!({});
let text = "x".repeat(64 * 1024);
let items = [
json!({"type": "message", "content": [{"type": "output_text", "text": text}]}),
json!({"type": "reasoning", "summary": [{"type": "summary_text", "text": text}]}),
json!({"type": "custom_tool_call", "input": text, "name": "custom"}),
json!({"type": "shell_call", "action": {"command": text}}),
json!({"type": "apply_patch_call", "operation": {"patch": text}}),
json!({"type": "computer_call", "action": {"keys": [text]}}),
json!({"type": "function_call_output", "output": text}),
json!({"type": "custom_tool_call_output", "output": {"text": text}}),
json!({"type": "image_generation_call", "result": text, "status": "completed"}),
];
let mut observed = OpenAIResponsesProviderState::terminal_observation();
let mut full = OpenAIResponsesProviderState::default();
for (index, item) in items.iter().enumerate() {
let event =
json!({"type": "response.output_item.done", "output_index": index, "item": item});
assert_eq!(
observed.push_event(&context, &event).unwrap(),
terminal_frames(full.push_event(&context, &event).unwrap())
);
assert_no_responses_observation_content(&observed);
}
let completed = json!({"type": "response.completed", "response": {
"id": "resp_complete", "model": "model", "service_tier": "Priority",
"output": items, "usage": {"input_tokens": 100, "output_tokens": 20, "input_tokens_details": {"cached_tokens": 0}}
}});
assert_eq!(
observed.push_event(&context, &completed).unwrap(),
terminal_frames(full.push_event(&context, &completed).unwrap())
);
assert_eq!(observed.actual_service_tier(), Some("priority"));
assert_no_responses_observation_content(&observed);
}
#[test]
fn openai_responses_terminal_observation_hashes_opaque_keys_without_changing_deduplication() {
let context = json!({});
let text = "x".repeat(64 * 1024);
let items = [
json!({"type": "future_item", "id": "id:with:separators", "encrypted_content": text}),
json!({"type": "compaction", "encrypted_content": text}),
json!({"type": "future_item", "payload": {"text": text, "escaped": "\n\"\\"}}),
];
let mut observed = OpenAIResponsesProviderState::terminal_observation();
let mut full = OpenAIResponsesProviderState::default();
for item in &items {
let object = item.as_object().unwrap();
let normal_key = OpenAIResponsesProviderState::output_item_key(object);
let expected: [u8; 32] = Sha256::digest(normal_key.as_bytes()).into();
let OpenAIResponsesOutputItemKey::Digest(actual) =
observed.retained_output_item_key(object)
else {
panic!("observation must retain only an opaque item digest");
};
assert_eq!(actual, expected);
let event = json!({"type": "response.output_item.done", "item": item});
for _ in 0..2 {
assert_eq!(
observed.push_event(&context, &event).unwrap(),
terminal_frames(full.push_event(&context, &event).unwrap())
);
}
}
assert_eq!(observed.opaque_completed_item_keys.len(), items.len());
assert_no_responses_observation_content(&observed);
let mut final_items = items.to_vec();
final_items.push(json!({"type": "future_item", "id": "new_item"}));
let final_event =
json!({"type": "response.completed", "response": {"output": final_items}});
let frames = observed.push_event(&context, &final_event).unwrap();
assert_eq!(
frames,
terminal_frames(full.push_event(&context, &final_event).unwrap())
);
assert_eq!(
frames
.iter()
.filter(|frame| matches!(frame.event, CanonicalStreamEvent::UnknownEvent(_)))
.count(),
1
);
}
#[test]
fn openai_responses_terminal_observation_preserves_tool_identity_validation() {
let context = json!({"original_request_body": {"tools": [{
"type": "namespace", "name": "reports", "description": "Reporting tools", "tools": [{
"type": "function", "name": "write_report", "parameters": {"type": "object"}
}]
}]}});
let expected_alias = NamespaceToolAliases::from_report_context(&context)
.chat_name("reports", "write_report")
.expect("fixture must contain a valid namespace tool")
.to_owned();
let mut observed = OpenAIResponsesProviderState::terminal_observation();
let mut full = OpenAIResponsesProviderState::default();
let events = [
json!({"type": "response.function_call_arguments.delta", "item_id": "fc_1", "output_index": 0, "delta": "{"}),
json!({"type": "response.output_item.added", "output_index": 0, "item": {
"type": "function_call", "id": "fc_1", "namespace": "reports", "name": "write_report", "arguments": ""
}}),
json!({"type": "response.function_call_arguments.done", "item_id": "fc_1", "call_id": "call_1", "namespace": "reports", "arguments": "{}"}),
json!({"type": "response.function_call_output.delta", "call_id": "call_1", "output_index": 7, "delta": "result"}),
json!({"type": "response.function_call_arguments.done", "item_id": "fc_other", "name": "ordinary", "arguments": "{}"}),
json!({"type": "response.function_call_arguments.done", "item_id": "fc_1", "namespace": "missing", "arguments": "{}"}),
json!({"type": "response.output_item.done", "item": {
"type": "function_call", "id": "invalid", "name": "read", "caller": {"type": "future"}, "arguments": "{}"
}}),
json!({"type": "response.output_item.done", "item": {"content": "missing type"}}),
json!({"type": "response.future.delta", "payload": "unsupported"}),
];
let mut unknowns = 0;
for (step, event) in events.into_iter().enumerate() {
let frames = observed.push_event(&context, &event).unwrap();
assert_eq!(
frames,
terminal_frames(full.push_event(&context, &event).unwrap())
);
unknowns += frames
.iter()
.filter(|frame| matches!(frame.event, CanonicalStreamEvent::UnknownEvent(_)))
.count();
assert_eq!(observed.tool_index_by_key, full.tool_index_by_key);
assert_eq!(observed.last_tool_index, full.last_tool_index);
assert_eq!(observed.tool_calls.len(), full.tool_calls.len());
for (index, tool) in &observed.tool_calls {
assert_eq!(tool.name, full.tool_calls[index].name);
assert_eq!(tool.call_id, full.tool_calls[index].call_id);
}
if step == 1 || step == 2 {
assert_eq!(unknowns, 0);
assert_eq!(observed.tool_calls[&0].name, expected_alias);
assert_eq!(
observed.tool_calls[&0].call_id,
if step == 1 { "" } else { "call_1" }
);
}
assert_no_responses_observation_content(&observed);
}
assert_eq!(unknowns, 4);
assert_eq!(
observed.finish(&context).unwrap(),
full.finish(&context).unwrap()
);
}
#[test]
fn openai_chat_terminal_observation_does_not_buffer_arguments_before_identity() {
let context = json!({});
let mut observed = OpenAIChatProviderState::terminal_observation();
let mut full = OpenAIChatProviderState::default();
let delta = json!({"choices": [{"delta": {
"content": "x".repeat(1024), "reasoning_content": "r".repeat(1024),
"tool_calls": [{"index": 0, "function": {"arguments": "a".repeat(1024)}}]
}}]});
for iteration in 0..2048 {
let frames = observed
.push_line(&context, data_line(delta.clone()))
.unwrap();
if iteration < 32 {
assert_eq!(
frames,
terminal_frames(full.push_line(&context, data_line(delta.clone())).unwrap())
);
}
assert!(observed.tool_calls.is_empty());
}
assert_eq!(full.tool_calls[&0].pending_arguments.len(), 32 * 1024);
for event in [
json!({"choices": [{"delta": {"tool_calls": [{"index": 0, "id": "call_1", "function": {"name": "read", "arguments": "end"}}]}}]}),
json!({"choices": [{"delta": {"future": "unknown"}}]}),
json!({"choices": [{"delta": {}, "finish_reason": "tool_calls"}]}),
json!({"choices": [], "usage": {"prompt_tokens": 100, "completion_tokens": 20, "prompt_tokens_details": {"cached_tokens": 0}}, "service_tier": "Flex"}),
] {
assert_eq!(
observed
.push_line(&context, data_line(event.clone()))
.unwrap(),
terminal_frames(full.push_line(&context, data_line(event)).unwrap())
);
}
assert_eq!(observed.actual_service_tier(), Some("flex"));
assert!(observed.tool_calls.is_empty());
assert_eq!(
observed.finish(&context).unwrap(),
full.finish(&context).unwrap()
);
}
fn response_sequence_numbers(sse: &str) -> Vec<u64> {
let mut sequence_numbers = Vec::new();
for payload in sse.lines().filter_map(|line| line.strip_prefix("data: ")) {
@@ -423,10 +423,26 @@ impl TerminalStreamParser {
{
return Some(Self::OpenAIImage(OpenAiImageStreamTerminalState::default()));
}
ProviderStreamParser::for_api_format(provider_api_format).map(Self::Standard)
let provider = match ProviderStreamParser::for_api_format(provider_api_format)? {
ProviderStreamParser::OpenAIChat(_) => {
ProviderStreamParser::OpenAIChat(OpenAIChatProviderState::terminal_observation())
}
ProviderStreamParser::OpenAIResponses(_) => ProviderStreamParser::OpenAIResponses(
OpenAIResponsesProviderState::terminal_observation(),
),
ProviderStreamParser::Gemini(_) => {
ProviderStreamParser::Gemini(GeminiProviderState::terminal_observation())
}
provider @ ProviderStreamParser::Claude(_) => provider,
};
Some(Self::Standard(provider))
}
}
#[cfg(test)]
#[path = "terminal_observation_tests.rs"]
mod terminal_observation_tests;
enum ProviderStreamParser {
OpenAIChat(OpenAIChatProviderState),
OpenAIResponses(OpenAIResponsesProviderState),
@@ -0,0 +1,282 @@
use serde_json::{json, Value};
use super::{ProviderStreamParser, StreamingStandardTerminalObserver, TerminalStreamParser};
fn full_observer(context: &Value) -> StreamingStandardTerminalObserver {
StreamingStandardTerminalObserver {
provider: Some(TerminalStreamParser::Standard(
ProviderStreamParser::for_api_format(context["provider_api_format"].as_str().unwrap())
.unwrap(),
)),
..Default::default()
}
}
// Compare every input prefix, including EOF, to catch changes in identity and terminal timing.
fn assert_summaries_match(context: &Value, events: &[Value]) {
let structured = context["provider_api_format"]
.as_str()
.unwrap()
.starts_with("openai:responses");
for end in 0..=events.len() {
let mut compact = StreamingStandardTerminalObserver::default();
let mut full = full_observer(context);
let mut via_event = StreamingStandardTerminalObserver::default();
for (index, event) in events[..end].iter().enumerate() {
let line = format!("data: {event}\n").into_bytes();
compact.push_line(context, line.clone()).unwrap();
full.push_line(context, line).unwrap();
assert_eq!(
compact.latest_summary(),
full.latest_summary(),
"prefix {index}: {event}"
);
if structured {
via_event.push_event(context, event).unwrap();
assert_eq!(via_event.latest_summary(), full.latest_summary());
}
}
let expected = full.finish(context).unwrap();
assert_eq!(compact.finish(context).unwrap(), expected, "EOF at {end}");
if structured {
assert_eq!(via_event.finish(context).unwrap(), expected);
}
}
}
fn context(format: &str) -> Value {
json!({"provider_api_format": format, "client_api_format": format, "mapped_model": "test-model"})
}
fn completed(output: Vec<Value>) -> Value {
json!({"type":"response.completed","response":{
"id":"resp-final","model":"final-model","status":"completed","service_tier":" PRIORITY ",
"output":output,"usage":{"input_tokens":11,"output_tokens":7,"total_tokens":18,
"input_tokens_details":{"cached_tokens":0},"output_tokens_details":{"reasoning_tokens":3}}
}})
}
#[test]
fn responses_content_snapshots_and_deltas_preserve_every_summary() {
let events = vec![
json!({"type":"response.output_text.delta","delta":""}),
json!({"type":"response.output_text.delta","delta":"he","output_index":0}),
json!({"type":"response.created","response":{"id":"late-id","model":"late-model"}}),
json!({"type":"response.output_text.delta","delta":{"text":"hello"},"output_index":0}),
json!({"type":"response.output_text.done","text":"hello","output_index":0}),
json!({"type":"response.content_part.added","content_index":1,"part":{"type":"output_text","text":"another"}}),
json!({"type":"response.content_part.done","content_index":1,"part":{"type":"output_text","text":"another part"}}),
json!({"type":"response.refusal.delta","delta":"refuse"}),
json!({"type":"response.refusal.done","refusal":"refused"}),
json!({"type":"response.audio.transcript.delta","delta":"audio"}),
json!({"type":"response.audio.transcript.done","transcript":"audio transcript"}),
json!({"type":"response.reasoning_summary_text.delta","delta":"think","summary_index":0}),
json!({"type":"response.reasoning_text.delta","delta":" again","summary_index":0}),
json!({"type":"response.reasoning_summary_part.added","summary_index":1,"part":{"type":"summary_text","text":"second"}}),
json!({"type":"response.reasoning_summary_part.done","summary_index":1,"part":{"type":"summary_text","text":"second thought"}}),
json!({"type":"response.reasoning_summary_text.done","summary_index":0,"text":"think again"}),
json!({"type":"response.reasoning_text.done","text":""}),
completed(vec![
json!({"type":"message","content":[{"type":"output_text","text":"hello"},{"type":"refusal","refusal":"refused"}]}),
json!({"type":"reasoning","summary":[{"type":"summary_text","text":"think again"},{"type":"summary_text","text":"second thought"}]}),
]),
];
for format in ["openai:responses", "openai:responses:compact"] {
assert_summaries_match(&context(format), &events);
for event in &events {
assert_summaries_match(&context(format), std::slice::from_ref(event));
}
}
}
#[test]
fn responses_tools_and_unknown_execution_fields_preserve_summaries() {
let calls = vec![
json!({"type":"function_call","call_id":"call-0","name":"lookup","arguments":"{\"x\":1}"}),
json!({"type":"custom_tool_call","call_id":"call-1","name":"custom","input":"raw input"}),
json!({"type":"shell_call","call_id":"call-2","action":{"commands":["pwd"]}}),
json!({"type":"local_shell_call","call_id":"call-3","action":{"command":["pwd"]}}),
json!({"type":"apply_patch_call","call_id":"call-4","operation":{"type":"update_file","path":"a","diff":"+b"}}),
json!({"type":"computer_call","call_id":"call-5","action":{"type":"click","x":1,"y":2}}),
json!({"type":"function_call","call_id":"bad-caller","name":"lookup","arguments":"{}","caller":{"type":"direct"}}),
json!({"type":"function_call","call_id":"bad-namespace","namespace":42,"name":"lookup","arguments":"{}"}),
];
let mut events = vec![
json!({"type":"response.function_call_arguments.delta","output_index":0,"delta":"{"}),
json!({"type":"response.function_call_arguments.delta","output_index":0,"call_id":"call-0","delta":"\"x\":1}"}),
json!({"type":"response.function_call_arguments.done","output_index":0,"item":{"call_id":"call-0","name":"lookup","arguments":"{\"x\":1}"}}),
json!({"type":"response.function_call_arguments.done","output_index":0,"namespace":{},"arguments":"{}"}),
json!({"type":"response.custom_tool_call_input.delta","output_index":1,"delta":"raw"}),
json!({"type":"response.custom_tool_call_input.done","output_index":1,"input":"raw input"}),
];
for (index, item) in calls.iter().enumerate() {
events.push(json!({"type":"response.output_item.added","output_index":index,"item":item}));
events.push(json!({"type":"response.output_item.done","output_index":index,"item":item}));
}
for kind in [
"function_call",
"custom_tool_call",
"shell_call",
"local_shell_call",
"apply_patch_call",
"computer_call",
] {
events.push(json!({"type":format!("response.{kind}_output.delta"),"call_id":"result","delta":"result"}));
events.push(json!({"type":format!("response.{kind}_output.done"),"call_id":"result","output":{"ok":true}}));
events.push(json!({"type":"response.output_item.done","item":{"type":format!("{kind}_output"),"call_id":"result","output":"result complete"}}));
}
events.push(completed(calls));
assert_summaries_match(&context("openai:responses"), &events);
}
#[test]
fn responses_opaque_dedup_and_images_preserve_unknown_counts() {
let items = vec![
json!({"type":"future_item","id":"stable-id","payload":"first"}),
json!({"type":"future_item","encrypted_content":"encrypted","payload":"second"}),
json!({"type":"future_item","payload":{"no_id":true}}),
json!({"type":"image_generation_call","result":"image data","status":"completed"}),
json!({"type":"reasoning","summary":[],"encrypted_content":"reasoning"}),
];
let mut events = Vec::new();
for item in &items {
events.push(json!({"type":"response.output_item.added","item":item}));
events.push(json!({"type":"response.output_item.done","item":item}));
events.push(json!({"type":"response.output_item.done","item":item}));
}
events.push(json!({"type":"response.output_item.done","item":{"missing_type":true}}));
let mut output = items;
output[0]["payload"] = json!("changed body, same id");
output[1]["payload"] = json!("changed body, same encrypted content");
output.push(json!({"type":"new_future_item","payload":"never emitted"}));
events.push(completed(output));
let ctx = context("openai:responses");
assert_summaries_match(&ctx, &events);
let mut observer = StreamingStandardTerminalObserver::default();
for event in &events {
observer.push_event(&ctx, event).unwrap();
}
assert_eq!(
observer.finish(&ctx).unwrap().unwrap().unknown_event_count,
2
);
}
#[test]
fn responses_namespace_validation_keeps_existing_tool_identity() {
let mut ctx = context("openai:responses");
ctx["original_request_body"] = json!({"tools":[{
"type":"namespace","name":"search","description":"Search tools","tools":[{"type":"function","name":"lookup","parameters":{"type":"object"}}]
}]});
let events = [
json!({"type":"response.output_item.added","output_index":0,"item":{"type":"function_call","call_id":"call","namespace":"search","name":"lookup","arguments":"{"}}),
json!({"type":"response.function_call_arguments.delta","output_index":0,"delta":"}"}),
json!({"type":"response.function_call_arguments.done","output_index":0,"namespace":"search","arguments":"{}"}),
json!({"type":"response.function_call_arguments.done","output_index":0,"namespace":"missing","arguments":"{}"}),
completed(vec![
json!({"type":"function_call","call_id":"call","namespace":"search","name":"lookup","arguments":"{}"}),
]),
];
assert_summaries_match(&ctx, &events);
let mut observer = StreamingStandardTerminalObserver::default();
for event in &events {
observer.push_event(&ctx, event).unwrap();
}
let summary = observer.finish(&ctx).unwrap().unwrap();
assert_eq!(summary.unknown_event_count, 1);
assert_eq!(summary.finish_reason.as_deref(), Some("tool_calls"));
}
#[test]
fn responses_errors_zero_usage_and_event_type_lines_preserve_summaries() {
for terminal in [
json!({"type":"response.failed","response":{"status":"failed","error":{"message":"failed"},"usage":{"input_tokens":0,"output_tokens":0}}}),
json!({"type":"error","error":{"type":"server_error","message":"failed"}}),
json!({"type":"response.incomplete","response":{"status":"incomplete","incomplete_details":{"reason":"max_output_tokens"},"usage":{"input_tokens":2,"output_tokens":0}}}),
json!({"type":"response.done","response":{"status":"completed","usage":{"input_tokens":0,"output_tokens":0,"total_tokens":0}}}),
json!({"type":"response.completed","response":null}),
] {
let ctx = context("openai:responses");
let events = vec![
json!({"type":"response.future"}),
json!({"type":"ping"}),
terminal,
];
assert_summaries_match(&ctx, &events);
let mut compact = StreamingStandardTerminalObserver::default();
let mut full = full_observer(&ctx);
for mut event in events {
let kind = event.as_object_mut().unwrap().remove("type").unwrap();
for line in [
format!("event: {}\n", kind.as_str().unwrap()),
format!("data: {event}\n"),
"\n".to_string(),
] {
compact.push_line(&ctx, line.as_bytes().to_vec()).unwrap();
full.push_line(&ctx, line.into_bytes()).unwrap();
assert_eq!(compact.latest_summary(), full.latest_summary());
}
}
assert_eq!(compact.finish(&ctx).unwrap(), full.finish(&ctx).unwrap());
}
}
#[test]
fn chat_delayed_tool_identity_and_usage_only_preserve_summaries() {
let ctx = context("openai:chat");
assert_summaries_match(
&ctx,
&[
json!({"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{"}}]}}]}),
json!({"id":"late-id","model":"late-model","choices":[{"delta":{"tool_calls":[{"index":0,"id":"call","function":{"name":"lookup","arguments":"}"}}]}}]}),
json!({"choices":[{"delta":{"content":"text","reasoning_content":"reason"}}],"service_tier":"priority"}),
json!({"choices":[{"delta":{},"finish_reason":"tool_calls"}]}),
json!({"choices":[],"usage":{"prompt_tokens":3,"completion_tokens":2,"total_tokens":5}}),
],
);
for event in [
json!({"usage":{"prompt_tokens":0,"completion_tokens":0,"total_tokens":0}}),
json!({"choices":[{"delta":{"tool_calls":"malformed"}}]}),
json!({"choices":[{"delta":{"tool_calls":[null,{}, {"function":null}]}}]}),
json!({"choices":[{"delta":{},"finish_reason":"future_reason"}]}),
json!({"choices":[{"delta":{"future_content":true}}]}),
] {
assert_summaries_match(&ctx, &[event]);
}
}
#[test]
fn gemini_content_tools_and_errors_preserve_summaries() {
let ctx = context("gemini:generate_content");
let parts = vec![
json!({"text":"text"}),
json!({"text":"reason","thought":true,"thoughtSignature":"sig"}),
json!({"functionCall":{"id":"call","name":"lookup","args":{"x":1}}}),
json!({"functionResponse":{"id":"call","name":"lookup","response":{"ok":true}}}),
json!({"inlineData":{"mimeType":"image/png","data":"aW1hZ2U="}}),
json!({"futureContent":"unknown"}),
];
let mut events = Vec::new();
for part in &parts {
let event = json!({"candidates":[{"content":{"parts":[part]}}]});
events.push(event.clone());
events.push(event.clone());
assert_summaries_match(&ctx, &[event]);
}
events.push(json!({"responseId":"late-id","modelVersion":"late-model","candidates":[{"content":{"parts":parts},"finishReason":"STOP"}],"usageMetadata":{"promptTokenCount":5,"candidatesTokenCount":3,"totalTokenCount":8}}));
assert_summaries_match(&ctx, &events);
for reason in [
"MALFORMED_FUNCTION_CALL",
"SAFETY",
"MAX_TOKENS",
"FUTURE_REASON",
] {
assert_summaries_match(
&ctx,
&[
json!({"response":{"candidates":[{"content":{"parts":[{"text":"partial"}]}}]}}),
json!({"candidates":[{"content":{"parts":[]},"finishReason":reason}],"usageMetadata":{"promptTokenCount":0,"candidatesTokenCount":0}}),
],
);
}
}