diff --git a/crates/agentic-core/benches/executor_throughput.rs b/crates/agentic-core/benches/executor_throughput.rs index 8774e5d..1806237 100644 --- a/crates/agentic-core/benches/executor_throughput.rs +++ b/crates/agentic-core/benches/executor_throughput.rs @@ -137,6 +137,7 @@ fn make_request(input: &str, stream: bool, prev_id: Option) -> RequestPa conversation_id: None, tools: None, tool_choice: ToolChoice::Auto, + tool_choice_explicitly_set: false, stream, store: true, include: None, diff --git a/crates/agentic-core/src/config.rs b/crates/agentic-core/src/config.rs index cb87708..288ef19 100644 --- a/crates/agentic-core/src/config.rs +++ b/crates/agentic-core/src/config.rs @@ -1,12 +1,17 @@ +use std::collections::HashMap; +use std::hash::BuildHasher; + #[derive(Debug, Clone)] pub struct Config { pub llm_api_base: String, pub openai_api_key: Option, pub llm_ready_timeout_s: f64, pub llm_ready_interval_s: f64, + pub skip_llm_ready_check: bool, /// Database URL for conversation and response storage. /// `None` means stateful features are disabled; all requests are proxied. pub db_url: Option, + pub model_aliases: HashMap, } #[must_use] @@ -19,6 +24,39 @@ pub fn normalize_base_url(url: &str) -> String { s } +#[must_use] +pub fn resolve_model_alias(model: &str, aliases: &HashMap) -> String { + aliases.get(model).cloned().unwrap_or_else(|| model.to_string()) +} + +/// Parse `alias=target` entries from CLI/env configuration. +/// +/// # Errors +/// Returns an error when an entry is missing `=`, or either side is empty. +pub fn parse_model_aliases(entries: I) -> Result, String> +where + I: IntoIterator, + S: AsRef, +{ + let mut aliases = HashMap::new(); + for entry in entries { + let entry = entry.as_ref().trim(); + if entry.is_empty() { + continue; + } + let Some((alias, target)) = entry.split_once('=') else { + return Err(format!("model alias '{entry}' must use alias=target")); + }; + let alias = alias.trim(); + let target = target.trim(); + if alias.is_empty() || target.is_empty() { + return Err(format!("model alias '{entry}' must have non-empty alias and target")); + } + aliases.insert(alias.to_string(), target.to_string()); + } + Ok(aliases) +} + #[cfg(test)] mod tests { use super::*; @@ -34,4 +72,18 @@ mod tests { assert_eq!(normalize_base_url("http://host:8000"), "http://host:8000"); assert_eq!(normalize_base_url("http://host:8000/"), "http://host:8000"); } + + #[test] + fn model_aliases_parse_and_resolve() { + let aliases = parse_model_aliases(["codex-auto-review=Qwen/Qwen3"]).unwrap(); + assert_eq!(resolve_model_alias("codex-auto-review", &aliases), "Qwen/Qwen3"); + assert_eq!(resolve_model_alias("other", &aliases), "other"); + } + + #[test] + fn model_aliases_reject_invalid_entries() { + assert!(parse_model_aliases(["missing-separator"]).is_err()); + assert!(parse_model_aliases(["=target"]).is_err()); + assert!(parse_model_aliases(["alias="]).is_err()); + } } diff --git a/crates/agentic-core/src/events/normalize.rs b/crates/agentic-core/src/events/normalize.rs index 6d2b52a..52b00c5 100644 --- a/crates/agentic-core/src/events/normalize.rs +++ b/crates/agentic-core/src/events/normalize.rs @@ -127,6 +127,7 @@ fn extract_output_item_added(json: &Value) -> EventPayload { item_type: SSEItemType::from(json_str(item, "type")), output_index: json_u32(json, "output_index"), name: json_str_opt(item, "name"), + namespace: json_str_opt(item, "namespace"), call_id: json_str_opt(item, "call_id"), } } diff --git a/crates/agentic-core/src/events/types.rs b/crates/agentic-core/src/events/types.rs index 84d0ee5..edff2b5 100644 --- a/crates/agentic-core/src/events/types.rs +++ b/crates/agentic-core/src/events/types.rs @@ -113,6 +113,7 @@ pub enum EventPayload { item_type: SSEItemType, output_index: u32, name: Option, + namespace: Option, call_id: Option, }, diff --git a/crates/agentic-core/src/executor/accumulator.rs b/crates/agentic-core/src/executor/accumulator.rs index 7a8925e..606a9d6 100644 --- a/crates/agentic-core/src/executor/accumulator.rs +++ b/crates/agentic-core/src/executor/accumulator.rs @@ -434,6 +434,7 @@ mod tests { item_type: "message".into(), output_index: 0, name: None, + namespace: None, call_id: None, }, sequence_number: Some(1), @@ -625,6 +626,7 @@ mod tests { item_type: "function_call".into(), output_index: 0, name: Some("get_weather".into()), + namespace: Some("mcp__weather".into()), call_id: Some("call_abc".into()), }, sequence_number: Some(1), @@ -680,6 +682,7 @@ mod tests { assert_eq!(fc.id, "fc_1"); assert_eq!(fc.call_id, "call_abc"); assert_eq!(fc.name, "get_weather"); + assert_eq!(fc.namespace.as_deref(), Some("mcp__weather")); assert_eq!(fc.arguments, r#"{"location":"Paris"}"#); assert_eq!(fc.status, MessageStatus::Completed); } else { @@ -698,6 +701,7 @@ mod tests { item_type: "function_call".into(), output_index: 0, name: Some("search".into()), + namespace: None, call_id: Some("call_1".into()), }, sequence_number: Some(1), @@ -746,6 +750,7 @@ mod tests { item_type: "function_call".into(), output_index: 0, name: Some("get_weather".into()), + namespace: None, call_id: Some("call_1".into()), }, sequence_number: Some(1), @@ -769,6 +774,7 @@ mod tests { item_type: "function_call".into(), output_index: 1, name: Some("get_time".into()), + namespace: None, call_id: Some("call_2".into()), }, sequence_number: Some(3), @@ -811,6 +817,7 @@ mod tests { item_type: "message".into(), output_index: 0, name: None, + namespace: None, call_id: None, }, sequence_number: Some(1), @@ -833,6 +840,7 @@ mod tests { item_type: "function_call".into(), output_index: 1, name: Some("lookup".into()), + namespace: None, call_id: Some("call_x".into()), }, sequence_number: Some(3), @@ -875,6 +883,7 @@ mod tests { item_type: "function_call".into(), output_index: 0, name: Some("old_name".into()), + namespace: None, call_id: Some("old_call".into()), }, sequence_number: Some(1), @@ -912,6 +921,7 @@ mod tests { item_type: "function_call".into(), output_index: 0, name: Some("tool".into()), + namespace: None, call_id: Some("c1".into()), }, sequence_number: Some(1), @@ -968,6 +978,7 @@ mod tests { item_type: "function_call".into(), output_index: 0, name: Some("partial".into()), + namespace: None, call_id: Some("c1".into()), }, sequence_number: Some(1), diff --git a/crates/agentic-core/src/executor/dispatch.rs b/crates/agentic-core/src/executor/dispatch.rs new file mode 100644 index 0000000..0d63872 --- /dev/null +++ b/crates/agentic-core/src/executor/dispatch.rs @@ -0,0 +1,29 @@ +use crate::tool::ToolRegistry; +use crate::types::io::{InputItem, OutputItem}; + +#[derive(Debug, Clone)] +pub enum LoopDecision { + Continue(Vec), + RequiresClientAction(Vec), + Done, + Incomplete(String), +} + +#[must_use] +pub fn client_action_items(output: &[OutputItem], registry: &ToolRegistry) -> Vec { + output + .iter() + .filter(|item| item.requires_client_action(registry)) + .cloned() + .collect() +} + +#[must_use] +pub fn decide_client_action(output: &[OutputItem], registry: &ToolRegistry) -> LoopDecision { + let items = client_action_items(output, registry); + if items.is_empty() { + LoopDecision::Done + } else { + LoopDecision::RequiresClientAction(items) + } +} diff --git a/crates/agentic-core/src/executor/engine.rs b/crates/agentic-core/src/executor/engine.rs index a0aa2c9..e05f2ca 100644 --- a/crates/agentic-core/src/executor/engine.rs +++ b/crates/agentic-core/src/executor/engine.rs @@ -10,13 +10,14 @@ use std::sync::Arc; use async_stream::stream; use either::Either; use futures::{Stream, StreamExt}; -use tracing::warn; +use tracing::{debug, warn}; use crate::executor::accumulator::ResponseAccumulator; use crate::executor::error::{ExecutorError, ExecutorResult}; use crate::executor::modes::{ConversationHandler, ResponseHandler}; use crate::executor::request::{ExecutionContext, RequestContext}; use crate::storage::InOutItem; +use crate::tool::{flatten_tool_choice_for_upstream, flatten_tools_for_upstream, normalize_output_items_with_tools}; use crate::types::event::ResponseStatus; use crate::types::io::{InputItem, ResponsesInput, resolve_tool_choice, resolve_tools}; use crate::types::request_response::{RequestPayload, ResponsePayload}; @@ -31,6 +32,13 @@ pub type BoxStream = Pin + Send>>; /// Wire-format marker signalling end-of-stream to the client. const DONE_MARKER: &str = "data: [DONE]\n\n"; +fn responses_input_item_count(input: &ResponsesInput) -> usize { + match input { + ResponsesInput::Text(_) => 1, + ResponsesInput::Items(items) => items.len(), + } +} + /// Fetch the next raw bytes chunk from a streaming response. /// /// Returns `Ok(Some(bytes))` on data, `Ok(None)` when the stream ends cleanly, @@ -96,6 +104,22 @@ async fn send_request( Ok(resp) } +fn drain_complete_utf8_lines(buffer: &mut Vec) -> Vec { + let mut lines = Vec::new(); + while let Some(pos) = buffer.iter().position(|byte| *byte == b'\n') { + let line = buffer.drain(..=pos).collect::>(); + let line_end = if pos > 0 && line.get(pos - 1) == Some(&b'\r') { + pos - 1 + } else { + pos + }; + if let Ok(line) = std::str::from_utf8(&line[..line_end]) { + lines.push(line.to_string()); + } + } + lines +} + /// Makes a non-streaming HTTP POST to the LLM backend and returns the full JSON body. /// /// Used by [`run_blocking`] so it can pass the result to [`ResponseAccumulator::from_json`]. @@ -139,8 +163,23 @@ pub async fn rehydrate_conversation( response_id, conversation_id: None, }; + debug!( + response_id = %ctx.response_id, + model = %ctx.original_request.model, + store = ctx.original_request.store, + stream = ctx.original_request.stream, + has_previous_response_id = ctx.original_request.previous_response_id.is_some(), + has_conversation_id = ctx.original_request.conversation_id.is_some(), + new_input_items = ctx.new_input_items.len(), + tools = ctx.original_request.tools.as_ref().map_or(0, Vec::len), + "rehydrating responses request" + ); if ctx.original_request.conversation_id.is_some() && ctx.original_request.previous_response_id.is_some() { + debug!( + response_id = %ctx.response_id, + "rejecting responses request with both conversation_id and previous_response_id" + ); return Err(ExecutorError::InvalidRequest( "provide only one of conversation_id or previous_response_id".into(), )); @@ -157,6 +196,11 @@ pub async fn rehydrate_conversation( } ctx.enriched_request.input = ResponsesInput::Items(ctx.new_input_items.clone()); + debug!( + response_id = %ctx.response_id, + input_items = responses_input_item_count(&ctx.enriched_request.input), + "responses request has no server-side history to hydrate" + ); Ok(ctx) } @@ -170,6 +214,7 @@ async fn rehydrate_from_response(ctx: &mut RequestContext, exec_ctx: &ExecutionC let history = exec_ctx.resp_handler.rehydrate(ctx).await?; let mut items = InOutItem::into_input_items(history); + let history_item_count = items.len(); items.reserve(ctx.new_input_items.len()); items.extend(ctx.new_input_items.iter().cloned()); @@ -183,9 +228,18 @@ async fn rehydrate_from_response(ctx: &mut RequestContext, exec_ctx: &ExecutionC ctx.enriched_request.tool_choice = resolve_tool_choice( &ctx.original_request.tool_choice, &stored.metadata.effective_tool_choice, - false, + ctx.original_request.tool_choice_explicitly_set, ); ctx.conversation_id = stored.conversation_id; + debug!( + response_id = %ctx.response_id, + previous_response_id = ?ctx.original_request.previous_response_id, + conversation_id = ?ctx.conversation_id, + history_items = history_item_count, + new_input_items = ctx.new_input_items.len(), + effective_tools = ctx.enriched_request.tools.as_ref().map_or(0, Vec::len), + "rehydrated responses request from previous response" + ); Ok(()) } @@ -206,14 +260,71 @@ async fn rehydrate_from_conversation(ctx: &mut RequestContext, exec_ctx: &Execut )?; let mut items = InOutItem::into_input_items(history); + let history_item_count = items.len(); items.reserve(ctx.new_input_items.len()); items.extend(ctx.new_input_items.iter().cloned()); ctx.enriched_request.input = ResponsesInput::Items(items); - ctx.conversation_id = Some(conv_data.conversation_id); + let conversation_id = conv_data.conversation_id; + ctx.conversation_id = Some(conversation_id.clone()); + debug!( + response_id = %ctx.response_id, + conversation_id = %conversation_id, + store = ctx.original_request.store, + history_items = history_item_count, + new_input_items = ctx.new_input_items.len(), + "rehydrated responses request from conversation" + ); Ok(()) } +/// Apply request transformations required before sending a hydrated context +/// to the upstream Responses endpoint. +pub fn prepare_context_for_upstream(ctx: &mut RequestContext, exec_ctx: &ExecutionContext) { + let original_model = ctx.enriched_request.model.clone(); + let resolved_model = exec_ctx.resolve_model_alias(&original_model); + let alias_rewritten = resolved_model != original_model; + ctx.enriched_request.model = resolved_model; + ctx.enriched_request.normalize_for_upstream(); + debug!( + response_id = %ctx.response_id, + model_before = %original_model, + model_after = %ctx.enriched_request.model, + alias_rewritten, + input_items = responses_input_item_count(&ctx.enriched_request.input), + tools = ctx.enriched_request.tools.as_ref().map_or(0, Vec::len), + "prepared responses request for upstream" + ); +} + +/// Serialize a hydrated, upstream-prepared context into the vLLM request body. +/// +/// The original request stored on the context is left untouched; namespace +/// tools and tool choices are flattened only in this cloned upstream body. +/// +/// # Errors +/// Returns [`ExecutorError::JsonError`] when the upstream request body cannot be serialized. +pub fn upstream_request_json(ctx: &RequestContext, stream: bool) -> ExecutorResult { + let mut upstream_request = ctx.enriched_request.clone(); + let original_tools = ctx.enriched_request.tools.as_ref().map_or(0, Vec::len); + upstream_request.tools = flatten_tools_for_upstream(ctx.enriched_request.tools.as_deref()); + upstream_request.tool_choice = + flatten_tool_choice_for_upstream(&ctx.enriched_request.tool_choice, ctx.enriched_request.tools.as_deref()); + let upstream_tools = upstream_request.tools.as_ref().map_or(0, Vec::len); + let tool_choice_flattened = upstream_request.tool_choice != ctx.enriched_request.tool_choice; + debug!( + response_id = %ctx.response_id, + model = %upstream_request.model, + stream, + input_items = responses_input_item_count(&upstream_request.input), + original_tools, + upstream_tools, + tool_choice_flattened, + "serializing upstream responses request" + ); + serialize_to_string(&upstream_request.to_upstream_request(stream)).map_err(ExecutorError::JsonError) +} + /// Step 2 — Call the LLM inference backend; yields raw SSE lines (`data: …`). /// /// Always requests `stream=true` upstream. Stops on `[DONE]`. @@ -237,7 +348,7 @@ pub fn call_inference( }; let mut bytes = resp.bytes_stream(); - let mut buf = String::with_capacity(8192); + let mut buf = Vec::with_capacity(8192); loop { let chunk = match next_chunk(&mut bytes, chunk_timeout).await { @@ -246,19 +357,14 @@ pub fn call_inference( Err(e) => { yield Err(e); return; } }; - match std::str::from_utf8(&chunk) { - Ok(s) => buf.push_str(s), - Err(_) => buf.push_str(&String::from_utf8_lossy(&chunk)), - } + buf.extend_from_slice(&chunk); - while let Some(pos) = buf.find('\n') { - let line = buf[..pos].trim_end_matches('\r'); - match line { + for line in drain_complete_utf8_lines(&mut buf) { + match line.as_str() { "data: [DONE]" => return, - l if l.starts_with("data: ") => yield Ok(l.to_string()), + l if l.starts_with("data: ") => yield Ok(line), _ => {} } - buf.drain(..=pos); } } } @@ -299,9 +405,12 @@ pub async fn persist_response( async fn run_blocking(ctx: RequestContext, exec_ctx: &ExecutionContext) -> ExecutorResult { let url = exec_ctx.responses_url(); - // Non-streaming request: stream=false → full JSON body → from_json. - let upstream_json = - serialize_to_string(&ctx.enriched_request.to_upstream_request(false)).map_err(ExecutorError::JsonError)?; + debug!( + response_id = %ctx.response_id, + url = %url, + "executing non-streaming responses request" + ); + let upstream_json = upstream_request_json(&ctx, false)?; let body = fetch_response_json(upstream_json, &url, &exec_ctx.client, exec_ctx.client_auth.as_deref()).await?; @@ -311,6 +420,7 @@ async fn run_blocking(ctx: RequestContext, exec_ctx: &ExecutionContext) -> Execu ctx.original_request.previous_response_id.as_deref(), ctx.original_request.instructions.as_deref(), ); + normalize_output_items_with_tools(&mut payload.output, ctx.enriched_request.tools.as_deref()); ctx.inject_ids(&mut payload); let should_persist = ctx.original_request.store @@ -319,8 +429,15 @@ async fn run_blocking(ctx: RequestContext, exec_ctx: &ExecutionContext) -> Execu if should_persist { let ch = exec_ctx.conv_handler.clone(); let rh = exec_ctx.resp_handler.clone(); - if let Err(e) = persist_response(payload.clone(), ctx, ch, rh).await { - warn!("persist failed: {e}"); + let response_id = ctx.response_id.clone(); + let output_items = payload.output.len(); + match persist_response(payload.clone(), ctx, ch, rh).await { + Ok(()) => debug!( + response_id = %response_id, + output_items, + "persisted non-streaming responses output" + ), + Err(e) => warn!("persist failed: {e}"), } } @@ -329,8 +446,12 @@ async fn run_blocking(ctx: RequestContext, exec_ctx: &ExecutionContext) -> Execu fn run_stream(ctx: RequestContext, exec_ctx: Arc) -> BoxStream { let url = exec_ctx.responses_url(); - // Streaming request: stream=true → SSE lines → from_stream. - let upstream_json = match serialize_to_string(&ctx.enriched_request.to_upstream_request(true)) { + debug!( + response_id = %ctx.response_id, + url = %url, + "executing streaming responses request" + ); + let upstream_json = match upstream_request_json(&ctx, true) { Ok(s) => s, Err(e) => { return Box::pin(stream! { @@ -368,6 +489,7 @@ fn run_stream(ctx: RequestContext, exec_ctx: Arc) -> BoxStream ctx.original_request.previous_response_id.as_deref(), ctx.original_request.instructions.as_deref(), ); + normalize_output_items_with_tools(&mut payload.output, ctx.enriched_request.tools.as_deref()); ctx.inject_ids(&mut payload); yield payload.as_responses_chunk(); yield DONE_MARKER.to_string(); @@ -375,8 +497,15 @@ fn run_stream(ctx: RequestContext, exec_ctx: Arc) -> BoxStream if should_persist { let ch = exec_ctx.conv_handler.clone(); let rh = exec_ctx.resp_handler.clone(); - if let Err(e) = persist_response(payload, ctx, ch, rh).await { - warn!("persist failed: {e}"); + let response_id = ctx.response_id.clone(); + let output_items = payload.output.len(); + match persist_response(payload, ctx, ch, rh).await { + Ok(()) => debug!( + response_id = %response_id, + output_items, + "persisted streaming responses output" + ), + Err(e) => warn!("persist failed: {e}"), } } } @@ -409,10 +538,47 @@ pub async fn execute( request: RequestPayload, exec_ctx: Arc, ) -> ExecutorResult> { - let ctx = rehydrate_conversation(request, &exec_ctx).await?; + debug!( + model = %request.model, + store = request.store, + stream = request.stream, + has_previous_response_id = request.previous_response_id.is_some(), + has_conversation_id = request.conversation_id.is_some(), + tools = request.tools.as_ref().map_or(0, Vec::len), + "executor received responses request" + ); + let mut ctx = rehydrate_conversation(request, &exec_ctx).await?; + prepare_context_for_upstream(&mut ctx, &exec_ctx); if ctx.original_request.stream { Ok(Either::Right(run_stream(ctx, exec_ctx))) } else { Ok(Either::Left(run_blocking(ctx, &exec_ctx).await?)) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn utf8_line_reader_preserves_split_multibyte_characters() { + let snowman = "\u{2603}"; + let line = format!(r#"data: {{"delta":"snow {snowman}"}}"#); + let bytes = format!("{line}\n").into_bytes(); + let split_at = bytes + .windows(snowman.len()) + .position(|window| window == snowman.as_bytes()) + .expect("snowman bytes present") + + 1; + let mut buffer = bytes[..split_at].to_vec(); + + assert!(drain_complete_utf8_lines(&mut buffer).is_empty()); + + buffer.extend_from_slice(&bytes[split_at..]); + let lines = drain_complete_utf8_lines(&mut buffer); + + assert!(buffer.is_empty()); + assert_eq!(lines, vec![line]); + assert!(!lines[0].contains('\u{FFFD}')); + } +} diff --git a/crates/agentic-core/src/executor/mod.rs b/crates/agentic-core/src/executor/mod.rs index 32fbabc..ccab900 100644 --- a/crates/agentic-core/src/executor/mod.rs +++ b/crates/agentic-core/src/executor/mod.rs @@ -1,12 +1,17 @@ //! Agentic loop executor. pub mod accumulator; +pub mod dispatch; pub mod engine; pub mod error; pub mod modes; pub mod request; -pub use engine::{BoxStream, call_inference, create_conversation, execute, persist_response, rehydrate_conversation}; +pub use dispatch::{LoopDecision, client_action_items, decide_client_action}; +pub use engine::{ + BoxStream, call_inference, create_conversation, execute, persist_response, prepare_context_for_upstream, + rehydrate_conversation, upstream_request_json, +}; pub use error::{ExecutorError, ExecutorResult}; pub use modes::{ConversationHandler, ResponseHandler}; pub use request::ExecutionContext; diff --git a/crates/agentic-core/src/executor/modes/conversation.rs b/crates/agentic-core/src/executor/modes/conversation.rs index b0fb9de..488fdc3 100644 --- a/crates/agentic-core/src/executor/modes/conversation.rs +++ b/crates/agentic-core/src/executor/modes/conversation.rs @@ -92,8 +92,8 @@ impl ConversationHandler { let metadata = ResponseMetadata { model: ctx.enriched_request.model, previous_response_id: ctx.original_request.previous_response_id, - effective_tools: ctx.original_request.tools, - effective_tool_choice: ctx.original_request.tool_choice, + effective_tools: ctx.enriched_request.tools, + effective_tool_choice: ctx.enriched_request.tool_choice, effective_instructions: ctx.original_request.instructions, }; @@ -133,6 +133,7 @@ mod tests { conversation_id: conversation_id.map(str::to_string), tools: None, tool_choice: ToolChoice::Auto, + tool_choice_explicitly_set: false, stream: false, store: true, include: None, diff --git a/crates/agentic-core/src/executor/modes/response.rs b/crates/agentic-core/src/executor/modes/response.rs index a747776..ac61d14 100644 --- a/crates/agentic-core/src/executor/modes/response.rs +++ b/crates/agentic-core/src/executor/modes/response.rs @@ -72,8 +72,8 @@ impl ResponseHandler { let metadata = ResponseMetadata { model: ctx.enriched_request.model, previous_response_id: ctx.original_request.previous_response_id, - effective_tools: ctx.original_request.tools, - effective_tool_choice: ctx.original_request.tool_choice, + effective_tools: ctx.enriched_request.tools, + effective_tool_choice: ctx.enriched_request.tool_choice, effective_instructions: ctx.original_request.instructions, }; @@ -112,6 +112,7 @@ mod tests { conversation_id: None, tools: None, tool_choice: ToolChoice::Auto, + tool_choice_explicitly_set: false, stream: false, store: true, include: None, diff --git a/crates/agentic-core/src/executor/request.rs b/crates/agentic-core/src/executor/request.rs index 66f7fe0..8579baf 100644 --- a/crates/agentic-core/src/executor/request.rs +++ b/crates/agentic-core/src/executor/request.rs @@ -1,7 +1,8 @@ +use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; -use crate::config::Config; +use crate::config::{Config, resolve_model_alias}; use crate::error::Error; use crate::executor::modes::{ConversationHandler, ResponseHandler}; use crate::storage::{ConversationStore, ResponseStore, create_pool_with_schema}; @@ -51,6 +52,7 @@ pub struct ExecutionContext { /// Maximum wait time for the next SSE chunk. `Duration::ZERO` disables the timeout. /// Sourced from [`Config::streaming_chunk_timeout_s`](crate::config::Config::streaming_chunk_timeout_s). pub streaming_timeout: Duration, + pub model_aliases: HashMap, } impl ExecutionContext { @@ -81,9 +83,15 @@ impl ExecutionContext { llm_base_url, client_auth, streaming_timeout: Duration::from_secs(30), + model_aliases: HashMap::new(), } } + #[must_use] + pub fn resolve_model_alias(&self, model: &str) -> String { + resolve_model_alias(model, &self.model_aliases) + } + /// Build an `ExecutionContext` directly from [`Config`](crate::config::Config). /// /// Creates the database pool, both storage handlers, and an HTTP client @@ -110,6 +118,7 @@ impl ExecutionContext { llm_base_url: cfg.llm_api_base.clone(), client_auth: cfg.openai_api_key.clone(), streaming_timeout: Duration::from_secs(30), + model_aliases: cfg.model_aliases.clone(), }) } } diff --git a/crates/agentic-core/src/lib.rs b/crates/agentic-core/src/lib.rs index 93bb05a..048778f 100644 --- a/crates/agentic-core/src/lib.rs +++ b/crates/agentic-core/src/lib.rs @@ -18,10 +18,12 @@ pub use tool::{ FunctionHandler, GatewayExecutor, ToolEntry, ToolError, ToolHandler, ToolOutput, ToolRegistry, ToolType, }; pub use types::{ - CodeInterpreterToolParam, EmptyToolNameError, FileSearchToolParam, FunctionTool, FunctionToolCall, + CodeInterpreterToolParam, CodexCustomToolParam, CodexNamespaceMember, CodexNamespaceToolParam, + CodexToolSearchToolParam, EmptyToolNameError, FileSearchToolParam, FunctionTool, FunctionToolCall, FunctionToolParam, FunctionToolResultMessage, IncompleteDetails, InputContent, InputImageContent, InputItem, InputMessage, InputMessageContent, InputTextContent, InputTokenDetails, McpToolParam, NonEmptyToolName, OutputItem, OutputMessage, OutputTextContent, OutputTokenDetails, ReasoningOutput, ReasoningTextContent, RequestPayload, - ResponsePayload, ResponseUsage, ResponsesInput, ResponsesTool, ToolChoice, UpstreamRequest, WebSearchToolParam, + ResponsePayload, ResponseUsage, ResponsesInput, ResponsesTool, ToolChoice, ToolSearchExecution, UpstreamRequest, + WebSearchToolParam, }; pub use utils::{utcnow_str, uuid7_str}; diff --git a/crates/agentic-core/src/proxy.rs b/crates/agentic-core/src/proxy.rs index 36c5260..58575ed 100644 --- a/crates/agentic-core/src/proxy.rs +++ b/crates/agentic-core/src/proxy.rs @@ -1,15 +1,21 @@ use std::pin::Pin; use std::time::Duration; +use async_stream::stream; use bytes::Bytes; -use futures::{Stream, TryStreamExt}; +use futures::{Stream, StreamExt, TryStreamExt}; use http::{HeaderMap, HeaderName, HeaderValue, StatusCode}; use reqwest::Client; use serde_json::Value; -use tracing::warn; +use std::collections::{HashMap, HashSet}; +use tracing::{debug, warn}; -use crate::config::Config; +use crate::config::{Config, resolve_model_alias}; use crate::error::Error; +use crate::tool::{ + alternate_model_visible_namespace_member_name, legacy_model_visible_namespace_member_name, + model_visible_namespace_member_name, +}; const HOP_BY_HOP: &[&str] = &[ "connection", @@ -43,6 +49,36 @@ pub enum ProxyBody { Stream(Pin> + Send>>), } +#[derive(Clone, Debug)] +struct NamespaceMemberName { + namespace: String, + name: String, +} + +#[derive(Clone, Debug)] +struct NamespaceCallMapping { + member: NamespaceMemberName, + upstream_name: String, + strip_container_arguments: bool, +} + +#[derive(Clone, Debug, Default)] +struct NamespaceNormalization { + calls: HashMap, + original_tools: Option, +} + +impl NamespaceNormalization { + fn is_empty(&self) -> bool { + self.calls.is_empty() && self.original_tools.is_none() + } +} + +struct NormalizedProxyRequest { + body: Bytes, + namespace: NamespaceNormalization, +} + pub struct ProxyResponse { pub status: StatusCode, pub headers: HeaderMap, @@ -134,6 +170,633 @@ fn is_sse_content_type(headers: &reqwest::header::HeaderMap) -> bool { .is_some_and(|ct| ct.to_ascii_lowercase().starts_with("text/event-stream")) } +fn normalize_developer_roles(value: &mut Value) -> bool { + match value { + Value::Object(object) => { + let mut changed = false; + if object.get("role").and_then(Value::as_str) == Some("developer") { + object.insert("role".to_string(), Value::String("system".to_string())); + changed = true; + } + for item in object.values_mut() { + changed |= normalize_developer_roles(item); + } + changed + } + Value::Array(items) => { + let mut changed = false; + for item in items { + changed |= normalize_developer_roles(item); + } + changed + } + Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => false, + } +} + +fn is_system_role(value: &Value) -> bool { + value + .as_object() + .and_then(|object| object.get("role")) + .and_then(Value::as_str) + .is_some_and(|role| role == "system") +} + +fn move_system_messages_to_front(value: &mut Value) -> bool { + let Value::Array(items) = value else { + return false; + }; + + let mut saw_non_system = false; + let mut changed = false; + for item in items.iter() { + if is_system_role(item) { + changed |= saw_non_system; + } else { + saw_non_system = true; + } + } + + if changed { + items.sort_by_key(|item| !is_system_role(item)); + } + changed +} + +fn content_as_parts(content: Value) -> Vec { + match content { + Value::Array(parts) => parts, + Value::String(text) => vec![serde_json::json!({ + "type": "input_text", + "text": text + })], + Value::Null => Vec::new(), + other => vec![other], + } +} + +fn append_message_content(target: &mut Value, source: &Value) { + let Some(source_content) = source.as_object().and_then(|object| object.get("content")).cloned() else { + return; + }; + let mut source_parts = content_as_parts(source_content); + if source_parts.is_empty() { + return; + } + + let Some(target_object) = target.as_object_mut() else { + return; + }; + let target_content = target_object + .entry("content".to_string()) + .or_insert_with(|| Value::Array(Vec::new())); + if !target_content.is_array() { + let current = std::mem::take(target_content); + *target_content = Value::Array(content_as_parts(current)); + } + if let Value::Array(target_parts) = target_content { + target_parts.append(&mut source_parts); + } +} + +fn merge_system_messages(value: &mut Value) -> bool { + let Value::Array(items) = value else { + return false; + }; + + let Some(first_system_index) = items.iter().position(is_system_role) else { + return false; + }; + + let mut merged = Vec::with_capacity(items.len()); + let mut first_system = items[first_system_index].clone(); + let mut changed = false; + + for (index, item) in items.iter().enumerate() { + if index == first_system_index { + continue; + } + if is_system_role(item) { + append_message_content(&mut first_system, item); + changed = true; + } else { + merged.push(item.clone()); + } + } + + if changed { + merged.insert(0, first_system); + *items = merged; + } + changed +} + +fn system_message(text: &str) -> Value { + serde_json::json!({ + "type": "message", + "role": "system", + "content": [ + { + "type": "input_text", + "text": text + } + ] + }) +} + +fn prepend_system_message(input: &mut Value, instructions: &str) { + match input { + Value::Array(items) => items.insert(0, system_message(instructions)), + Value::String(user_text) => { + let user_text = std::mem::take(user_text); + *input = Value::Array(vec![ + system_message(instructions), + serde_json::json!({ + "type": "message", + "role": "user", + "content": [ + { + "type": "input_text", + "text": user_text + } + ] + }), + ]); + } + _ => {} + } +} + +#[cfg(test)] +fn normalize_request_body(body: Bytes, config: &Config) -> Bytes { + normalize_proxy_request_body(body, config).body +} + +fn normalize_proxy_request_body(body: Bytes, config: &Config) -> NormalizedProxyRequest { + let Ok(mut value) = serde_json::from_slice::(&body) else { + return NormalizedProxyRequest { + body, + namespace: NamespaceNormalization::default(), + }; + }; + + let mut changed = false; + let mut namespace = NamespaceNormalization::default(); + if let Some(object) = value.as_object_mut() { + if let Some(model) = object.get("model").and_then(Value::as_str) { + let resolved = resolve_model_alias(model, &config.model_aliases); + if resolved != model { + debug!( + model_before = %model, + model_after = %resolved, + "rewrote proxy request model alias" + ); + object.insert("model".to_string(), Value::String(resolved)); + changed = true; + } + } + let instructions = object.remove("instructions").and_then(|value| match value { + Value::String(instructions) if !instructions.is_empty() => Some(instructions), + other => { + object.insert("instructions".to_string(), other); + None + } + }); + if let Some(instructions) = instructions { + if let Some(input) = object.get_mut("input") { + prepend_system_message(input, &instructions); + debug!("moved proxy request instructions into input"); + changed = true; + } else { + object.insert("instructions".to_string(), Value::String(instructions)); + } + } + if let Some(input) = object.get_mut("input") { + changed |= normalize_developer_roles(input); + changed |= move_system_messages_to_front(input); + changed |= merge_system_messages(input); + } + changed |= flatten_namespace_tools_for_upstream(object, &mut namespace); + changed |= rewrite_tool_choice_for_upstream(object, &namespace); + } + + if !changed { + return NormalizedProxyRequest { body, namespace }; + } + NormalizedProxyRequest { + body: serde_json::to_vec(&value).map_or(body, Bytes::from), + namespace, + } +} + +fn record_alias_candidate( + candidates: &mut HashMap>, + alias: String, + mapping: NamespaceCallMapping, +) { + candidates + .entry(alias) + .and_modify(|candidate| *candidate = None) + .or_insert_with(|| Some(mapping)); +} + +fn register_unambiguous_aliases( + normalization: &mut NamespaceNormalization, + candidates: HashMap>, + top_level_names: &HashSet, +) { + for (alias, mapping) in candidates { + if top_level_names.contains(&alias) { + continue; + } + if let Some(mapping) = mapping { + normalization.calls.entry(alias).or_insert(mapping); + } + } +} + +fn raw_top_level_tool_names(tools: &[Value]) -> HashSet { + tools + .iter() + .filter_map(|tool| { + let object = tool.as_object()?; + if object.get("type").and_then(Value::as_str) == Some("namespace") { + return None; + } + object.get("name").and_then(Value::as_str).map(str::to_string) + }) + .collect() +} + +fn raw_namespace_has_flat_name_collision( + namespace_name: &str, + function_members: &[&Value], + top_level_names: &HashSet, +) -> bool { + function_members.iter().any(|member| { + member.get("name").and_then(Value::as_str).is_some_and(|member_name| { + top_level_names.contains(&model_visible_namespace_member_name(namespace_name, member_name)) + }) + }) +} + +fn record_single_member_namespace_container_candidate( + container_candidates: &mut HashMap>, + namespace_name: &str, + function_members: &[&Value], +) { + if function_members.len() != 1 { + return; + } + let Some(member_name) = function_members[0].get("name").and_then(Value::as_str) else { + return; + }; + let flat_name = model_visible_namespace_member_name(namespace_name, member_name); + record_alias_candidate( + container_candidates, + namespace_name.to_string(), + NamespaceCallMapping { + member: NamespaceMemberName { + namespace: namespace_name.to_string(), + name: member_name.to_string(), + }, + upstream_name: flat_name, + strip_container_arguments: true, + }, + ); +} + +fn flatten_namespace_tools_for_upstream( + object: &mut serde_json::Map, + normalization: &mut NamespaceNormalization, +) -> bool { + let Some(tools) = object.get_mut("tools").and_then(Value::as_array_mut) else { + return false; + }; + + let original_tools = tools.clone(); + let top_level_names = raw_top_level_tool_names(tools); + let mut bare_member_candidates: HashMap> = HashMap::new(); + let mut legacy_member_candidates: HashMap> = HashMap::new(); + let mut alternate_member_candidates: HashMap> = HashMap::new(); + let mut container_candidates: HashMap> = HashMap::new(); + let mut upstream_tools = Vec::with_capacity(tools.len()); + let mut changed = false; + + for tool in std::mem::take(tools) { + let Some(tool_object) = tool.as_object() else { + upstream_tools.push(tool); + continue; + }; + if tool_object.get("type").and_then(Value::as_str) != Some("namespace") { + upstream_tools.push(tool); + continue; + } + let Some(namespace_name) = tool_object.get("name").and_then(Value::as_str) else { + upstream_tools.push(tool); + continue; + }; + let Some(members) = tool_object.get("tools").and_then(Value::as_array) else { + upstream_tools.push(tool); + continue; + }; + + let function_members: Vec<&Value> = members + .iter() + .filter(|member| member.get("type").and_then(Value::as_str) == Some("function")) + .collect(); + if function_members.is_empty() { + upstream_tools.push(tool); + continue; + } + if raw_namespace_has_flat_name_collision(namespace_name, &function_members, &top_level_names) { + debug!( + namespace = %namespace_name, + "leaving raw namespace tool unflattened because a top-level tool uses a generated name" + ); + upstream_tools.push(tool); + continue; + } + + let mut emitted_namespace_members = false; + for member in &function_members { + let Some(member_name) = member.get("name").and_then(Value::as_str) else { + continue; + }; + let flat_name = model_visible_namespace_member_name(namespace_name, member_name); + debug!( + namespace = %namespace_name, + member = %member_name, + upstream_name = %flat_name, + "flattened raw namespace tool member for upstream" + ); + let mut upstream_member = (*member).clone(); + if let Some(member_object) = upstream_member.as_object_mut() { + member_object.insert("name".to_string(), Value::String(flat_name.clone())); + } + upstream_tools.push(upstream_member); + let member = NamespaceMemberName { + namespace: namespace_name.to_string(), + name: member_name.to_string(), + }; + let mapping = NamespaceCallMapping { + member, + upstream_name: flat_name.clone(), + strip_container_arguments: false, + }; + normalization.calls.insert(flat_name.clone(), mapping.clone()); + let legacy_name = legacy_model_visible_namespace_member_name(namespace_name, member_name); + record_alias_candidate(&mut legacy_member_candidates, legacy_name, mapping.clone()); + let alternate_name = alternate_model_visible_namespace_member_name(namespace_name, member_name); + record_alias_candidate(&mut alternate_member_candidates, alternate_name, mapping.clone()); + record_alias_candidate(&mut bare_member_candidates, member_name.to_string(), mapping); + emitted_namespace_members = true; + changed = true; + } + + if !emitted_namespace_members { + upstream_tools.push(tool); + continue; + } + + record_single_member_namespace_container_candidate( + &mut container_candidates, + namespace_name, + &function_members, + ); + } + + if changed { + register_unambiguous_aliases(normalization, legacy_member_candidates, &top_level_names); + register_unambiguous_aliases(normalization, alternate_member_candidates, &top_level_names); + register_unambiguous_aliases(normalization, bare_member_candidates, &top_level_names); + register_unambiguous_aliases(normalization, container_candidates, &top_level_names); + normalization.original_tools = Some(Value::Array(original_tools)); + *tools = upstream_tools; + } else { + *tools = original_tools; + } + changed +} + +fn strip_namespace_container_arguments(value: &mut Value) { + let Some(arguments) = value.as_object_mut().and_then(|object| object.get_mut("arguments")) else { + return; + }; + let Some(arguments_text) = arguments.as_str() else { + return; + }; + let Ok(mut parsed) = serde_json::from_str::(arguments_text) else { + return; + }; + let Some(object) = parsed.as_object_mut() else { + return; + }; + if object.remove("tools").is_some() { + *arguments = Value::String(serde_json::to_string(&parsed).unwrap_or_else(|_| arguments_text.to_string())); + } +} + +fn normalize_call_object(value: &mut Value, namespace: &NamespaceNormalization) -> bool { + let Some(object) = value.as_object_mut() else { + return false; + }; + if object.get("type").and_then(Value::as_str) != Some("function_call") { + return false; + } + let Some(name) = object.get("name").and_then(Value::as_str) else { + return false; + }; + if object.get("namespace").and_then(Value::as_str).is_some() { + return false; + } + let Some(mapping) = namespace.calls.get(name) else { + return false; + }; + let original_name = name.to_string(); + + object.insert("namespace".to_string(), Value::String(mapping.member.namespace.clone())); + object.insert("name".to_string(), Value::String(mapping.member.name.clone())); + if mapping.strip_container_arguments { + strip_namespace_container_arguments(value); + } + debug!( + upstream_name = %original_name, + namespace = %mapping.member.namespace, + member = %mapping.member.name, + stripped_container_arguments = mapping.strip_container_arguments, + "restored raw proxy namespace function call" + ); + true +} + +fn namespace_mapping_for_member<'a>( + normalization: &'a NamespaceNormalization, + namespace: &str, + name: &str, +) -> Option<&'a NamespaceCallMapping> { + normalization + .calls + .values() + .find(|mapping| mapping.member.namespace == namespace && mapping.member.name == name) +} + +fn rewrite_tool_choice_function_object( + function: &mut serde_json::Map, + normalization: &NamespaceNormalization, +) -> bool { + let explicit_namespace = function.get("namespace").and_then(Value::as_str).map(str::to_string); + let Some(name_text) = function.get("name").and_then(Value::as_str).map(str::to_string) else { + return false; + }; + let mapping = if let Some(namespace) = explicit_namespace.as_deref() { + namespace_mapping_for_member(normalization, namespace, &name_text) + } else { + normalization.calls.get(&name_text) + }; + let Some(mapping) = mapping else { + return false; + }; + + let mut changed = mapping.upstream_name != name_text; + if changed { + function.insert("name".to_string(), Value::String(mapping.upstream_name.clone())); + } + if explicit_namespace.is_some() { + changed |= function.remove("namespace").is_some(); + } + changed +} + +fn rewrite_tool_choice_for_upstream( + object: &mut serde_json::Map, + normalization: &NamespaceNormalization, +) -> bool { + let Some(tool_choice) = object.get_mut("tool_choice") else { + return false; + }; + let Some(choice_object) = tool_choice.as_object_mut() else { + return false; + }; + + if choice_object.get("type").and_then(Value::as_str) == Some("function") { + return rewrite_tool_choice_function_object(choice_object, normalization); + } + + choice_object + .get_mut("function") + .and_then(Value::as_object_mut) + .is_some_and(|function| rewrite_tool_choice_function_object(function, normalization)) +} + +fn restore_original_tools(value: &mut Value, namespace: &NamespaceNormalization) -> bool { + let Some(original_tools) = &namespace.original_tools else { + return false; + }; + let Some(object) = value.as_object_mut() else { + return false; + }; + if !object.get("tools").is_some_and(Value::is_array) { + return false; + } + object.insert("tools".to_string(), original_tools.clone()); + true +} + +fn normalize_response_value(value: &mut Value, namespace: &NamespaceNormalization) -> bool { + let mut changed = false; + changed |= restore_original_tools(value, namespace); + + if let Some(item) = value.as_object_mut().and_then(|object| object.get_mut("item")) { + changed |= normalize_call_object(item, namespace); + } + + changed |= normalize_call_object(value, namespace); + + for key in ["response", "payload"] { + if let Some(nested) = value.as_object_mut().and_then(|object| object.get_mut(key)) { + changed |= normalize_response_value(nested, namespace); + } + } + + if let Some(Value::Array(items)) = value.as_object_mut().and_then(|object| object.get_mut("output")) { + for item in items { + changed |= normalize_call_object(item, namespace); + } + } + + changed +} + +fn normalize_sse_line(line: &str, namespace: &NamespaceNormalization) -> String { + let Some(data) = line.strip_prefix("data: ") else { + return line.to_string(); + }; + if data == "[DONE]" { + return line.to_string(); + } + let Ok(mut value) = serde_json::from_str::(data) else { + return line.to_string(); + }; + if !normalize_response_value(&mut value, namespace) { + return line.to_string(); + } + serde_json::to_string(&value).map_or_else(|_| line.to_string(), |json| format!("data: {json}")) +} + +fn normalize_response_body(body: Bytes, namespace: &NamespaceNormalization) -> Bytes { + let Ok(mut value) = serde_json::from_slice::(&body) else { + return body; + }; + if !normalize_response_value(&mut value, namespace) { + return body; + } + serde_json::to_vec(&value).map_or(body, Bytes::from) +} + +fn normalize_sse_stream( + mut upstream: Pin> + Send>>, + namespace: NamespaceNormalization, +) -> Pin> + Send>> { + Box::pin(stream! { + let mut buffer = Vec::new(); + while let Some(chunk) = upstream.next().await { + let chunk = match chunk { + Ok(chunk) => chunk, + Err(error) => { + yield Err(error); + return; + } + }; + buffer.extend_from_slice(&chunk); + + while let Some(pos) = buffer.iter().position(|byte| *byte == b'\n') { + let line = buffer.drain(..=pos).collect::>(); + let line_end = if pos > 0 && line.get(pos - 1) == Some(&b'\r') { + pos - 1 + } else { + pos + }; + let Ok(raw_line) = std::str::from_utf8(&line[..line_end]) else { + yield Ok(Bytes::from(line)); + continue; + }; + let normalized = normalize_sse_line(raw_line, &namespace); + yield Ok(Bytes::from(format!("{normalized}\n"))); + } + } + + if !buffer.is_empty() { + let Ok(raw_line) = std::str::from_utf8(&buffer) else { + yield Ok(Bytes::from(buffer)); + return; + }; + let normalized = normalize_sse_line(raw_line, &namespace); + yield Ok(Bytes::from(normalized)); + } + }) +} + #[must_use] pub fn error_response(status: StatusCode, code: &str, message: &str) -> ProxyResponse { let body = serde_json::json!({ @@ -196,12 +859,16 @@ pub async fn proxy_get(path: &str, request_headers: &HeaderMap, state: &ProxySta } pub async fn proxy_request(request: ProxyRequest, state: &ProxyState) -> ProxyResponse { + let request_body_bytes = request.body.len(); let is_streaming = serde_json::from_slice::(&request.body) .ok() .and_then(|v| v.get("stream")?.as_bool()) .unwrap_or(false); let llm_headers = filter_request_headers(&request.headers, &state.config); + let normalized_request = normalize_proxy_request_body(request.body, &state.config); + let body = normalized_request.body; + let namespace = normalized_request.namespace; let base = state.config.llm_api_base.trim_end_matches('/'); let mut url = format!("{base}/v1/responses"); @@ -209,6 +876,14 @@ pub async fn proxy_request(request: ProxyRequest, state: &ProxyState) -> ProxyRe url.push('?'); url.push_str(q); } + debug!( + url = %url, + stream = is_streaming, + request_body_bytes, + normalized_body_bytes = body.len(), + has_namespace_normalization = !namespace.is_empty(), + "proxying responses request to upstream" + ); let client = if is_streaming { &state.stream_client @@ -216,7 +891,7 @@ pub async fn proxy_request(request: ProxyRequest, state: &ProxyState) -> ProxyRe &state.non_stream_client }; - let llm_resp = match client.post(&url).headers(llm_headers).body(request.body).send().await { + let llm_resp = match client.post(&url).headers(llm_headers).body(body).send().await { Ok(r) => r, Err(e) if e.is_timeout() => { warn!("LLM request timed out: {e}"); @@ -229,12 +904,26 @@ pub async fn proxy_request(request: ProxyRequest, state: &ProxyState) -> ProxyRe }; let status = StatusCode::from_u16(llm_resp.status().as_u16()).unwrap_or(StatusCode::BAD_GATEWAY); + let response_is_sse = is_sse_content_type(llm_resp.headers()); + debug!( + status = status.as_u16(), + response_is_sse, + has_namespace_normalization = !namespace.is_empty(), + "received upstream proxy response" + ); let mut response_headers = filter_response_headers(llm_resp.headers()); - if is_sse_content_type(llm_resp.headers()) { + if response_is_sse { response_headers.insert("x-accel-buffering", HeaderValue::from_static("no")); - let byte_stream = llm_resp.bytes_stream().map_err(std::io::Error::other); + let byte_stream: Pin> + Send>> = + Box::pin(llm_resp.bytes_stream().map_err(std::io::Error::other)); + let byte_stream = if namespace.is_empty() { + byte_stream + } else { + debug!("normalizing namespace calls in upstream SSE proxy response"); + normalize_sse_stream(byte_stream, namespace) + }; return ProxyResponse { status, @@ -243,7 +932,7 @@ pub async fn proxy_request(request: ProxyRequest, state: &ProxyState) -> ProxyRe }; } - let payload: Bytes = match llm_resp.bytes().await { + let mut payload: Bytes = match llm_resp.bytes().await { Ok(b) => b, Err(e) => { warn!("failed to read LLM response body: {e}"); @@ -254,6 +943,10 @@ pub async fn proxy_request(request: ProxyRequest, state: &ProxyState) -> ProxyRe ); } }; + if !namespace.is_empty() { + debug!("normalizing namespace calls in upstream JSON proxy response"); + payload = normalize_response_body(payload, &namespace); + } ProxyResponse { status, @@ -273,7 +966,9 @@ mod tests { openai_api_key: Some("test-key".to_owned()), llm_ready_timeout_s: 5.0, llm_ready_interval_s: 0.1, + skip_llm_ready_check: false, db_url: None, + model_aliases: std::collections::HashMap::new(), } } @@ -387,6 +1082,418 @@ mod tests { assert!(!filtered.contains_key("authorization")); } + #[test] + fn model_alias_rewrites_request_body() { + let mut config = test_config(); + config + .model_aliases + .insert("codex-auto-review".to_string(), "real-model".to_string()); + let body = Bytes::from_static(br#"{"model":"codex-auto-review","input":"hi","store":false}"#); + + let rewritten = normalize_request_body(body, &config); + let value: Value = serde_json::from_slice(&rewritten).unwrap(); + + assert_eq!(value["model"], "real-model"); + assert_eq!(value["input"], "hi"); + } + + #[test] + fn proxy_request_body_normalizes_developer_roles() { + let config = test_config(); + let body = Bytes::from_static( + br#"{"model":"test","input":[{"role":"developer","content":"rules"},{"role":"user","content":"hi"}],"store":false}"#, + ); + + let rewritten = normalize_request_body(body, &config); + let value: Value = serde_json::from_slice(&rewritten).unwrap(); + + assert_eq!(value["input"][0]["role"], "system"); + assert_eq!(value["input"][1]["role"], "user"); + } + + #[test] + fn proxy_request_body_moves_system_messages_to_front() { + let config = test_config(); + let body = Bytes::from_static( + br#"{"model":"test","input":[{"role":"user","content":"hi"},{"role":"developer","content":"rules"}],"store":false}"#, + ); + + let rewritten = normalize_request_body(body, &config); + let value: Value = serde_json::from_slice(&rewritten).unwrap(); + + assert_eq!(value["input"][0]["role"], "system"); + assert_eq!(value["input"][1]["role"], "user"); + } + + #[test] + fn proxy_request_body_moves_instructions_into_input() { + let config = test_config(); + let body = Bytes::from_static( + br#"{"model":"test","instructions":"rules","input":[{"role":"user","content":"hi"}],"store":false}"#, + ); + + let rewritten = normalize_request_body(body, &config); + let value: Value = serde_json::from_slice(&rewritten).unwrap(); + + assert!(value.get("instructions").is_none()); + assert_eq!(value["input"][0]["role"], "system"); + assert_eq!(value["input"][0]["content"][0]["text"], "rules"); + assert_eq!(value["input"][1]["role"], "user"); + } + + #[test] + fn proxy_request_body_moves_instructions_into_string_input() { + let config = test_config(); + let body = Bytes::from_static(br#"{"model":"test","instructions":"rules","input":"hi","store":false}"#); + + let rewritten = normalize_request_body(body, &config); + let value: Value = serde_json::from_slice(&rewritten).unwrap(); + + assert!(value.get("instructions").is_none()); + assert_eq!(value["input"][0]["role"], "system"); + assert_eq!(value["input"][0]["content"][0]["text"], "rules"); + assert_eq!(value["input"][1]["role"], "user"); + assert_eq!(value["input"][1]["content"][0]["text"], "hi"); + } + + #[test] + fn proxy_request_body_merges_system_messages() { + let config = test_config(); + let body = Bytes::from_static( + br#"{"model":"test","input":[{"role":"system","content":[{"type":"input_text","text":"rules 1"}]},{"role":"system","content":[{"type":"input_text","text":"rules 2"}]},{"role":"user","content":"hi"}],"store":false}"#, + ); + + let rewritten = normalize_request_body(body, &config); + let value: Value = serde_json::from_slice(&rewritten).unwrap(); + + assert_eq!(value["input"].as_array().unwrap().len(), 2); + assert_eq!(value["input"][0]["role"], "system"); + assert_eq!(value["input"][0]["content"][0]["text"], "rules 1"); + assert_eq!(value["input"][0]["content"][1]["text"], "rules 2"); + assert_eq!(value["input"][1]["role"], "user"); + } + + #[test] + fn proxy_request_body_flattens_namespace_tools_for_upstream() { + let config = test_config(); + let body = Bytes::from_static( + br#"{"model":"test","input":"hi","tools":[{"type":"namespace","name":"mcp__agentic_fixture","tools":[{"type":"function","name":"echo_text","parameters":{"type":"object"}},{"type":"function","name":"add_numbers","parameters":{"type":"object"}}]}],"store":false}"#, + ); + + let normalized = normalize_proxy_request_body(body, &config); + let value: Value = serde_json::from_slice(&normalized.body).unwrap(); + + assert_eq!(value["tools"].as_array().unwrap().len(), 2); + assert_eq!(value["tools"][0]["type"], "function"); + assert_eq!(value["tools"][0]["name"], "agentic_ns__mcp__agentic_fixture__echo_text"); + assert_eq!(value["tools"][1]["type"], "function"); + assert_eq!( + value["tools"][1]["name"], + "agentic_ns__mcp__agentic_fixture__add_numbers" + ); + assert!(normalized.namespace.original_tools.is_some()); + assert!( + normalized + .namespace + .calls + .contains_key("agentic_ns__mcp__agentic_fixture__echo_text") + ); + } + + #[test] + fn proxy_request_body_rewrites_tool_choice_for_flattened_namespace_tool() { + let config = test_config(); + let body = Bytes::from_static( + br#"{"model":"test","input":"hi","tools":[{"type":"namespace","name":"mcp__shell","tools":[{"type":"function","name":"run"}]}],"tool_choice":{"type":"function","name":"run"},"store":false}"#, + ); + + let normalized = normalize_proxy_request_body(body, &config); + let value: Value = serde_json::from_slice(&normalized.body).unwrap(); + + assert_eq!(value["tool_choice"]["type"], "function"); + assert_eq!(value["tool_choice"]["name"], "agentic_ns__mcp__shell__run"); + } + + #[test] + fn proxy_request_body_rewrites_namespaced_tool_choice_for_flattened_namespace_tool() { + let config = test_config(); + let body = Bytes::from_static( + br#"{"model":"test","input":"hi","tools":[{"type":"namespace","name":"mcp__shell","tools":[{"type":"function","name":"run"}]},{"type":"namespace","name":"mcp__git","tools":[{"type":"function","name":"run"}]}],"tool_choice":{"type":"function","namespace":"mcp__git","name":"run"},"store":false}"#, + ); + + let normalized = normalize_proxy_request_body(body, &config); + let value: Value = serde_json::from_slice(&normalized.body).unwrap(); + + assert_eq!(value["tool_choice"]["type"], "function"); + assert!(value["tool_choice"].get("namespace").is_none()); + assert_eq!(value["tool_choice"]["name"], "agentic_ns__mcp__git__run"); + } + + #[test] + fn proxy_request_body_does_not_flatten_namespace_member_over_top_level_name() { + let body = Bytes::from_static( + br#"{"model":"test","input":"hi","tools":[{"type":"function","name":"agentic_ns__mcp__shell__run"},{"type":"namespace","name":"mcp__shell","tools":[{"type":"function","name":"run"}]},{"type":"namespace","name":"mcp__git","tools":[{"type":"function","name":"status"}]}],"tool_choice":{"type":"function","namespace":"mcp__shell","name":"run"},"store":false}"#, + ); + + let normalized = normalize_proxy_request_body(body, &test_config()); + let value: Value = serde_json::from_slice(&normalized.body).unwrap(); + let tools = value["tools"].as_array().unwrap(); + let flat_function_count = tools + .iter() + .filter(|tool| tool.get("type").and_then(Value::as_str) == Some("function")) + .filter(|tool| tool.get("name").and_then(Value::as_str) == Some("agentic_ns__mcp__shell__run")) + .count(); + + assert_eq!(flat_function_count, 1); + assert!(tools.iter().any(|tool| { + tool.get("type").and_then(Value::as_str) == Some("namespace") + && tool.get("name").and_then(Value::as_str) == Some("mcp__shell") + })); + assert!(tools.iter().any(|tool| { + tool.get("type").and_then(Value::as_str) == Some("function") + && tool.get("name").and_then(Value::as_str) == Some("agentic_ns__mcp__git__status") + })); + assert_eq!(value["tool_choice"]["namespace"], "mcp__shell"); + assert_eq!(value["tool_choice"]["name"], "run"); + assert!(!normalized.namespace.calls.contains_key("agentic_ns__mcp__shell__run")); + } + + #[test] + fn proxy_request_body_keeps_colliding_namespace_whole_while_flattening_others() { + for shell_choice in ["status", "run"] { + let body = Bytes::from( + serde_json::to_vec(&serde_json::json!({ + "model": "test", + "input": "hi", + "tools": [ + {"type": "function", "name": "agentic_ns__mcp__shell__run"}, + { + "type": "namespace", + "name": "mcp__shell", + "tools": [ + {"type": "function", "name": "run"}, + {"type": "function", "name": "status"} + ] + }, + { + "type": "namespace", + "name": "mcp__git", + "tools": [{"type": "function", "name": "status"}] + } + ], + "tool_choice": { + "type": "function", + "namespace": "mcp__shell", + "name": shell_choice + }, + "store": false + })) + .unwrap(), + ); + + let normalized = normalize_proxy_request_body(body, &test_config()); + let value: Value = serde_json::from_slice(&normalized.body).unwrap(); + let tools = value["tools"].as_array().unwrap(); + let shell_namespace = tools + .iter() + .find(|tool| { + tool.get("type").and_then(Value::as_str) == Some("namespace") + && tool.get("name").and_then(Value::as_str) == Some("mcp__shell") + }) + .expect("shell namespace"); + let shell_member_names: Vec<&str> = shell_namespace["tools"] + .as_array() + .unwrap() + .iter() + .filter_map(|member| member.get("name").and_then(Value::as_str)) + .collect(); + + assert_eq!(shell_member_names, vec!["run", "status"]); + assert!(!tools.iter().any(|tool| { + tool.get("type").and_then(Value::as_str) == Some("function") + && tool.get("name").and_then(Value::as_str) == Some("agentic_ns__mcp__shell__status") + })); + assert!(tools.iter().any(|tool| { + tool.get("type").and_then(Value::as_str) == Some("function") + && tool.get("name").and_then(Value::as_str) == Some("agentic_ns__mcp__git__status") + })); + assert_eq!(value["tool_choice"]["namespace"], "mcp__shell"); + assert_eq!(value["tool_choice"]["name"], shell_choice); + assert!( + !normalized + .namespace + .calls + .values() + .any(|mapping| mapping.member.namespace == "mcp__shell") + ); + assert!(normalized.namespace.calls.contains_key("agentic_ns__mcp__git__status")); + } + } + + #[test] + fn proxy_sse_maps_flat_namespace_member_call() { + let config = test_config(); + let body = Bytes::from_static( + br#"{"tools":[{"type":"namespace","name":"mcp__agentic_fixture","tools":[{"type":"function","name":"echo_text"},{"type":"function","name":"add_numbers"}]}]}"#, + ); + let normalized_request = normalize_proxy_request_body(body, &config); + let line = r#"data: {"type":"response.output_item.done","item":{"type":"function_call","name":"agentic_ns__mcp__agentic_fixture__echo_text","call_id":"call_1","arguments":"{\"text\":\"hi\"}"}}"#; + + let normalized = normalize_sse_line(line, &normalized_request.namespace); + let value: Value = serde_json::from_str(normalized.strip_prefix("data: ").unwrap()).unwrap(); + + assert_eq!(value["item"]["namespace"], "mcp__agentic_fixture"); + assert_eq!(value["item"]["name"], "echo_text"); + assert_eq!(value["item"]["arguments"], "{\"text\":\"hi\"}"); + } + + #[test] + fn proxy_sse_does_not_namespace_non_function_call_item() { + let body = Bytes::from_static( + br#"{"tools":[{"type":"namespace","name":"mcp__agentic_fixture","tools":[{"type":"function","name":"run"}]}]}"#, + ); + let normalized_request = normalize_proxy_request_body(body, &test_config()); + let line = r#"data: {"type":"response.output_item.done","item":{"type":"custom_tool_call","name":"mcp__agentic_fixture.run","input":"patch"}}"#; + + let normalized = normalize_sse_line(line, &normalized_request.namespace); + let value: Value = serde_json::from_str(normalized.strip_prefix("data: ").unwrap()).unwrap(); + + assert!(value["item"].get("namespace").is_none()); + assert_eq!(value["item"]["name"], "mcp__agentic_fixture.run"); + assert_eq!(value["item"]["type"], "custom_tool_call"); + } + + #[test] + fn proxy_sse_flat_namespace_member_preserves_tools_argument() { + let config = test_config(); + let body = Bytes::from_static( + br#"{"tools":[{"type":"namespace","name":"mcp__agentic_fixture","tools":[{"type":"function","name":"run"}]}]}"#, + ); + let normalized_request = normalize_proxy_request_body(body, &config); + let line = r#"data: {"type":"response.output_item.done","item":{"type":"function_call","name":"agentic_ns__mcp__agentic_fixture__run","call_id":"call_1","arguments":"{\"tools\":\"legitimate\",\"cmd\":\"pwd\"}"}}"#; + + let normalized = normalize_sse_line(line, &normalized_request.namespace); + let value: Value = serde_json::from_str(normalized.strip_prefix("data: ").unwrap()).unwrap(); + + assert_eq!(value["item"]["namespace"], "mcp__agentic_fixture"); + assert_eq!(value["item"]["name"], "run"); + assert_eq!(value["item"]["arguments"], "{\"tools\":\"legitimate\",\"cmd\":\"pwd\"}"); + } + + #[test] + fn proxy_sse_maps_underscore_namespace_member_alias() { + let body = Bytes::from_static( + br#"{"tools":[{"type":"namespace","name":"mcp__agentic_fixture","tools":[{"type":"function","name":"echo_text"}]}]}"#, + ); + let normalized_request = normalize_proxy_request_body(body, &test_config()); + let line = r#"data: {"type":"response.output_item.done","item":{"type":"function_call","name":"mcp__agentic_fixture_echo_text","call_id":"call_1","arguments":"{\"text\":\"hi\"}"}}"#; + + let normalized = normalize_sse_line(line, &normalized_request.namespace); + let value: Value = serde_json::from_str(normalized.strip_prefix("data: ").unwrap()).unwrap(); + + assert_eq!(value["item"]["namespace"], "mcp__agentic_fixture"); + assert_eq!(value["item"]["name"], "echo_text"); + } + + #[test] + fn proxy_sse_ambiguous_underscore_namespace_member_alias_is_not_normalized() { + let body = Bytes::from_static( + br#"{"tools":[{"type":"namespace","name":"mcp__a_b","tools":[{"type":"function","name":"c"}]},{"type":"namespace","name":"mcp__a","tools":[{"type":"function","name":"b_c"}]}]}"#, + ); + let normalized_request = normalize_proxy_request_body(body, &test_config()); + let line = r#"data: {"type":"response.output_item.done","item":{"type":"function_call","name":"mcp__a_b_c","call_id":"call_1","arguments":"{}"}}"#; + + let normalized = normalize_sse_line(line, &normalized_request.namespace); + let value: Value = serde_json::from_str(normalized.strip_prefix("data: ").unwrap()).unwrap(); + + assert!(value["item"].get("namespace").is_none()); + assert_eq!(value["item"]["name"], "mcp__a_b_c"); + } + + #[test] + fn proxy_sse_maps_unambiguous_bare_namespace_member() { + let body = Bytes::from_static( + br#"{"tools":[{"type":"namespace","name":"mcp__agentic_fixture","tools":[{"type":"function","name":"run"}]}]}"#, + ); + let normalized_request = normalize_proxy_request_body(body, &test_config()); + let line = r#"data: {"type":"response.output_item.done","item":{"type":"function_call","name":"run","call_id":"call_1","arguments":"{\"cmd\":\"pwd\"}"}}"#; + + let normalized = normalize_sse_line(line, &normalized_request.namespace); + let value: Value = serde_json::from_str(normalized.strip_prefix("data: ").unwrap()).unwrap(); + + assert_eq!(value["item"]["namespace"], "mcp__agentic_fixture"); + assert_eq!(value["item"]["name"], "run"); + } + + #[test] + fn proxy_json_restores_original_tools_and_maps_flat_call() { + let config = test_config(); + let body = Bytes::from_static( + br#"{"tools":[{"type":"namespace","name":"mcp__agentic_fixture","tools":[{"type":"function","name":"echo_text"},{"type":"function","name":"add_numbers"}]}]}"#, + ); + let normalized_request = normalize_proxy_request_body(body, &config); + let upstream = Bytes::from_static( + br#"{"id":"resp_1","object":"response","tools":[{"type":"function","name":"agentic_ns__mcp__agentic_fixture__echo_text"}],"output":[{"type":"function_call","name":"agentic_ns__mcp__agentic_fixture__echo_text","call_id":"call_1","arguments":"{\"text\":\"hi\"}"}]}"#, + ); + + let normalized = normalize_response_body(upstream, &normalized_request.namespace); + let value: Value = serde_json::from_slice(&normalized).unwrap(); + + assert_eq!(value["tools"][0]["type"], "namespace"); + assert_eq!(value["tools"][0]["name"], "mcp__agentic_fixture"); + assert_eq!(value["output"][0]["namespace"], "mcp__agentic_fixture"); + assert_eq!(value["output"][0]["name"], "echo_text"); + } + + #[test] + fn proxy_sse_normalizes_namespace_container_call() { + let body = Bytes::from_static( + br#"{"tools":[{"type":"namespace","name":"mcp__agentic_fixture","tools":[{"type":"function","name":"run"}]}]}"#, + ); + let normalized_request = normalize_proxy_request_body(body, &test_config()); + let line = r#"data: {"type":"response.output_item.done","item":{"type":"function_call","name":"mcp__agentic_fixture","call_id":"call_1","arguments":"{\"tools\":\"opaque\",\"cmd\":\"pwd\"}"}}"#; + + let normalized = normalize_sse_line(line, &normalized_request.namespace); + let value: Value = serde_json::from_str(normalized.strip_prefix("data: ").unwrap()).unwrap(); + + assert_eq!(value["item"]["namespace"], "mcp__agentic_fixture"); + assert_eq!(value["item"]["name"], "run"); + assert_eq!(value["item"]["arguments"], "{\"cmd\":\"pwd\"}"); + } + + #[tokio::test] + async fn proxy_sse_stream_preserves_utf8_split_across_chunks() { + let body = Bytes::from_static( + br#"{"tools":[{"type":"namespace","name":"mcp__agentic_fixture","tools":[{"type":"function","name":"run"}]}]}"#, + ); + let normalized_request = normalize_proxy_request_body(body, &test_config()); + let snowman = "\u{2603}"; + let line = format!( + r#"data: {{"type":"response.output_item.done","item":{{"type":"function_call","name":"agentic_ns__mcp__agentic_fixture__run","call_id":"call_1","arguments":"{{\"text\":\"snow {snowman}\"}}"}}}}"# + ); + let bytes = format!("{line}\n").into_bytes(); + let split_at = bytes + .windows(snowman.len()) + .position(|window| window == snowman.as_bytes()) + .expect("snowman bytes present") + + 1; + let chunks = vec![ + Ok(Bytes::copy_from_slice(&bytes[..split_at])), + Ok(Bytes::copy_from_slice(&bytes[split_at..])), + ]; + let mut stream = normalize_sse_stream(Box::pin(futures::stream::iter(chunks)), normalized_request.namespace); + + let output = stream.next().await.expect("normalized line").expect("stream ok"); + assert!(stream.next().await.is_none()); + let text = String::from_utf8(output.to_vec()).expect("normalized line is utf8"); + assert!(!text.contains('\u{FFFD}')); + + let value: Value = serde_json::from_str(text.trim_end().strip_prefix("data: ").unwrap()).unwrap(); + assert_eq!(value["item"]["namespace"], "mcp__agentic_fixture"); + assert_eq!(value["item"]["name"], "run"); + assert_eq!(value["item"]["arguments"], format!(r#"{{"text":"snow {snowman}"}}"#)); + } + #[test] fn filter_response_headers_strips_hop_by_hop() { let mut headers = reqwest::header::HeaderMap::new(); diff --git a/crates/agentic-core/src/storage/models/item.rs b/crates/agentic-core/src/storage/models/item.rs index 54a9797..41f642b 100644 --- a/crates/agentic-core/src/storage/models/item.rs +++ b/crates/agentic-core/src/storage/models/item.rs @@ -1,5 +1,7 @@ //! Conversation history item stored in the database. +use serde::de::DeserializeOwned; +use serde_json::Value; use tracing::warn; use super::super::pool::{DbPool, DbResult, DbTransaction}; @@ -34,13 +36,13 @@ impl Item { /// Deserialize data column as `InputItem`. #[must_use] pub fn as_input(&self) -> Option { - deserialize_from_str_opt(&self.data) + self.deserialize_item_data() } /// Deserialize data column as `OutputItem`. #[must_use] pub fn as_output(&self) -> Option { - deserialize_from_str_opt(&self.data) + self.deserialize_item_data() } /// Deserialize data column as either `InputItem` or `OutputItem`. @@ -49,21 +51,31 @@ impl Item { if let Some(kind) = self.stored_item_kind() { match kind { ItemKind::Input => { - if let Some(input) = self.as_input().filter(|input| !matches!(input, InputItem::Unknown)) { + if let Some(input) = self.as_input() { return Some(InOutItem::Input(input)); } } ItemKind::Output => { - if let Some(output) = self.as_output().filter(|output| !matches!(output, OutputItem::Unknown)) { + if let Some(output) = self.as_output() { return Some(InOutItem::Output(output)); } } } } - match (self.as_input(), self.as_output()) { - (Some(input), _) if !matches!(input, InputItem::Unknown) => Some(InOutItem::Input(input)), - (_, Some(output)) if !matches!(output, OutputItem::Unknown) => Some(InOutItem::Output(output)), + let output = self.as_output(); + if output.as_ref().is_some_and(|item| !is_unknown_output(item)) { + return output.map(InOutItem::Output); + } + + let input = self.as_input(); + if input.as_ref().is_some_and(|item| !is_unknown_input(item)) { + return input.map(InOutItem::Input); + } + + match (input, output) { + (Some(input), _) => Some(InOutItem::Input(input)), + (_, Some(output)) => Some(InOutItem::Output(output)), _ => { warn!(item_id = %self.id, "unrecognized item type in stored data"); None @@ -72,9 +84,29 @@ impl Item { } fn stored_item_kind(&self) -> Option { - let value = deserialize_from_str_opt::(&self.data)?; + let value = deserialize_from_str_opt::(&self.data)?; ItemKind::from_stored_str(value.get(STORED_ITEM_KIND_KEY)?.as_str()?) } + + fn deserialize_item_data(&self) -> Option { + let mut value = deserialize_from_str_opt::(&self.data)?; + strip_stored_item_kind(&mut value); + serde_json::from_value(value).ok() + } +} + +fn strip_stored_item_kind(value: &mut Value) { + if let Value::Object(object) = value { + object.remove(STORED_ITEM_KIND_KEY); + } +} + +fn is_unknown_input(item: &InputItem) -> bool { + matches!(item, InputItem::Unknown(_)) +} + +fn is_unknown_output(item: &OutputItem) -> bool { + matches!(item, OutputItem::Unknown(_)) } /// Create items in a transaction with optional conversation context. @@ -158,7 +190,8 @@ pub async fn conversation_item_count(tx: &mut DbTransaction<'_>, conversation_id #[cfg(test)] mod tests { use super::*; - use crate::types::io::{OutputItem, ReasoningOutput, ReasoningTextContent}; + use crate::types::event::MessageStatus; + use crate::types::io::{InputItem, OutputItem, ReasoningOutput, ReasoningTextContent}; #[test] fn test_item_basic() { @@ -207,4 +240,137 @@ mod tests { Some(InOutItem::Output(OutputItem::Reasoning(_))) )); } + + #[test] + fn test_legacy_output_message_rehydrates_as_output_before_unknown_input() { + let item = Item { + id: "item_message".to_string(), + data: serde_json::json!({ + "type": "message", + "id": "msg_1", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "hello", "annotations": []}] + }) + .to_string(), + created_at: 1_704_067_200, + conversation_id: None, + seq: None, + }; + + let stored = item.as_inout().expect("stored item"); + assert!(matches!(stored, InOutItem::Output(OutputItem::Message(_)))); + + let inputs = InOutItem::into_input_items(vec![stored]); + assert!(matches!(inputs[0], InputItem::Message(_))); + } + + #[test] + fn test_namespaced_function_call_rehydrates_without_storage_marker() { + let stored = InOutItem::Output(OutputItem::FunctionCall(crate::types::io::FunctionToolCall { + id: "fc_1".to_string(), + call_id: "call_1".to_string(), + name: "run".to_string(), + namespace: Some("mcp__shell".to_string()), + arguments: "{\"cmd\":\"pwd\"}".to_string(), + status: MessageStatus::Completed, + })); + let item = Item { + id: "item_function_call".to_string(), + data: String::try_from(&stored).expect("serialization failed"), + created_at: 1_704_067_200, + conversation_id: None, + seq: None, + }; + + let inputs = InOutItem::into_input_items(vec![item.as_inout().expect("stored item")]); + let value = serde_json::to_value(&inputs[0]).expect("input value"); + + assert_eq!(value["type"], "function_call"); + assert_eq!(value["namespace"], "mcp__shell"); + assert_eq!(value["name"], "run"); + assert!(value.get(STORED_ITEM_KIND_KEY).is_none()); + + println!("namespace round-trip: mcp__shell.run -> storage -> input function_call"); + println!("storage marker stripped: _agentic_item_kind absent"); + } + + #[test] + fn test_multiple_namespaced_function_calls_rehydrate_without_storage_marker() { + let stored_items = [ + InOutItem::Output(OutputItem::FunctionCall(crate::types::io::FunctionToolCall { + id: "fc_1".to_string(), + call_id: "call_1".to_string(), + name: "run".to_string(), + namespace: Some("mcp__shell".to_string()), + arguments: "{\"cmd\":\"pwd\"}".to_string(), + status: MessageStatus::Completed, + })), + InOutItem::Output(OutputItem::FunctionCall(crate::types::io::FunctionToolCall { + id: "fc_2".to_string(), + call_id: "call_2".to_string(), + name: "run".to_string(), + namespace: Some("mcp__git".to_string()), + arguments: "{\"args\":[\"status\",\"--short\"]}".to_string(), + status: MessageStatus::Completed, + })), + ]; + let rows: Vec = stored_items + .iter() + .enumerate() + .map(|(idx, stored)| Item { + id: format!("item_function_call_{idx}"), + data: String::try_from(stored).expect("serialization failed"), + created_at: 1_704_067_200, + conversation_id: None, + seq: Some(idx.try_into().expect("seq")), + }) + .map(|item| item.as_inout().expect("stored item")) + .collect(); + + let inputs = InOutItem::into_input_items(rows); + let values = serde_json::to_value(&inputs).expect("input values"); + + assert_eq!(values[0]["type"], "function_call"); + assert_eq!(values[0]["namespace"], "mcp__shell"); + assert_eq!(values[0]["name"], "run"); + assert_eq!(values[0]["call_id"], "call_1"); + assert!(values[0].get(STORED_ITEM_KIND_KEY).is_none()); + + assert_eq!(values[1]["type"], "function_call"); + assert_eq!(values[1]["namespace"], "mcp__git"); + assert_eq!(values[1]["name"], "run"); + assert_eq!(values[1]["call_id"], "call_2"); + assert!(values[1].get(STORED_ITEM_KIND_KEY).is_none()); + + println!("namespace round-trip: mcp__shell.run -> call_1"); + println!("namespace round-trip: mcp__git.run -> call_2"); + println!("same tool name preserved under separate namespaces"); + } + + #[test] + fn test_raw_rehydrated_items_strip_storage_marker() { + let stored = InOutItem::Output(OutputItem::ToolSearchCall(serde_json::json!({ + "type": "tool_search_call", + "id": "ts_1", + "execution": "client" + }))); + let item = Item { + id: "item_tool_search".to_string(), + data: String::try_from(&stored).expect("serialization failed"), + created_at: 1_704_067_200, + conversation_id: None, + seq: None, + }; + + let inputs = InOutItem::into_input_items(vec![item.as_inout().expect("stored item")]); + let value = serde_json::to_value(&inputs[0]).expect("input value"); + + assert_eq!(value["type"], "tool_search_call"); + assert_eq!(value["execution"], "client"); + assert!(value.get(STORED_ITEM_KIND_KEY).is_none()); + + println!("raw item round-trip: tool_search_call execution=client"); + println!("storage marker stripped from raw item"); + } } diff --git a/crates/agentic-core/src/storage/types/item.rs b/crates/agentic-core/src/storage/types/item.rs index 85a5491..322b356 100644 --- a/crates/agentic-core/src/storage/types/item.rs +++ b/crates/agentic-core/src/storage/types/item.rs @@ -86,12 +86,14 @@ impl InOutItem { pub fn into_input_items(history: Vec) -> Vec { history .into_iter() - .filter_map(|i| match i { - InOutItem::Input(item) => Some(item), - InOutItem::Output(OutputItem::Message(msg)) => Some(InputItem::Message(msg.into())), - InOutItem::Output(OutputItem::Reasoning(r)) => Some(InputItem::Reasoning(r)), - InOutItem::Output(OutputItem::FunctionCall(f)) => Some(InputItem::FunctionCall(f)), - InOutItem::Output(OutputItem::Unknown) => None, + .map(|i| match i { + InOutItem::Input(item) => item, + InOutItem::Output(OutputItem::Message(msg)) => InputItem::Message(msg.into()), + InOutItem::Output(OutputItem::FunctionCall(call)) => InputItem::FunctionCall(call), + InOutItem::Output(OutputItem::ToolSearchCall(item)) => InputItem::ToolSearchCall(item), + InOutItem::Output(OutputItem::CustomToolCall(item)) => InputItem::CustomToolCall(item), + InOutItem::Output(OutputItem::Reasoning(r)) => InputItem::Reasoning(r), + InOutItem::Output(OutputItem::Unknown(item)) => InputItem::Unknown(item), }) .collect() } @@ -203,6 +205,7 @@ mod tests { id: "fc_1".to_string(), call_id: "call_abc".to_string(), name: "my_tool".to_string(), + namespace: None, arguments: "{}".to_string(), status: MessageStatus::Completed, }; diff --git a/crates/agentic-core/src/tool/mod.rs b/crates/agentic-core/src/tool/mod.rs index 6b9b49d..2966ec7 100644 --- a/crates/agentic-core/src/tool/mod.rs +++ b/crates/agentic-core/src/tool/mod.rs @@ -10,4 +10,9 @@ pub mod registry; pub use function::FunctionHandler; pub use handler::{GatewayExecutor, ToolError, ToolHandler, ToolOutput}; +pub use normalize::{ + alternate_model_visible_namespace_member_name, flatten_tool_choice_for_upstream, flatten_tools_for_upstream, + legacy_model_visible_namespace_member_name, model_visible_namespace_member_name, normalize_output_items_with_tools, + normalize_response_value_with_tools, +}; pub use registry::{ToolEntry, ToolRegistry, ToolType}; diff --git a/crates/agentic-core/src/tool/normalize.rs b/crates/agentic-core/src/tool/normalize.rs index de88a4b..f38dfb0 100644 --- a/crates/agentic-core/src/tool/normalize.rs +++ b/crates/agentic-core/src/tool/normalize.rs @@ -1,42 +1,59 @@ -use crate::types::io::FunctionTool; +use std::collections::HashSet; + +use serde_json::Value; + +use crate::types::event::MessageStatus; use crate::types::io::input::FunctionToolResultMessage; -use crate::types::tools::ResponsesTool; +use crate::types::io::{FunctionTool, FunctionToolCall, OutputItem, ToolChoice}; +use crate::types::tools::{ + CodexNamespaceMember, CodexNamespaceToolParam, FunctionToolParam, NonEmptyToolName, ResponsesTool, +}; use super::handler::ToolOutput; impl ResponsesTool { /// Normalise this tool declaration to the `FunctionTool` wire format that vLLM understands. /// - /// - `Function` variants convert via [`From<&FunctionToolParam>`] for `FunctionTool`. - /// Returns `None` and logs at `debug` level if the name is empty. - /// - All other variants (`Mcp`, `WebSearch`, `FileSearch`, `CodeInterpreter`) return - /// `None` and emit a `tracing::debug!` — their full handlers have not landed yet. - /// - /// This is the entry point called by `RequestPayload::to_upstream_request()` so that - /// vLLM always receives a `Vec`, never a raw `ResponsesTool` enum. + /// `Function` variants convert directly. Codex namespace members should be flattened first + /// with [`flatten_tools_for_upstream`], which turns each member into a `Function` variant. #[must_use] pub fn to_function_tool(&self) -> Option { match self { - // name is NonEmptyToolName — empty names are rejected by serde at - // deserialization time, so no runtime check is needed here. ResponsesTool::Function(p) => Some(FunctionTool::from(p)), ResponsesTool::Mcp(p) => { tracing::debug!( server_label = %p.server_label, - "MCP tool skipped in normalize — handler not yet registered" + "MCP tool skipped in normalize - handler not yet registered" ); None } ResponsesTool::WebSearch(_) => { - tracing::debug!("web_search tool skipped in normalize — handler not yet registered"); + tracing::debug!("web_search tool skipped in normalize - handler not yet registered"); None } ResponsesTool::FileSearch(_) => { - tracing::debug!("file_search tool skipped in normalize — handler not yet registered"); + tracing::debug!("file_search tool skipped in normalize - handler not yet registered"); None } ResponsesTool::CodeInterpreter(_) => { - tracing::debug!("code_interpreter tool skipped in normalize — handler not yet registered"); + tracing::debug!("code_interpreter tool skipped in normalize - handler not yet registered"); + None + } + ResponsesTool::Namespace(_) => { + tracing::debug!("namespace tool skipped in normalize - flatten_tools_for_upstream should run first"); + None + } + ResponsesTool::ToolSearch(_) => { + tracing::debug!("tool_search tool skipped in normalize - provider/client-owned"); + None + } + ResponsesTool::Custom(_) => { + tracing::debug!("custom tool skipped in normalize - client-owned raw tool"); + None + } + ResponsesTool::Unknown(value) => { + let tool_type = value.get("type").and_then(Value::as_str).unwrap_or(""); + tracing::debug!(tool_type, "unknown tool skipped in normalize"); None } } @@ -51,3 +68,744 @@ impl From for FunctionToolResultMessage { } } } + +pub const MODEL_VISIBLE_NAMESPACE_MEMBER_PREFIX: &str = "agentic_ns__"; + +#[must_use] +pub fn model_visible_namespace_member_name(namespace: &str, member: &str) -> String { + format!("{MODEL_VISIBLE_NAMESPACE_MEMBER_PREFIX}{namespace}__{member}") +} + +#[must_use] +pub fn legacy_model_visible_namespace_member_name(namespace: &str, member: &str) -> String { + format!("{namespace}.{member}") +} + +#[must_use] +pub fn alternate_model_visible_namespace_member_name(namespace: &str, member: &str) -> String { + legacy_model_visible_namespace_member_name(namespace, member).replace('.', "_") +} + +#[must_use] +pub fn flatten_tools_for_upstream(tools: Option<&[ResponsesTool]>) -> Option> { + tools.map(|tools| { + let top_level_names = top_level_tool_names(tools); + let mut upstream_tools = Vec::with_capacity(tools.len()); + for tool in tools { + match tool { + ResponsesTool::Namespace(namespace) => { + if namespace_has_flat_name_collision(namespace, &top_level_names) { + tracing::debug!( + namespace = %namespace.name, + "leaving namespace tool unflattened because a top-level tool uses a generated name" + ); + upstream_tools.push(tool.clone()); + continue; + } + + let mut emitted_members = false; + for member in &namespace.tools { + if let CodexNamespaceMember::Function(function) = member { + let flat_name_text = + model_visible_namespace_member_name(&namespace.name, function.name.as_str()); + let Ok(flat_name) = NonEmptyToolName::try_from(flat_name_text.clone()) else { + continue; + }; + tracing::debug!( + namespace = %namespace.name, + member = %function.name.as_str(), + upstream_name = %flat_name_text, + "flattened namespace tool member for upstream" + ); + let mut function = function.clone(); + function.name = flat_name; + upstream_tools.push(ResponsesTool::Function(function)); + emitted_members = true; + } + } + if !emitted_members { + tracing::debug!( + namespace = %namespace.name, + "leaving namespace tool unflattened because it has no function members" + ); + upstream_tools.push(tool.clone()); + } + } + ResponsesTool::Function(_) + | ResponsesTool::Mcp(_) + | ResponsesTool::WebSearch(_) + | ResponsesTool::FileSearch(_) + | ResponsesTool::CodeInterpreter(_) + | ResponsesTool::ToolSearch(_) + | ResponsesTool::Custom(_) + | ResponsesTool::Unknown(_) => upstream_tools.push(tool.clone()), + } + } + upstream_tools + }) +} + +pub fn normalize_output_items_with_tools(output: &mut [OutputItem], tools: Option<&[ResponsesTool]>) { + for item in output { + if let OutputItem::FunctionCall(call) = item { + normalize_function_call_with_tools(call, tools); + } + } +} + +pub fn normalize_response_value_with_tools(value: &mut Value, tools: Option<&[ResponsesTool]>) -> bool { + let Some(tools) = tools else { + return false; + }; + normalize_response_value_inner(value, tools) +} + +fn normalize_response_value_inner(value: &mut Value, tools: &[ResponsesTool]) -> bool { + let mut changed = false; + + if let Some(item) = value.as_object_mut().and_then(|object| object.get_mut("item")) { + changed |= normalize_call_value_with_tools(item, tools); + } + + changed |= normalize_call_value_with_tools(value, tools); + + for key in ["response", "payload"] { + if let Some(nested) = value.as_object_mut().and_then(|object| object.get_mut(key)) { + changed |= normalize_response_value_inner(nested, tools); + } + } + + if let Some(Value::Array(items)) = value.as_object_mut().and_then(|object| object.get_mut("output")) { + for item in items { + changed |= normalize_call_value_with_tools(item, tools); + } + } + + changed +} + +fn normalize_call_value_with_tools(value: &mut Value, tools: &[ResponsesTool]) -> bool { + let Some(object) = value.as_object_mut() else { + return false; + }; + if object.get("type").and_then(Value::as_str) != Some("function_call") { + return false; + } + if object.get("namespace").and_then(Value::as_str).is_some() { + return false; + } + let Some(name) = object.get("name").and_then(Value::as_str).map(str::to_string) else { + return false; + }; + + let had_arguments = object.contains_key("arguments"); + let arguments = object + .get("arguments") + .and_then(Value::as_str) + .unwrap_or_default() + .to_string(); + let original_name = name.clone(); + let original_arguments = arguments.clone(); + let mut call = FunctionToolCall { + id: object.get("id").and_then(Value::as_str).unwrap_or_default().to_string(), + call_id: object + .get("call_id") + .and_then(Value::as_str) + .unwrap_or_default() + .to_string(), + name, + namespace: None, + arguments, + status: MessageStatus::Completed, + }; + + normalize_function_call_with_tools(&mut call, Some(tools)); + if call.namespace.is_none() && call.name == original_name && call.arguments == original_arguments { + return false; + } + + object.insert("name".to_string(), Value::String(call.name)); + if let Some(namespace) = call.namespace { + object.insert("namespace".to_string(), Value::String(namespace)); + } + if had_arguments || call.arguments != original_arguments { + object.insert("arguments".to_string(), Value::String(call.arguments)); + } + true +} + +fn normalize_function_call_with_tools(call: &mut FunctionToolCall, tools: Option<&[ResponsesTool]>) { + if call.namespace.is_some() { + return; + } + let Some(tools) = tools else { + return; + }; + + if let Some((namespace, function)) = namespace_member_call(&call.name, tools) { + tracing::debug!( + upstream_name = %call.name, + namespace = %namespace.name, + member = %function.name.as_str(), + match_kind = "namespace_member", + "normalized upstream namespace function call" + ); + apply_namespace_member_call(call, namespace, function); + return; + } + + if let Some((namespace, function)) = namespace_container_call(&call.name, tools) { + tracing::debug!( + upstream_name = %call.name, + namespace = %namespace.name, + member = %function.name.as_str(), + match_kind = "namespace_container", + stripped_container_arguments = true, + "normalized upstream namespace function call" + ); + apply_namespace_member_call(call, namespace, function); + strip_namespace_container_arguments(&mut call.arguments); + return; + } + + if let Some((namespace, function)) = unambiguous_namespace_member_call(&call.name, tools) { + tracing::debug!( + upstream_name = %call.name, + namespace = %namespace.name, + member = %function.name.as_str(), + match_kind = "bare_member", + "normalized upstream namespace function call" + ); + apply_namespace_member_call(call, namespace, function); + } +} + +fn apply_namespace_member_call( + call: &mut FunctionToolCall, + namespace: &CodexNamespaceToolParam, + function: &FunctionToolParam, +) { + call.namespace = Some(namespace.name.clone()); + call.name = function.name.as_str().to_string(); +} + +fn record_unique_namespace_match<'a>( + found: &mut Option<(&'a CodexNamespaceToolParam, &'a FunctionToolParam)>, + ambiguous: &mut bool, + candidate: (&'a CodexNamespaceToolParam, &'a FunctionToolParam), +) { + if found.is_some() { + *ambiguous = true; + } else { + *found = Some(candidate); + } +} + +fn namespace_member_call<'a>( + call_name: &str, + tools: &'a [ResponsesTool], +) -> Option<(&'a CodexNamespaceToolParam, &'a FunctionToolParam)> { + if tools.iter().any(|tool| top_level_tool_named(tool, call_name)) { + return None; + } + + let mut exact = None; + let mut exact_ambiguous = false; + let mut legacy = None; + let mut legacy_ambiguous = false; + let mut alternate = None; + let mut alternate_ambiguous = false; + + for tool in tools { + let ResponsesTool::Namespace(namespace) = tool else { + continue; + }; + if namespace_flat_name_collides(namespace, tools) { + continue; + } + for member in &namespace.tools { + let CodexNamespaceMember::Function(function) = member else { + continue; + }; + if call_name == model_visible_namespace_member_name(&namespace.name, function.name.as_str()) { + record_unique_namespace_match(&mut exact, &mut exact_ambiguous, (namespace, function)); + } + if call_name == legacy_model_visible_namespace_member_name(&namespace.name, function.name.as_str()) { + record_unique_namespace_match(&mut legacy, &mut legacy_ambiguous, (namespace, function)); + } + if call_name == alternate_model_visible_namespace_member_name(&namespace.name, function.name.as_str()) { + record_unique_namespace_match(&mut alternate, &mut alternate_ambiguous, (namespace, function)); + } + } + } + + if !exact_ambiguous { + if let Some(found) = exact { + return Some(found); + } + } + if !legacy_ambiguous { + if let Some(found) = legacy { + return Some(found); + } + } + if alternate_ambiguous { None } else { alternate } +} + +fn namespace_member_call_by_namespace<'a>( + namespace_name: &str, + member_name: &str, + tools: &'a [ResponsesTool], +) -> Option<(&'a CodexNamespaceToolParam, &'a FunctionToolParam)> { + for tool in tools { + let ResponsesTool::Namespace(namespace) = tool else { + continue; + }; + if namespace.name != namespace_name { + continue; + } + for member in &namespace.tools { + let CodexNamespaceMember::Function(function) = member else { + continue; + }; + if function.name.as_str() == member_name { + return Some((namespace, function)); + } + } + } + None +} + +fn top_level_tool_named(tool: &ResponsesTool, name: &str) -> bool { + match tool { + ResponsesTool::Function(function) => function.name.as_str() == name, + ResponsesTool::Custom(custom) => custom.name == name, + ResponsesTool::Unknown(value) => value.get("name").and_then(Value::as_str) == Some(name), + ResponsesTool::Mcp(_) + | ResponsesTool::WebSearch(_) + | ResponsesTool::FileSearch(_) + | ResponsesTool::CodeInterpreter(_) + | ResponsesTool::Namespace(_) + | ResponsesTool::ToolSearch(_) => false, + } +} + +fn top_level_tool_names(tools: &[ResponsesTool]) -> HashSet { + tools + .iter() + .filter_map(|tool| match tool { + ResponsesTool::Function(function) => Some(function.name.as_str().to_string()), + ResponsesTool::Custom(custom) => Some(custom.name.clone()), + ResponsesTool::Unknown(value) => value.get("name").and_then(Value::as_str).map(str::to_string), + ResponsesTool::Mcp(_) + | ResponsesTool::WebSearch(_) + | ResponsesTool::FileSearch(_) + | ResponsesTool::CodeInterpreter(_) + | ResponsesTool::Namespace(_) + | ResponsesTool::ToolSearch(_) => None, + }) + .collect() +} + +fn namespace_has_flat_name_collision(namespace: &CodexNamespaceToolParam, top_level_names: &HashSet) -> bool { + namespace.tools.iter().any(|member| match member { + CodexNamespaceMember::Function(function) => top_level_names.contains(&model_visible_namespace_member_name( + &namespace.name, + function.name.as_str(), + )), + CodexNamespaceMember::Unknown(_) => false, + }) +} + +fn namespace_member_flat_name_collides( + namespace: &CodexNamespaceToolParam, + function: &FunctionToolParam, + tools: &[ResponsesTool], +) -> bool { + let flat_name = model_visible_namespace_member_name(&namespace.name, function.name.as_str()); + tools.iter().any(|tool| top_level_tool_named(tool, &flat_name)) +} + +fn namespace_flat_name_collides(namespace: &CodexNamespaceToolParam, tools: &[ResponsesTool]) -> bool { + namespace.tools.iter().any(|member| match member { + CodexNamespaceMember::Function(function) => namespace_member_flat_name_collides(namespace, function, tools), + CodexNamespaceMember::Unknown(_) => false, + }) +} + +fn namespace_container_call<'a>( + call_name: &str, + tools: &'a [ResponsesTool], +) -> Option<(&'a CodexNamespaceToolParam, &'a FunctionToolParam)> { + if tools.iter().any(|tool| top_level_tool_named(tool, call_name)) { + return None; + } + + let mut found = None; + let mut ambiguous = false; + for tool in tools { + let ResponsesTool::Namespace(namespace) = tool else { + continue; + }; + if call_name != namespace.name { + continue; + } + if namespace_flat_name_collides(namespace, tools) { + continue; + } + + let mut function_members = namespace.tools.iter().filter_map(|member| match member { + CodexNamespaceMember::Function(function) => Some(function), + CodexNamespaceMember::Unknown(_) => None, + }); + let Some(function) = function_members.next() else { + continue; + }; + if function_members.next().is_some() { + continue; + } + + record_unique_namespace_match(&mut found, &mut ambiguous, (namespace, function)); + } + + if ambiguous { None } else { found } +} + +fn unambiguous_namespace_member_call<'a>( + call_name: &str, + tools: &'a [ResponsesTool], +) -> Option<(&'a CodexNamespaceToolParam, &'a FunctionToolParam)> { + let mut found = None; + for tool in tools { + if top_level_tool_named(tool, call_name) { + return None; + } + let ResponsesTool::Namespace(namespace) = tool else { + continue; + }; + if namespace_flat_name_collides(namespace, tools) { + continue; + } + for member in &namespace.tools { + let CodexNamespaceMember::Function(function) = member else { + continue; + }; + if function.name.as_str() != call_name { + continue; + } + if found.is_some() { + return None; + } + found = Some((namespace, function)); + } + } + found +} + +fn strip_namespace_container_arguments(arguments: &mut String) { + let Ok(mut value) = serde_json::from_str::(arguments) else { + return; + }; + let Some(object) = value.as_object_mut() else { + return; + }; + if object.remove("tools").is_some() { + *arguments = serde_json::to_string(&value).unwrap_or_else(|_| std::mem::take(arguments)); + } +} + +#[must_use] +pub fn flatten_tool_choice_for_upstream(choice: &ToolChoice, tools: Option<&[ResponsesTool]>) -> ToolChoice { + let ToolChoice::Function { namespace, name } = choice else { + return choice.clone(); + }; + let Some(tools) = tools else { + return choice.clone(); + }; + + let resolved = if let Some(namespace) = namespace { + namespace_member_call_by_namespace(namespace, name, tools) + .filter(|(namespace, _function)| !namespace_flat_name_collides(namespace, tools)) + } else { + namespace_member_call(name, tools) + .or_else(|| namespace_container_call(name, tools)) + .or_else(|| unambiguous_namespace_member_call(name, tools)) + }; + let Some((namespace, function)) = resolved else { + return choice.clone(); + }; + + ToolChoice::Function { + namespace: None, + name: model_visible_namespace_member_name(&namespace.name, function.name.as_str()), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn completed_call(name: &str, arguments: &str) -> OutputItem { + OutputItem::FunctionCall(FunctionToolCall { + id: "fc_1".to_string(), + call_id: "call_1".to_string(), + name: name.to_string(), + namespace: None, + arguments: arguments.to_string(), + status: crate::types::event::MessageStatus::Completed, + }) + } + + #[test] + fn function_tool_choice_flattens_unambiguous_namespace_member() { + let tools: Vec = serde_json::from_value(serde_json::json!([ + { + "type": "namespace", + "name": "mcp__shell", + "tools": [{"type": "function", "name": "run"}] + } + ])) + .unwrap(); + let choice = ToolChoice::Function { + namespace: None, + name: "run".to_string(), + }; + + assert_eq!( + flatten_tool_choice_for_upstream(&choice, Some(&tools)), + ToolChoice::Function { + namespace: None, + name: "agentic_ns__mcp__shell__run".to_string() + } + ); + } + + #[test] + fn namespaced_function_tool_choice_flattens_exact_member() { + let tools: Vec = serde_json::from_value(serde_json::json!([ + { + "type": "namespace", + "name": "mcp__shell", + "tools": [{"type": "function", "name": "run"}] + }, + { + "type": "namespace", + "name": "mcp__git", + "tools": [{"type": "function", "name": "run"}] + } + ])) + .unwrap(); + let choice: ToolChoice = serde_json::from_value(serde_json::json!({ + "type": "function", + "namespace": "mcp__git", + "name": "run" + })) + .unwrap(); + + assert_eq!( + flatten_tool_choice_for_upstream(&choice, Some(&tools)), + ToolChoice::Function { + namespace: None, + name: "agentic_ns__mcp__git__run".to_string() + } + ); + } + + #[test] + fn flatten_tools_does_not_generate_colliding_namespace_member_name() { + let tools: Vec = serde_json::from_value(serde_json::json!([ + {"type": "function", "name": "agentic_ns__mcp__shell__run"}, + { + "type": "namespace", + "name": "mcp__shell", + "tools": [{"type": "function", "name": "run"}] + } + ])) + .unwrap(); + + let upstream = flatten_tools_for_upstream(Some(&tools)).expect("tools"); + let flat_function_count = upstream + .iter() + .filter(|tool| matches!(tool, ResponsesTool::Function(function) if function.name.as_str() == "agentic_ns__mcp__shell__run")) + .count(); + + assert_eq!(flat_function_count, 1); + assert!(upstream.iter().any(|tool| matches!( + tool, + ResponsesTool::Namespace(namespace) if namespace.name == "mcp__shell" + ))); + } + + #[test] + fn namespace_container_call_normalizes_to_member_call() { + let tools: Vec = serde_json::from_value(serde_json::json!([ + { + "type": "namespace", + "name": "mcp__agentic_fixture", + "tools": [ + {"type": "function", "name": "run", "parameters": {"type": "object"}} + ] + } + ])) + .unwrap(); + let mut output = vec![completed_call( + "mcp__agentic_fixture", + "{\"tools\":\"opaque\",\"cmd\":\"echo namespace fixture\"}", + )]; + + normalize_output_items_with_tools(&mut output, Some(&tools)); + + let OutputItem::FunctionCall(call) = &output[0] else { + panic!("expected function call"); + }; + assert_eq!(call.namespace.as_deref(), Some("mcp__agentic_fixture")); + assert_eq!(call.name, "run"); + assert_eq!(call.arguments, "{\"cmd\":\"echo namespace fixture\"}"); + } + + #[test] + fn flat_namespace_member_call_preserves_tools_argument() { + let tools: Vec = serde_json::from_value(serde_json::json!([ + { + "type": "namespace", + "name": "mcp__agentic_fixture", + "tools": [{"type": "function", "name": "run"}] + } + ])) + .unwrap(); + let mut output = vec![completed_call( + "agentic_ns__mcp__agentic_fixture__run", + "{\"tools\":\"legitimate\",\"cmd\":\"pwd\"}", + )]; + + normalize_output_items_with_tools(&mut output, Some(&tools)); + + let OutputItem::FunctionCall(call) = &output[0] else { + panic!("expected function call"); + }; + assert_eq!(call.namespace.as_deref(), Some("mcp__agentic_fixture")); + assert_eq!(call.name, "run"); + assert_eq!(call.arguments, "{\"tools\":\"legitimate\",\"cmd\":\"pwd\"}"); + } + + #[test] + fn plain_function_call_round_trip() { + let tools: Vec = serde_json::from_value(serde_json::json!([ + { + "type": "function", + "name": "get_weather", + "parameters": {"type": "object"} + } + ])) + .unwrap(); + let upstream = flatten_tools_for_upstream(Some(&tools)).expect("tools"); + let mut output = vec![completed_call("get_weather", "{\"city\":\"SF\"}")]; + + normalize_output_items_with_tools(&mut output, Some(&tools)); + + assert!(matches!( + upstream.as_slice(), + [ResponsesTool::Function(function)] if function.name.as_str() == "get_weather" + )); + let OutputItem::FunctionCall(call) = &output[0] else { + panic!("expected function call"); + }; + assert!(call.namespace.is_none()); + assert_eq!(call.name, "get_weather"); + assert_eq!(call.arguments, "{\"city\":\"SF\"}"); + } + + #[test] + fn underscore_namespace_member_alias_normalizes_to_member_call() { + let tools: Vec = serde_json::from_value(serde_json::json!([ + { + "type": "namespace", + "name": "mcp__agentic_fixture", + "tools": [{"type": "function", "name": "echo_text"}] + } + ])) + .unwrap(); + let mut output = vec![completed_call("mcp__agentic_fixture_echo_text", "{\"text\":\"hi\"}")]; + + normalize_output_items_with_tools(&mut output, Some(&tools)); + + let OutputItem::FunctionCall(call) = &output[0] else { + panic!("expected function call"); + }; + assert_eq!(call.namespace.as_deref(), Some("mcp__agentic_fixture")); + assert_eq!(call.name, "echo_text"); + } + + #[test] + fn ambiguous_underscore_namespace_member_alias_is_not_normalized() { + let tools: Vec = serde_json::from_value(serde_json::json!([ + { + "type": "namespace", + "name": "mcp__a_b", + "tools": [{"type": "function", "name": "c"}] + }, + { + "type": "namespace", + "name": "mcp__a", + "tools": [{"type": "function", "name": "b_c"}] + } + ])) + .unwrap(); + let mut output = vec![completed_call("mcp__a_b_c", "{}")]; + + normalize_output_items_with_tools(&mut output, Some(&tools)); + + let OutputItem::FunctionCall(call) = &output[0] else { + panic!("expected function call"); + }; + assert!(call.namespace.is_none()); + assert_eq!(call.name, "mcp__a_b_c"); + } + + #[test] + fn unambiguous_bare_namespace_member_normalizes_to_member_call() { + let tools: Vec = serde_json::from_value(serde_json::json!([ + { + "type": "namespace", + "name": "mcp__agentic_fixture", + "tools": [{"type": "function", "name": "run"}] + } + ])) + .unwrap(); + let mut output = vec![completed_call("run", "{\"cmd\":\"pwd\"}")]; + + normalize_output_items_with_tools(&mut output, Some(&tools)); + + let OutputItem::FunctionCall(call) = &output[0] else { + panic!("expected function call"); + }; + assert_eq!(call.namespace.as_deref(), Some("mcp__agentic_fixture")); + assert_eq!(call.name, "run"); + } + + #[test] + fn response_value_normalizes_nested_function_call_item() { + let tools: Vec = serde_json::from_value(serde_json::json!([ + { + "type": "namespace", + "name": "mcp__agentic_fixture", + "tools": [{"type": "function", "name": "add_numbers"}] + } + ])) + .unwrap(); + let mut value = serde_json::json!({ + "type": "response.output_item.done", + "item": { + "type": "function_call", + "name": "agentic_ns__mcp__agentic_fixture__add_numbers", + "call_id": "call_1", + "arguments": "{\"numbers\":[8,0]}" + } + }); + + assert!(normalize_response_value_with_tools(&mut value, Some(&tools))); + assert_eq!(value["item"]["namespace"], "mcp__agentic_fixture"); + assert_eq!(value["item"]["name"], "add_numbers"); + assert_eq!(value["item"]["arguments"], "{\"numbers\":[8,0]}"); + } +} diff --git a/crates/agentic-core/src/tool/registry.rs b/crates/agentic-core/src/tool/registry.rs index fd8d438..dcc83dd 100644 --- a/crates/agentic-core/src/tool/registry.rs +++ b/crates/agentic-core/src/tool/registry.rs @@ -111,6 +111,32 @@ impl ToolRegistry { }, ); } + ResponsesTool::Namespace(p) => { + // Codex namespace members are client-owned tools. The model + // sees flattened function names after normalization; until + // the executor owns a namespace-aware dispatch loop, unknown + // function calls still default to client-owned. + tracing::debug!( + namespace = %p.name, + "namespace tool declared but skipped in registry - client-owned Codex shape" + ); + } + ResponsesTool::ToolSearch(p) => { + tracing::debug!( + execution = ?p.execution, + "tool_search declared but skipped in registry - provider/client-owned Codex shape" + ); + } + ResponsesTool::Custom(p) => { + tracing::debug!( + name = %p.name, + "custom tool declared but skipped in registry - client-owned Codex shape" + ); + } + ResponsesTool::Unknown(value) => { + let tool_type = value.get("type").and_then(Value::as_str).unwrap_or(""); + tracing::debug!(tool_type, "unknown tool declared but skipped in registry"); + } } } diff --git a/crates/agentic-core/src/types/io/input.rs b/crates/agentic-core/src/types/io/input.rs index f5df8e2..848077b 100644 --- a/crates/agentic-core/src/types/io/input.rs +++ b/crates/agentic-core/src/types/io/input.rs @@ -1,4 +1,5 @@ -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_json::Value; use super::output::{FunctionToolCall, ReasoningOutput}; @@ -15,10 +16,9 @@ pub struct InputImageContent { /// Content item inside a message input. /// -/// Uses an internally-tagged enum — serde consumes `"type"` for the variant -/// discriminant so the inner structs must NOT redeclare a `type_` field. -/// `output_text` and `reasoning_text` reuse `InputTextContent` since they -/// carry only a `text` field; they are preserved so vLLM sees the full history. +/// Uses an internally-tagged enum. Unknown nested content deliberately fails +/// `InputMessage` deserialization so the containing `InputItem` can fall back +/// to raw JSON instead of silently dropping fields. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] pub enum InputContent { @@ -28,9 +28,6 @@ pub enum InputContent { OutputText(InputTextContent), /// Reasoning step text in rehydrated history. ReasoningText(InputTextContent), - /// Any other content type — drop silently. - #[serde(other)] - Unknown, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -52,21 +49,120 @@ pub struct FunctionToolResultMessage { pub output: String, } -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "type")] +#[derive(Debug, Clone)] pub enum InputItem { - #[serde(rename = "message")] Message(InputMessage), - /// The model's tool invocation — appears in rehydrated history so vLLM sees - /// the full call/output pair across turns. - #[serde(rename = "function_call")] FunctionCall(FunctionToolCall), - #[serde(rename = "function_call_output")] FunctionCallOutput(FunctionToolResultMessage), - #[serde(rename = "reasoning")] + ToolSearchCall(Value), + CustomToolCall(Value), Reasoning(ReasoningOutput), - #[serde(other)] - Unknown, + Unknown(Value), +} + +fn value_with_type(type_name: &str, value: &T) -> Result { + let mut value = serde_json::to_value(value)?; + if let Value::Object(map) = &mut value { + map.insert("type".to_string(), Value::String(type_name.to_string())); + } + Ok(value) +} + +fn raw_value_with_type(type_name: &str, value: &Value) -> Value { + let mut value = value.clone(); + if let Value::Object(map) = &mut value { + map.entry("type".to_string()) + .or_insert_with(|| Value::String(type_name.to_string())); + } + value +} + +fn serialize_typed(serializer: S, type_name: &str, value: &T) -> Result +where + S: Serializer, + T: Serialize, +{ + value_with_type(type_name, value) + .map_err(serde::ser::Error::custom)? + .serialize(serializer) +} + +impl Serialize for InputItem { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::Message(item) => serialize_typed(serializer, "message", item), + Self::FunctionCall(item) => serialize_typed(serializer, "function_call", item), + Self::FunctionCallOutput(item) => serialize_typed(serializer, "function_call_output", item), + Self::ToolSearchCall(item) => raw_value_with_type("tool_search_call", item).serialize(serializer), + Self::CustomToolCall(item) => raw_value_with_type("custom_tool_call", item).serialize(serializer), + Self::Reasoning(item) => serialize_typed(serializer, "reasoning", item), + Self::Unknown(item) => item.serialize(serializer), + } + } +} + +impl<'de> Deserialize<'de> for InputItem { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = Value::deserialize(deserializer)?; + let Some(type_name) = value.get("type").and_then(Value::as_str) else { + return Ok(Self::Unknown(value)); + }; + + match type_name { + "message" => Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::Message)), + "function_call" => { + Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::FunctionCall)) + } + "function_call_output" => { + Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::FunctionCallOutput)) + } + "tool_search_call" => Ok(Self::ToolSearchCall(value)), + "custom_tool_call" => Ok(Self::CustomToolCall(value)), + "reasoning" => Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::Reasoning)), + _ => Ok(Self::Unknown(value)), + } + } +} + +fn normalize_raw_message_role_for_upstream(value: &mut Value) { + let Some(object) = value.as_object_mut() else { + return; + }; + if object.get("role").and_then(Value::as_str) == Some("developer") { + object.insert("role".to_string(), Value::String("system".to_string())); + } +} + +impl InputItem { + fn is_system_role_for_upstream(&self) -> bool { + match self { + Self::Message(message) => message.role == "system", + Self::ToolSearchCall(value) | Self::CustomToolCall(value) | Self::Unknown(value) => value + .as_object() + .and_then(|object| object.get("role")) + .and_then(Value::as_str) + .is_some_and(|role| role == "system"), + Self::FunctionCall(_) | Self::FunctionCallOutput(_) | Self::Reasoning(_) => false, + } + } + + pub(crate) fn normalize_for_upstream(&mut self) { + match self { + Self::Message(message) if message.role == "developer" => { + message.role = "system".to_string(); + } + Self::ToolSearchCall(value) | Self::CustomToolCall(value) | Self::Unknown(value) => { + normalize_raw_message_role_for_upstream(value); + } + Self::Message(_) | Self::FunctionCall(_) | Self::FunctionCallOutput(_) | Self::Reasoning(_) => {} + } + } } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -75,3 +171,34 @@ pub enum ResponsesInput { Text(String), Items(Vec), } + +impl ResponsesInput { + pub(crate) fn prepend_system_text(&mut self, text: String) { + let system = InputItem::Message(InputMessage { + role: "system".to_string(), + content: InputMessageContent::Text(text), + }); + + match self { + Self::Text(user_text) => { + *self = Self::Items(vec![ + system, + InputItem::Message(InputMessage { + role: "user".to_string(), + content: InputMessageContent::Text(std::mem::take(user_text)), + }), + ]); + } + Self::Items(items) => items.insert(0, system), + } + } + + pub(crate) fn normalize_for_upstream(&mut self) { + if let Self::Items(items) = self { + for item in &mut *items { + item.normalize_for_upstream(); + } + items.sort_by_key(|item| !item.is_system_role_for_upstream()); + } + } +} diff --git a/crates/agentic-core/src/types/io/output.rs b/crates/agentic-core/src/types/io/output.rs index fa4a85e..abf1971 100644 --- a/crates/agentic-core/src/types/io/output.rs +++ b/crates/agentic-core/src/types/io/output.rs @@ -1,8 +1,9 @@ -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; use crate::events::EventPayload; use crate::executor::error::ExecutorError; +use crate::tool::{ToolRegistry, ToolType}; use crate::types::event::MessageStatus; use crate::utils::uuid7_str; @@ -79,9 +80,16 @@ impl From for InputMessage { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FunctionToolCall { + #[serde(default)] pub id: String, + #[serde(default)] pub call_id: String, + #[serde(default)] pub name: String, + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + pub namespace: Option, + #[serde(default)] pub arguments: String, #[serde(default = "default_completed_status")] #[serde(deserialize_with = "deserialize_status_or_default")] @@ -94,7 +102,7 @@ fn default_completed_status() -> MessageStatus { fn deserialize_status_or_default<'de, D>(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { let opt: Option = Option::deserialize(deserializer)?; Ok(opt.unwrap_or(MessageStatus::Completed)) @@ -105,7 +113,11 @@ impl TryFrom<&EventPayload> for FunctionToolCall { fn try_from(payload: &EventPayload) -> Result { let EventPayload::OutputItemAdded { - item_id, call_id, name, .. + item_id, + call_id, + name, + namespace, + .. } = payload else { return Err(ExecutorError::ParseError("expected OutputItemAdded payload".into())); @@ -119,6 +131,7 @@ impl TryFrom<&EventPayload> for FunctionToolCall { id, call_id: call_id.as_deref().unwrap_or_default().to_owned(), name: name.as_deref().unwrap_or_default().to_owned(), + namespace: namespace.clone(), arguments: String::new(), status: MessageStatus::InProgress, }) @@ -233,22 +246,103 @@ impl ApplyDone for FunctionToolCall { } } -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "type")] +#[derive(Debug, Clone)] pub enum OutputItem { - #[serde(rename = "message")] Message(OutputMessage), - #[serde(rename = "function_call")] FunctionCall(FunctionToolCall), - #[serde(rename = "reasoning")] + ToolSearchCall(Value), + CustomToolCall(Value), Reasoning(ReasoningOutput), - #[serde(other)] - Unknown, + Unknown(Value), +} + +fn value_with_type(type_name: &str, value: &T) -> Result { + let mut value = serde_json::to_value(value)?; + if let Value::Object(map) = &mut value { + map.insert("type".to_string(), Value::String(type_name.to_string())); + } + Ok(value) +} + +fn raw_value_with_type(type_name: &str, value: &Value) -> Value { + let mut value = value.clone(); + if let Value::Object(map) = &mut value { + map.entry("type".to_string()) + .or_insert_with(|| Value::String(type_name.to_string())); + } + value +} + +fn serialize_typed(serializer: S, type_name: &str, value: &T) -> Result +where + S: Serializer, + T: Serialize, +{ + value_with_type(type_name, value) + .map_err(serde::ser::Error::custom)? + .serialize(serializer) +} + +impl Serialize for OutputItem { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::Message(item) => serialize_typed(serializer, "message", item), + Self::FunctionCall(item) => serialize_typed(serializer, "function_call", item), + Self::ToolSearchCall(item) => raw_value_with_type("tool_search_call", item).serialize(serializer), + Self::CustomToolCall(item) => raw_value_with_type("custom_tool_call", item).serialize(serializer), + Self::Reasoning(item) => serialize_typed(serializer, "reasoning", item), + Self::Unknown(item) => item.serialize(serializer), + } + } +} + +impl<'de> Deserialize<'de> for OutputItem { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = Value::deserialize(deserializer)?; + let Some(type_name) = value.get("type").and_then(Value::as_str) else { + return Ok(Self::Unknown(value)); + }; + + match type_name { + "message" => Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::Message)), + "function_call" => { + Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::FunctionCall)) + } + "tool_search_call" => Ok(Self::ToolSearchCall(value)), + "custom_tool_call" => Ok(Self::CustomToolCall(value)), + "reasoning" => Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::Reasoning)), + _ => Ok(Self::Unknown(value)), + } + } +} + +impl OutputItem { + #[must_use] + pub fn requires_client_action(&self, registry: &ToolRegistry) -> bool { + match self { + Self::FunctionCall(call) => registry + .lookup(&call.name) + .is_none_or(|entry| entry.tool_type == ToolType::Function), + Self::ToolSearchCall(value) => value + .get("execution") + .and_then(Value::as_str) + .is_some_and(|execution| execution == "client"), + Self::CustomToolCall(_) => true, + Self::Message(_) | Self::Reasoning(_) | Self::Unknown(_) => false, + } + } } #[cfg(test)] mod tests { use super::*; + use crate::types::io::InputItem; #[test] fn reasoning_output_round_trips_through_serde() { @@ -274,7 +368,6 @@ mod tests { #[test] fn reasoning_input_round_trips_through_serde() { - use crate::types::io::input::InputItem; let reasoning = ReasoningOutput::new("rs_1"); let item = InputItem::Reasoning(reasoning); let json = serde_json::to_value(&item).unwrap(); @@ -307,4 +400,61 @@ mod tests { assert!(matches!(items[0], OutputItem::Reasoning(_))); assert!(matches!(items[1], OutputItem::Message(_))); } + + #[test] + fn codex_response_items_round_trip_raw_shapes() { + let function_call = serde_json::json!({ + "type": "function_call", + "id": "fc_1", + "call_id": "call_1", + "name": "run", + "namespace": "mcp__shell", + "arguments": "{\"cmd\":\"pwd\"}", + "status": "completed" + }); + let item: OutputItem = serde_json::from_value(function_call).unwrap(); + if let OutputItem::FunctionCall(call) = &item { + assert_eq!(call.namespace.as_deref(), Some("mcp__shell")); + assert_eq!(call.name, "run"); + } else { + panic!("expected function call"); + } + assert_eq!(serde_json::to_value(&item).unwrap()["namespace"], "mcp__shell"); + + let custom_call = serde_json::json!({ + "type": "custom_tool_call", + "id": "ctc_1", + "name": "apply_patch", + "input": "*** Begin Patch\n*** End Patch\n" + }); + let item: OutputItem = serde_json::from_value(custom_call).unwrap(); + assert!(matches!(item, OutputItem::CustomToolCall(_))); + assert_eq!( + serde_json::to_value(&item).unwrap()["input"], + "*** Begin Patch\n*** End Patch\n" + ); + + let unknown = serde_json::json!({"type": "new_item", "payload": {"a": 1}}); + let item: InputItem = serde_json::from_value(unknown).unwrap(); + assert!(matches!(item, InputItem::Unknown(_))); + assert_eq!(serde_json::to_value(&item).unwrap()["payload"]["a"], 1); + } + + #[test] + fn known_items_with_new_nested_shapes_fall_back_to_raw() { + let message = serde_json::json!({ + "type": "message", + "role": "user", + "content": [ + { + "type": "input_file", + "file_id": "file_1" + } + ] + }); + + let item: InputItem = serde_json::from_value(message).unwrap(); + assert!(matches!(item, InputItem::Unknown(_))); + assert_eq!(serde_json::to_value(&item).unwrap()["content"][0]["type"], "input_file"); + } } diff --git a/crates/agentic-core/src/types/io/tools.rs b/crates/agentic-core/src/types/io/tools.rs index 9b5c70e..8855c98 100644 --- a/crates/agentic-core/src/types/io/tools.rs +++ b/crates/agentic-core/src/types/io/tools.rs @@ -1,4 +1,4 @@ -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de, ser::SerializeMap}; use serde_json::Value; use crate::types::tools::ResponsesTool; @@ -13,19 +13,88 @@ pub struct FunctionTool { pub strict: Option, } -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] +#[derive(Debug, Clone, Default, PartialEq, Eq)] pub enum ToolChoice { #[default] Auto, None, Required, - #[serde(rename = "function")] Function { + namespace: Option, name: String, }, } +impl Serialize for ToolChoice { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::Auto => serializer.serialize_str("auto"), + Self::None => serializer.serialize_str("none"), + Self::Required => serializer.serialize_str("required"), + Self::Function { namespace, name } => { + let mut map = serializer.serialize_map(Some(2 + usize::from(namespace.is_some())))?; + map.serialize_entry("type", "function")?; + if let Some(namespace) = namespace { + map.serialize_entry("namespace", namespace)?; + } + map.serialize_entry("name", name)?; + map.end() + } + } + } +} + +impl<'de> Deserialize<'de> for ToolChoice { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = Value::deserialize(deserializer)?; + match value { + Value::String(choice) => match choice.as_str() { + "auto" => Ok(Self::Auto), + "none" => Ok(Self::None), + "required" => Ok(Self::Required), + other => Err(de::Error::unknown_variant( + other, + &["auto", "none", "required", "function"], + )), + }, + Value::Object(object) => { + if object.get("type").and_then(Value::as_str) == Some("function") { + let namespace = object.get("namespace").and_then(Value::as_str).map(str::to_string); + let name = object + .get("name") + .and_then(Value::as_str) + .ok_or_else(|| de::Error::missing_field("name"))?; + return Ok(Self::Function { + namespace, + name: name.to_string(), + }); + } + + if let Some(function) = object.get("function").and_then(Value::as_object) { + let namespace = function.get("namespace").and_then(Value::as_str).map(str::to_string); + let name = function + .get("name") + .and_then(Value::as_str) + .ok_or_else(|| de::Error::missing_field("name"))?; + return Ok(Self::Function { + namespace, + name: name.to_string(), + }); + } + + Err(de::Error::custom("expected tool_choice string or function object")) + } + _ => Err(de::Error::custom("expected tool_choice string or function object")), + } + } +} + /// Returns the effective tool list, preferring `request_tools` when explicitly /// set by the caller, otherwise falling back to the stored configuration. #[inline] diff --git a/crates/agentic-core/src/types/mod.rs b/crates/agentic-core/src/types/mod.rs index 8cdde1a..eb35c97 100644 --- a/crates/agentic-core/src/types/mod.rs +++ b/crates/agentic-core/src/types/mod.rs @@ -11,6 +11,7 @@ pub use io::{ }; pub use request_response::{IncompleteDetails, RequestPayload, ResponsePayload, UpstreamRequest}; pub use tools::{ - CodeInterpreterToolParam, EmptyToolNameError, FileSearchToolParam, FunctionToolParam, McpToolParam, - NonEmptyToolName, ResponsesTool, WebSearchToolParam, + CodeInterpreterToolParam, CodexCustomToolParam, CodexNamespaceMember, CodexNamespaceToolParam, + CodexToolSearchToolParam, EmptyToolNameError, FileSearchToolParam, FunctionToolParam, McpToolParam, + NonEmptyToolName, ResponsesTool, ToolSearchExecution, WebSearchToolParam, }; diff --git a/crates/agentic-core/src/types/request_response.rs b/crates/agentic-core/src/types/request_response.rs index 444a2ee..4a97559 100644 --- a/crates/agentic-core/src/types/request_response.rs +++ b/crates/agentic-core/src/types/request_response.rs @@ -1,4 +1,4 @@ -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; use super::io::{ @@ -7,7 +7,7 @@ use super::io::{ use super::tools::ResponsesTool; use crate::utils::common::serialize_to_string; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize)] pub struct RequestPayload { pub model: String, pub input: ResponsesInput, @@ -15,8 +15,9 @@ pub struct RequestPayload { pub previous_response_id: Option, pub conversation_id: Option, pub tools: Option>, - #[serde(default)] pub tool_choice: ToolChoice, + #[serde(skip)] + pub tool_choice_explicitly_set: bool, #[serde(default)] pub stream: bool, #[serde(default = "default_true")] @@ -33,6 +34,55 @@ fn default_true() -> bool { true } +impl<'de> Deserialize<'de> for RequestPayload { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct WireRequestPayload { + model: String, + input: ResponsesInput, + instructions: Option, + previous_response_id: Option, + conversation_id: Option, + tools: Option>, + tool_choice: Option, + #[serde(default)] + stream: bool, + #[serde(default = "default_true")] + store: bool, + include: Option>, + temperature: Option, + top_p: Option, + max_output_tokens: Option, + truncation: Option, + metadata: Option, + } + + let wire = WireRequestPayload::deserialize(deserializer)?; + let tool_choice_explicitly_set = wire.tool_choice.is_some(); + Ok(Self { + model: wire.model, + input: wire.input, + instructions: wire.instructions, + previous_response_id: wire.previous_response_id, + conversation_id: wire.conversation_id, + tools: wire.tools, + tool_choice: wire.tool_choice.unwrap_or_default(), + tool_choice_explicitly_set, + stream: wire.stream, + store: wire.store, + include: wire.include, + temperature: wire.temperature, + top_p: wire.top_p, + max_output_tokens: wire.max_output_tokens, + truncation: wire.truncation, + metadata: wire.metadata, + }) + } +} + #[derive(Debug, Serialize)] pub struct UpstreamRequest<'a> { pub model: &'a str, @@ -40,10 +90,9 @@ pub struct UpstreamRequest<'a> { pub stream: bool, #[serde(skip_serializing_if = "Option::is_none")] pub instructions: Option<&'a str>, - /// Normalised tools forwarded to vLLM — always `Vec` regardless of - /// what tool types the client declared. Gateway-managed types (`MCP`, `web_search`, …) - /// are normalized to function stubs; function tools pass through unchanged. - /// Skipped when empty so vLLM does not receive an empty array. + /// Normalized tools forwarded to vLLM. Namespace tools must be flattened + /// before this is built; non-function/provider/client-owned tools are not + /// sent on the typed executor path. #[serde(skip_serializing_if = "Option::is_none")] pub tools: Option>, #[serde(skip_serializing_if = "is_default_tool_choice")] @@ -67,20 +116,21 @@ fn is_default_tool_choice(choice: &ToolChoice) -> bool { } impl RequestPayload { + pub(crate) fn normalize_for_upstream(&mut self) { + if let Some(instructions) = self.instructions.take().filter(|instructions| !instructions.is_empty()) { + self.input.prepend_system_text(instructions); + } + self.input.normalize_for_upstream(); + } + /// Construct an `UpstreamRequest` suitable for forwarding to vLLM. - /// - /// All tool types are normalised to `Vec` via - /// [`ResponsesTool::to_function_tool`]. Gateway-managed tool types whose handlers - /// have not yet landed (`MCP`, `web_search`, `file_search`, `code_interpreter`) are skipped - /// with a warning — vLLM only understands `type: "function"`. #[must_use] pub fn to_upstream_request(&self, stream: bool) -> UpstreamRequest<'_> { let tools: Option> = self .tools .as_ref() .map(|tools| tools.iter().filter_map(ResponsesTool::to_function_tool).collect()); - // Treat an empty normalised list the same as no tools (skip the field entirely). - let tools = tools.filter(|v| !v.is_empty()); + let tools = tools.filter(|tools| !tools.is_empty()); UpstreamRequest { model: &self.model, @@ -140,3 +190,28 @@ impl From<&ResponsesInput> for Vec { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn request_payload_tracks_explicit_tool_choice_presence() { + let absent: RequestPayload = serde_json::from_value(serde_json::json!({ + "model": "test", + "input": "hi" + })) + .unwrap(); + assert_eq!(absent.tool_choice, ToolChoice::Auto); + assert!(!absent.tool_choice_explicitly_set); + + let explicit: RequestPayload = serde_json::from_value(serde_json::json!({ + "model": "test", + "input": "hi", + "tool_choice": "none" + })) + .unwrap(); + assert_eq!(explicit.tool_choice, ToolChoice::None); + assert!(explicit.tool_choice_explicitly_set); + } +} diff --git a/crates/agentic-core/src/types/tools/mod.rs b/crates/agentic-core/src/types/tools/mod.rs index dd63b8d..bde3adc 100644 --- a/crates/agentic-core/src/types/tools/mod.rs +++ b/crates/agentic-core/src/types/tools/mod.rs @@ -6,6 +6,7 @@ pub mod params; pub use params::{ - CodeInterpreterToolParam, EmptyToolNameError, FileSearchToolParam, FunctionToolParam, McpToolParam, - NonEmptyToolName, ResponsesTool, WebSearchToolParam, + CodeInterpreterToolParam, CodexCustomToolParam, CodexNamespaceMember, CodexNamespaceToolParam, + CodexToolSearchToolParam, EmptyToolNameError, FileSearchToolParam, FunctionToolParam, McpToolParam, + NonEmptyToolName, ResponsesTool, ToolSearchExecution, WebSearchToolParam, }; diff --git a/crates/agentic-core/src/types/tools/params.rs b/crates/agentic-core/src/types/tools/params.rs index e25a0a8..fb09b29 100644 --- a/crates/agentic-core/src/types/tools/params.rs +++ b/crates/agentic-core/src/types/tools/params.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; /// Error returned when a tool name is empty. @@ -66,46 +66,40 @@ impl std::fmt::Display for NonEmptyToolName { } } -// Request-side tool params (serde-enum-representation, api-non-exhaustive) - -/// Wire-compatible with the existing `{"type":"function",...}` format. -/// Replaces the `pub type ResponsesTool = FunctionTool` alias in `io/tools.rs`. +/// Request-side tool params. /// -/// Marked `#[non_exhaustive]` because the Responses API adds new tool types -/// (e.g. `computer_use_preview`). Downstream match arms must include a catch-all. +/// This enum covers the current generic tool framework plus Codex-specific +/// Responses shapes (`namespace`, `tool_search`, `custom`) and a raw fallback +/// so future provider tools do not make requests fail at the boundary. #[non_exhaustive] -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "type")] +#[derive(Debug, Clone)] pub enum ResponsesTool { - #[serde(rename = "function")] Function(FunctionToolParam), - - #[serde(rename = "mcp")] Mcp(McpToolParam), - - #[serde(rename = "web_search_preview")] WebSearch(WebSearchToolParam), - - #[serde(rename = "file_search")] FileSearch(FileSearchToolParam), - - #[serde(rename = "code_interpreter")] CodeInterpreter(CodeInterpreterToolParam), + Namespace(CodexNamespaceToolParam), + ToolSearch(CodexToolSearchToolParam), + Custom(CodexCustomToolParam), + Unknown(Value), } /// Parameters for a user-defined function tool. -/// -/// Does NOT carry a `type` field — serde consumes the tag during -/// deserialization and the payload struct must not also carry it. -/// -/// `name` is a [`NonEmptyToolName`]: serde rejects empty strings at -/// deserialization time, making the invalid state unrepresentable. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FunctionToolParam { pub name: NonEmptyToolName, + #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub parameters: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub strict: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub defer_loading: Option, + #[serde(default)] + #[serde(flatten)] + pub extra: HashMap, } /// Parameters for an MCP (Model Context Protocol) tool server. @@ -132,7 +126,169 @@ pub struct FileSearchToolParam { #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct CodeInterpreterToolParam {} -// Tests +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CodexNamespaceToolParam { + pub name: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(default)] + pub tools: Vec, + #[serde(default)] + #[serde(flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Clone)] +pub enum CodexNamespaceMember { + Function(FunctionToolParam), + Unknown(Value), +} + +impl Serialize for CodexNamespaceMember { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::Function(function) => serialize_with_type(serializer, "function", function), + Self::Unknown(value) => value.serialize(serializer), + } + } +} + +impl<'de> Deserialize<'de> for CodexNamespaceMember { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = Value::deserialize(deserializer)?; + if value.get("type").and_then(Value::as_str) == Some("function") { + return Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::Function)); + } + Ok(Self::Unknown(value)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ToolSearchExecution { + Server, + Client, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CodexToolSearchToolParam { + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub execution: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub parameters: Option, + #[serde(default)] + #[serde(flatten)] + pub extra: HashMap, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CodexCustomToolParam { + pub name: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub format: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub defer_loading: Option, + #[serde(default)] + #[serde(flatten)] + pub extra: HashMap, +} + +fn value_with_type(type_name: &str, value: &T) -> Result { + let mut value = serde_json::to_value(value)?; + if let Value::Object(map) = &mut value { + map.insert("type".to_string(), Value::String(type_name.to_string())); + } + Ok(value) +} + +fn serialize_with_type(serializer: S, type_name: &str, value: &T) -> Result +where + S: Serializer, + T: Serialize, +{ + value_with_type(type_name, value) + .map_err(serde::ser::Error::custom)? + .serialize(serializer) +} + +impl Serialize for ResponsesTool { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::Function(param) => serialize_with_type(serializer, "function", param), + Self::Mcp(param) => serialize_with_type(serializer, "mcp", param), + Self::WebSearch(param) => serialize_with_type(serializer, "web_search_preview", param), + Self::FileSearch(param) => serialize_with_type(serializer, "file_search", param), + Self::CodeInterpreter(param) => serialize_with_type(serializer, "code_interpreter", param), + Self::Namespace(param) => serialize_with_type(serializer, "namespace", param), + Self::ToolSearch(param) => serialize_with_type(serializer, "tool_search", param), + Self::Custom(param) => serialize_with_type(serializer, "custom", param), + Self::Unknown(value) => value.serialize(serializer), + } + } +} + +impl<'de> Deserialize<'de> for ResponsesTool { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = Value::deserialize(deserializer)?; + let Some(type_name) = value.get("type").and_then(Value::as_str) else { + return Ok(Self::Unknown(value)); + }; + + match type_name { + "function" => Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::Function)), + "mcp" => Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::Mcp)), + "web_search_preview" | "web_search" => { + Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::WebSearch)) + } + "file_search" => Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::FileSearch)), + "code_interpreter" => { + Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::CodeInterpreter)) + } + "namespace" => Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::Namespace)), + "tool_search" => Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::ToolSearch)), + "custom" => Ok(serde_json::from_value(value.clone()).map_or(Self::Unknown(value), Self::Custom)), + _ => Ok(Self::Unknown(value)), + } + } +} + +impl ResponsesTool { + #[must_use] + pub fn original_type(&self) -> Option<&str> { + match self { + Self::Function(_) => Some("function"), + Self::Mcp(_) => Some("mcp"), + Self::WebSearch(_) => Some("web_search_preview"), + Self::FileSearch(_) => Some("file_search"), + Self::CodeInterpreter(_) => Some("code_interpreter"), + Self::Namespace(_) => Some("namespace"), + Self::ToolSearch(_) => Some("tool_search"), + Self::Custom(_) => Some("custom"), + Self::Unknown(value) => value.get("type").and_then(Value::as_str), + } + } + + #[must_use] + pub fn to_raw_value(&self) -> Value { + serde_json::to_value(self).unwrap_or(Value::Null) + } +} #[cfg(test)] mod tests { @@ -169,7 +325,8 @@ mod tests { "type": "function", "name": "get_weather", "description": "Get weather for a city", - "parameters": {"type": "object", "properties": {"city": {"type": "string"}}} + "parameters": {"type": "object", "properties": {"city": {"type": "string"}}}, + "x-extra": "kept" }); let tool: ResponsesTool = serde_json::from_value(json).unwrap(); assert!(matches!(tool, ResponsesTool::Function(_))); @@ -179,6 +336,7 @@ mod tests { let back = serde_json::to_value(&tool).unwrap(); assert_eq!(back["type"], "function"); assert_eq!(back["name"], "get_weather"); + assert_eq!(back["x-extra"], "kept"); } #[test] @@ -199,43 +357,40 @@ mod tests { } #[test] - fn responses_tool_web_search_round_trips() { - let json = serde_json::json!({"type": "web_search_preview"}); - let tool: ResponsesTool = serde_json::from_value(json).unwrap(); - assert!(matches!(tool, ResponsesTool::WebSearch(_))); - assert_eq!(serde_json::to_value(&tool).unwrap()["type"], "web_search_preview"); - } - - #[test] - fn responses_tool_file_search_round_trips() { - let json = serde_json::json!({"type": "file_search", "vector_store_ids": ["vs_abc"]}); - let tool: ResponsesTool = serde_json::from_value(json).unwrap(); - assert!(matches!(tool, ResponsesTool::FileSearch(_))); - let back = serde_json::to_value(&tool).unwrap(); - assert_eq!(back["type"], "file_search"); - assert_eq!(back["vector_store_ids"][0], "vs_abc"); - } - - #[test] - fn responses_tool_code_interpreter_round_trips() { - let json = serde_json::json!({"type": "code_interpreter"}); - let tool: ResponsesTool = serde_json::from_value(json).unwrap(); - assert!(matches!(tool, ResponsesTool::CodeInterpreter(_))); - assert_eq!(serde_json::to_value(&tool).unwrap()["type"], "code_interpreter"); - } - - #[test] - fn mcp_tool_param_round_trips_with_headers() { - let json = serde_json::json!({ - "type": "mcp", - "server_label": "my_server", - "server_url": "http://localhost:9000", - "headers": {"Authorization": "Bearer tok"} - }); - let tool: ResponsesTool = serde_json::from_value(json).unwrap(); - assert_eq!( - serde_json::to_value(&tool).unwrap()["headers"]["Authorization"], - "Bearer tok" - ); + fn codex_tool_shapes_round_trip() { + let tools_json = serde_json::json!([ + { + "type": "namespace", + "name": "mcp__shell", + "tools": [ + {"type": "function", "name": "run", "parameters": {"type": "object"}} + ] + }, + { + "type": "tool_search", + "execution": "client", + "parameters": {"type": "object"} + }, + { + "type": "custom", + "name": "apply_patch", + "format": {"type": "grammar"}, + "defer_loading": true + }, + { + "type": "future_tool", + "opaque": true + } + ]); + + let tools: Vec = serde_json::from_value(tools_json).unwrap(); + assert!(matches!(tools[0], ResponsesTool::Namespace(_))); + assert!(matches!(tools[1], ResponsesTool::ToolSearch(_))); + assert!(matches!(tools[2], ResponsesTool::Custom(_))); + assert!(matches!(tools[3], ResponsesTool::Unknown(_))); + + let serialized = serde_json::to_value(&tools).unwrap(); + assert_eq!(serialized[0]["tools"][0]["type"], "function"); + assert_eq!(serialized[3]["opaque"], true); } } diff --git a/crates/agentic-core/tests/accumulator_cassette_test.rs b/crates/agentic-core/tests/accumulator_cassette_test.rs index cecc22e..b4abe7b 100644 --- a/crates/agentic-core/tests/accumulator_cassette_test.rs +++ b/crates/agentic-core/tests/accumulator_cassette_test.rs @@ -9,11 +9,12 @@ use serde::Deserialize; use agentic_core::executor::accumulator::ResponseAccumulator; use agentic_core::types::event::MessageStatus; -use agentic_core::types::io::OutputItem; +use agentic_core::types::io::{FunctionToolCall, OutputItem}; const CASSETTE_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/cassettes/events"); const TOOL_CALLS_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/cassettes/tool_calls"); const REASONING_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/cassettes/reasoning/responses"); +const CODEX_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/cassettes/codex"); // --- Legacy event cassette format --- @@ -57,6 +58,8 @@ struct Turn { struct TurnResponse { #[allow(dead_code)] headers: serde_yml::Value, + #[allow(dead_code)] + status_code: Option, #[serde(default)] sse: Vec, body: Option, @@ -76,6 +79,10 @@ fn load_reasoning_cassette(filename: &str) -> TurnCassette { load_turn_cassette_from(REASONING_DIR, filename) } +fn load_codex_cassette(filename: &str) -> TurnCassette { + load_turn_cassette_from(CODEX_DIR, filename) +} + /// Extracts `data: ...` lines from raw SSE entries (which may include /// `event:` lines and blank separators). fn extract_data_lines(sse_entries: &[String]) -> Vec { @@ -87,6 +94,70 @@ fn extract_data_lines(sse_entries: &[String]) -> Vec { .collect() } +fn response_body_from_data_line(line: &str) -> Option { + let data = line.strip_prefix("data: ")?; + if data == "[DONE]" { + return None; + } + let value: serde_json::Value = serde_json::from_str(data).ok()?; + if let Some(response) = value.get("response").and_then(serde_json::Value::as_object) { + return Some(serde_json::Value::Object(response.clone())); + } + if value.get("object").and_then(serde_json::Value::as_str) == Some("response") { + return Some(value); + } + None +} + +fn process_completed_response_object_from_sse( + cassette: &TurnCassette, + turn_idx: usize, + model: &str, +) -> Vec { + let data_lines = extract_data_lines(&cassette.turns[turn_idx].response.sse); + let response = data_lines + .iter() + .filter_map(|line| response_body_from_data_line(line)) + .find(|value| value.get("status").and_then(serde_json::Value::as_str) == Some("completed")) + .unwrap_or_else(|| panic!("turn {} must contain a completed response object", turn_idx + 1)); + let body = serde_json::to_string(&response).unwrap(); + let acc = ResponseAccumulator::from_json(&body, None).unwrap(); + let payload = acc.finalize(model, None, None); + assert_eq!(payload.status, "completed"); + payload.output +} + +fn process_codex_streaming_turn(cassette: &TurnCassette, turn_idx: usize, model: &str) -> Vec { + let data_lines = extract_data_lines(&cassette.turns[turn_idx].response.sse); + assert!( + !data_lines.is_empty(), + "Codex cassette turn {} must have SSE data lines", + turn_idx + 1 + ); + let acc = ResponseAccumulator::from_sse_lines(data_lines, None); + let payload = acc.finalize(model, None, None); + assert_eq!(payload.status, "completed"); + payload.output +} + +fn first_function_call(output: &[OutputItem]) -> &FunctionToolCall { + output + .iter() + .find_map(|item| { + if let OutputItem::FunctionCall(call) = item { + Some(call) + } else { + None + } + }) + .expect("output must contain a function call") +} + +fn turn_request_body(turn: &Turn) -> serde_json::Value { + let body = turn.request.get("body").expect("turn request must have body"); + serde_json::to_value(body).expect("request body must convert to JSON") +} + // === Legacy cassette tests === /// Feeds a real vLLM `function_call` SSE recording through the accumulator and @@ -469,6 +540,168 @@ fn test_reasoning_cassette_gpt_oss_streaming() { ); } +// === Codex integration cassettes === + +#[test] +fn test_codex_gateway_http_cassettes_parse_completed_response_objects() { + let cases = [ + ( + "codex-gateway-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + None, + "agentic_plain_echo", + ), + ( + "codex-gateway-http-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + Some("mcp__agentic_fixture"), + "add_numbers", + ), + ]; + + for (filename, expected_namespace, expected_name) in cases { + let cassette = load_codex_cassette(filename); + assert_eq!(cassette.turns.len(), 2, "{filename} should have two turns"); + + let output = process_completed_response_object_from_sse(&cassette, 0, "Qwen/Qwen3.6-35B-A3B"); + let call = first_function_call(&output); + assert_eq!(call.namespace.as_deref(), expected_namespace, "{filename} namespace"); + assert_eq!(call.name, expected_name, "{filename} function name"); + assert_eq!(call.status, MessageStatus::Completed); + assert!(!call.call_id.is_empty(), "{filename} call_id must be populated"); + assert!(!call.arguments.is_empty(), "{filename} arguments must be populated"); + } +} + +#[test] +fn test_codex_gateway_websocket_cassettes_preserve_function_and_namespace_calls() { + let cases = [ + ( + "codex-gateway-websocket-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + None, + "agentic_plain_echo", + ), + ( + "codex-gateway-websocket-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + Some("mcp__agentic_fixture"), + "add_numbers", + ), + ]; + + for (filename, expected_namespace, expected_name) in cases { + let cassette = load_codex_cassette(filename); + assert_eq!(cassette.turns.len(), 2, "{filename} should have two turns"); + + let output = process_codex_streaming_turn(&cassette, 0, "Qwen/Qwen3.6-35B-A3B"); + let call = first_function_call(&output); + assert_eq!(call.namespace.as_deref(), expected_namespace, "{filename} namespace"); + assert_eq!(call.name, expected_name, "{filename} function name"); + assert_eq!(call.status, MessageStatus::Completed); + } +} + +#[test] +fn test_codex_direct_vllm_http_cassettes_capture_upstream_tool_shapes() { + let cases = [ + ( + "codex-direct-vllm-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + None, + "agentic_plain_echo", + ), + ( + "codex-direct-vllm-http-flat-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + None, + "agentic_ns__mcp__agentic_fixture__add_numbers", + ), + ]; + + for (filename, expected_namespace, expected_name) in cases { + let cassette = load_codex_cassette(filename); + assert_eq!(cassette.turns.len(), 2, "{filename} should have two turns"); + + let output = process_codex_streaming_turn(&cassette, 0, "Qwen/Qwen3.6-35B-A3B"); + let call = first_function_call(&output); + assert_eq!(call.namespace.as_deref(), expected_namespace, "{filename} namespace"); + assert_eq!(call.name, expected_name, "{filename} function name"); + assert_eq!(call.status, MessageStatus::Completed); + } +} + +#[test] +fn test_codex_openai_baseline_cassettes_accept_namespace_on_http_and_websocket() { + let cases = [ + ( + "codex-openai-https-function-tool-gpt-4o-streaming.yaml", + None, + "agentic_plain_echo", + ), + ( + "codex-openai-https-namespace-tool-gpt-4o-streaming.yaml", + Some("mcp__agentic_fixture"), + "add_numbers", + ), + ( + "codex-openai-websocket-function-tool-gpt-4o-streaming.yaml", + None, + "agentic_plain_echo", + ), + ( + "codex-openai-websocket-namespace-tool-gpt-4o-streaming.yaml", + Some("mcp__agentic_fixture"), + "add_numbers", + ), + ]; + + for (filename, expected_namespace, expected_name) in cases { + let cassette = load_codex_cassette(filename); + assert_eq!(cassette.turns.len(), 2, "{filename} should have two turns"); + + let output = process_codex_streaming_turn(&cassette, 0, "gpt-4o"); + let call = first_function_call(&output); + assert_eq!(call.namespace.as_deref(), expected_namespace, "{filename} namespace"); + assert_eq!(call.name, expected_name, "{filename} function name"); + assert_eq!(call.status, MessageStatus::Completed); + } +} + +#[test] +fn test_codex_cassette_second_turns_are_tool_output_continuations() { + let cassettes = [ + "codex-direct-vllm-http-flat-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-direct-vllm-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-gateway-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-gateway-http-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-gateway-websocket-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-gateway-websocket-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-openai-https-function-tool-gpt-4o-streaming.yaml", + "codex-openai-https-namespace-tool-gpt-4o-streaming.yaml", + "codex-openai-websocket-function-tool-gpt-4o-streaming.yaml", + "codex-openai-websocket-namespace-tool-gpt-4o-streaming.yaml", + ]; + + for filename in cassettes { + let cassette = load_codex_cassette(filename); + assert_eq!(cassette.turns.len(), 2, "{filename} should have two turns"); + let turn2_body = turn_request_body(&cassette.turns[1]); + assert!( + turn2_body + .get("previous_response_id") + .and_then(serde_json::Value::as_str) + .is_some_and(|id| !id.is_empty()), + "{filename} turn 2 must include previous_response_id" + ); + + let input = turn2_body + .get("input") + .and_then(serde_json::Value::as_array) + .unwrap_or_else(|| panic!("{filename} turn 2 input must be an array")); + assert!( + input + .iter() + .any(|item| item.get("type").and_then(serde_json::Value::as_str) == Some("function_call_output")), + "{filename} turn 2 must include a function_call_output" + ); + } +} + // === Stateful multi-turn cassette tests (previous_response_id chaining) === // // These cassettes are recorded against gpt-oss-20b with `store=true` and diff --git a/crates/agentic-core/tests/cassettes/codex/codex-direct-vllm-http-flat-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml b/crates/agentic-core/tests/cassettes/codex/codex-direct-vllm-http-flat-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml new file mode 100644 index 0000000..923c368 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/codex-direct-vllm-http-flat-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml @@ -0,0 +1,1583 @@ +turns: +- filename: t1 + request: + body: + input: You must call agentic_ns__mcp__agentic_fixture__add_numbers with numbers + [8, 0] before answering. + model: Qwen/Qwen3.6-35B-A3B + store: true + stream: true + tools: + - description: Flattened form of mcp__agentic_fixture.add_numbers for direct + vLLM recording. + name: agentic_ns__mcp__agentic_fixture__add_numbers + parameters: + additionalProperties: false + properties: + numbers: + items: + type: number + minItems: 1 + type: array + required: + - numbers + type: object + strict: true + type: function + headers: + accept: '*/*' + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'event: response.created + + ' + - 'data: {"type":"response.created","sequence_number":0,"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988856,"error":null,"id":"resp_019f226a-ddbb-7ac1-988f-9165c8b31569","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"in_progress","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true,"type":"function","description":"Flattened + form of mcp__agentic_fixture.add_numbers for direct vLLM recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null}} + + ' + - ' + + ' + - 'event: response.in_progress + + ' + - 'data: {"type":"response.in_progress","sequence_number":1,"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988856,"error":null,"id":"resp_019f226a-ddbb-7ac1-988f-9165c8b31569","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"in_progress","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true,"type":"function","description":"Flattened + form of mcp__agentic_fixture.add_numbers for direct vLLM recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null}} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","summary":[],"type":"reasoning","content":[]},"output_index":0,"sequence_number":2} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":3,"delta":"The"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":4,"delta":" + user wants me"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":5,"delta":" + to call the"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":6,"delta":" + `agentic"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":7,"delta":"_ns__m"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":8,"delta":"cp__ag"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":9,"delta":"entic_fixture__"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":10,"delta":"add_numbers`"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":11,"delta":" + tool"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":12,"delta":" + with the argument"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":13,"delta":" + `numbers"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":14,"delta":"` + set"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":15,"delta":" + to `[8"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":16,"delta":", + 0"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":17,"delta":"]`.\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":18,"delta":"I + need"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":19,"delta":" + to make"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":20,"delta":" + this tool call"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":21,"delta":" + first,"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":22,"delta":" + and"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":23,"delta":" + then I"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":24,"delta":" + can answer."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":25,"delta":"\n\n1"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":26,"delta":". **"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":27,"delta":"Tool + Name"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":28,"delta":"**: + `"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":29,"delta":"agentic_ns"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":30,"delta":"__mcp"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":31,"delta":"__agentic"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":32,"delta":"_fixture__add"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":33,"delta":"_numbers`\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":34,"delta":"2. + "} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":35,"delta":" + **Arguments**:"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":36,"delta":" + `numbers"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":37,"delta":"`:"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":38,"delta":" + `[8,"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":39,"delta":" + 0]"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":40,"delta":"`\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":41,"delta":"3. + "} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":42,"delta":" + **Action**:"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":43,"delta":" + Call the tool"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":44,"delta":".\n\nAfter"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":45,"delta":" + the"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":46,"delta":" + tool returns"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":47,"delta":" + the"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":48,"delta":" + result ("} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":49,"delta":"which + should"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":50,"delta":" + be 8"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":51,"delta":" + +"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":52,"delta":" + 0 ="} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":53,"delta":" + 8),"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":54,"delta":" + I will provide"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":55,"delta":" + the answer."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":56,"delta":" + The"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":57,"delta":" + prompt explicitly"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":58,"delta":" + says \"You"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":59,"delta":" + must call ..."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":60,"delta":" + before answering\"."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":61,"delta":"\n\nLet''s"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":62,"delta":" + construct the tool"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":63,"delta":" + call.\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":64,"delta":"Tool"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":65,"delta":": + `ag"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":66,"delta":"entic_ns__"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":67,"delta":"mcp__"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":68,"delta":"agentic_fixture"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":69,"delta":"__add_numbers"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":70,"delta":"`\nParameters"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":71,"delta":": + `{\""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":72,"delta":"numbers\": + ["} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":73,"delta":"8, + "} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":74,"delta":"0]}`"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":75,"delta":"\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.done + + ' + - 'data: {"type":"response.reasoning_text.done","content_index":0,"item_id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","output_index":0,"sequence_number":76,"text":"The + user wants me to call the `agentic_ns__mcp__agentic_fixture__add_numbers` tool + with the argument `numbers` set to `[8, 0]`.\nI need to make this tool call + first, and then I can answer.\n\n1. **Tool Name**: `agentic_ns__mcp__agentic_fixture__add_numbers`\n2. **Arguments**: + `numbers`: `[8, 0]`\n3. **Action**: Call the tool.\n\nAfter the tool returns + the result (which should be 8 + 0 = 8), I will provide the answer. The prompt + explicitly says \"You must call ... before answering\".\n\nLet''s construct + the tool call.\nTool: `agentic_ns__mcp__agentic_fixture__add_numbers`\nParameters: + `{\"numbers\": [8, 0]}`\n"} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","summary":[],"type":"reasoning","content":[{"text":"The + user wants me to call the `agentic_ns__mcp__agentic_fixture__add_numbers` tool + with the argument `numbers` set to `[8, 0]`.\nI need to make this tool call + first, and then I can answer.\n\n1. **Tool Name**: `agentic_ns__mcp__agentic_fixture__add_numbers`\n2. **Arguments**: + `numbers`: `[8, 0]`\n3. **Action**: Call the tool.\n\nAfter the tool returns + the result (which should be 8 + 0 = 8), I will provide the answer. The prompt + explicitly says \"You must call ... before answering\".\n\nLet''s construct + the tool call.\nTool: `agentic_ns__mcp__agentic_fixture__add_numbers`\nParameters: + `{\"numbers\": [8, 0]}`\n","type":"reasoning_text"}]},"output_index":0,"sequence_number":77} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"role":"assistant","type":"message","content":[],"id":"msg_019f226a-e2aa-7b93-926c-d13351a23950","status":"in_progress"},"output_index":1,"sequence_number":78} + + ' + - ' + + ' + - 'event: response.content_part.added + + ' + - 'data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_019f226a-e2aa-7b93-926c-d13351a23950","output_index":1,"sequence_number":79,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"}} + + ' + - ' + + ' + - 'event: response.output_text.delta + + ' + - 'data: {"type":"response.output_text.delta","content_index":0,"item_id":"msg_019f226a-e2aa-7b93-926c-d13351a23950","output_index":1,"sequence_number":80,"delta":"\n\n","logprobs":[]} + + ' + - ' + + ' + - 'event: response.output_text.done + + ' + - 'data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_019f226a-e2aa-7b93-926c-d13351a23950","output_index":1,"sequence_number":81,"text":"\n\n","logprobs":[]} + + ' + - ' + + ' + - 'event: response.content_part.done + + ' + - 'data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_019f226a-e2aa-7b93-926c-d13351a23950","output_index":1,"sequence_number":82,"part":{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"role":"assistant","type":"message","content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-e2aa-7b93-926c-d13351a23950","status":"completed"},"output_index":1,"sequence_number":83} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"arguments":"","call_id":"call_28da9ef11c5a4f9c9f61b6c2","name":"agentic_ns__mcp__agentic_fixture__add_numbers","type":"function_call","id":"fc_019f226a-e2ab-7e13-a43c-6e355494e2be","status":"in_progress"},"output_index":2,"sequence_number":84} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","content_index":0,"item_id":"fc_019f226a-e2ab-7e13-a43c-6e355494e2be","output_index":2,"sequence_number":85,"delta":"{}"} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","content_index":0,"item_id":"fc_019f226a-e2ab-7e13-a43c-6e355494e2be","output_index":2,"sequence_number":86,"delta":"{"} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","content_index":0,"item_id":"fc_019f226a-e2ab-7e13-a43c-6e355494e2be","output_index":2,"sequence_number":87,"delta":"\"numbers\": + [8, 0]"} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","content_index":0,"item_id":"fc_019f226a-e2ab-7e13-a43c-6e355494e2be","output_index":2,"sequence_number":88,"delta":"}"} + + ' + - ' + + ' + - 'event: response.function_call_arguments.done + + ' + - 'data: {"type":"response.function_call_arguments.done","content_index":0,"item_id":"fc_019f226a-e2ab-7e13-a43c-6e355494e2be","output_index":2,"sequence_number":89,"arguments":"{\"numbers\": + [8, 0]}"} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"arguments":"{\"numbers\": + [8, 0]}","call_id":"call_28da9ef11c5a4f9c9f61b6c2","name":"agentic_ns__mcp__agentic_fixture__add_numbers","type":"function_call","id":"fc_019f226a-e2ab-7e13-a43c-6e355494e2be","status":"completed"},"output_index":2,"sequence_number":90} + + ' + - ' + + ' + - 'event: response.completed + + ' + - 'data: {"type":"response.completed","sequence_number":91,"response":{"background":false,"completed_at":1782988858,"conversation":null,"created_at":1782988856,"error":null,"id":"resp_019f226a-ddbb-7ac1-988f-9165c8b31569","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"id":"rs_019f226a-e284-7152-870e-2080ad8d5cfc","summary":[],"type":"reasoning","content":[{"text":"The + user wants me to call the `agentic_ns__mcp__agentic_fixture__add_numbers` tool + with the argument `numbers` set to `[8, 0]`.\nI need to make this tool call + first, and then I can answer.\n\n1. **Tool Name**: `agentic_ns__mcp__agentic_fixture__add_numbers`\n2. **Arguments**: + `numbers`: `[8, 0]`\n3. **Action**: Call the tool.\n\nAfter the tool returns + the result (which should be 8 + 0 = 8), I will provide the answer. The prompt + explicitly says \"You must call ... before answering\".\n\nLet''s construct + the tool call.\nTool: `agentic_ns__mcp__agentic_fixture__add_numbers`\nParameters: + `{\"numbers\": [8, 0]}`\n","type":"reasoning_text"}]},{"role":"assistant","type":"message","content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-e2aa-7b93-926c-d13351a23950","status":"completed"},{"arguments":"{\"numbers\": + [8, 0]}","call_id":"call_28da9ef11c5a4f9c9f61b6c2","name":"agentic_ns__mcp__agentic_fixture__add_numbers","namespace":null,"type":"function_call","id":"fc_019f226a-e2ab-7e13-a43c-6e355494e2be","status":"completed"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"completed","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true,"type":"function","description":"Flattened + form of mcp__agentic_fixture.add_numbers for direct vLLM recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":334,"input_tokens_details":{"cached_tokens":0},"output_tokens":229,"output_tokens_details":{"reasoning_tokens":175},"total_tokens":563},"user":null}} + + ' + - ' + + ' + - 'data: [DONE] + + ' + - ' + + ' + status_code: 200 +- filename: t2 + request: + body: + input: + - call_id: call_28da9ef11c5a4f9c9f61b6c2 + output: '{"sum":8,"count":2}' + type: function_call_output + - content: Use the tool output. Return only the sum. + role: user + type: message + model: Qwen/Qwen3.6-35B-A3B + previous_response_id: resp_019f226a-ddbb-7ac1-988f-9165c8b31569 + store: true + stream: true + tools: + - description: Flattened form of mcp__agentic_fixture.add_numbers for direct + vLLM recording. + name: agentic_ns__mcp__agentic_fixture__add_numbers + parameters: + additionalProperties: false + properties: + numbers: + items: + type: number + minItems: 1 + type: array + required: + - numbers + type: object + strict: true + type: function + headers: + accept: '*/*' + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'event: response.created + + ' + - 'data: {"type":"response.created","sequence_number":0,"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988858,"error":null,"id":"resp_019f226a-e32a-7101-98e5-ac4ac27cef62","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"previous_response_id":"resp_019f226a-ddbb-7ac1-988f-9165c8b31569","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"in_progress","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true,"type":"function","description":"Flattened + form of mcp__agentic_fixture.add_numbers for direct vLLM recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null}} + + ' + - ' + + ' + - 'event: response.in_progress + + ' + - 'data: {"type":"response.in_progress","sequence_number":1,"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988858,"error":null,"id":"resp_019f226a-e32a-7101-98e5-ac4ac27cef62","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"previous_response_id":"resp_019f226a-ddbb-7ac1-988f-9165c8b31569","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"in_progress","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true,"type":"function","description":"Flattened + form of mcp__agentic_fixture.add_numbers for direct vLLM recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null}} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"rs_019f226a-e629-7381-a319-1f008265bf93","summary":[],"type":"reasoning","content":[]},"output_index":0,"sequence_number":2} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":3,"delta":"The"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":4,"delta":" + user wants me"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":5,"delta":" + to use the"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":6,"delta":" + output from"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":7,"delta":" + the tool"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":8,"delta":" + call I"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":9,"delta":" + just made."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":10,"delta":"\nThe + tool"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":11,"delta":" + `agentic"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":12,"delta":"_ns__m"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":13,"delta":"cp__ag"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":14,"delta":"entic_fixture__"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":15,"delta":"add_numbers`"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":16,"delta":" + was called with"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":17,"delta":" + `numbers ["} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":18,"delta":"8, + "} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":19,"delta":"0]`."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":20,"delta":"\nThe + output"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":21,"delta":" + was `{\""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":22,"delta":"sum\":8"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":23,"delta":",\"count\":"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":24,"delta":"2}`."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":25,"delta":"\nThe + user"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":26,"delta":" + instruction"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":27,"delta":" + is \""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":28,"delta":"Return + only the"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":29,"delta":" + sum.\"\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":30,"delta":"The + sum in"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":31,"delta":" + the output is"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":32,"delta":" + 8."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":33,"delta":"\nI + should"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":34,"delta":" + return just the"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":35,"delta":" + number 8"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":36,"delta":".\nI"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":37,"delta":" + must"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":38,"delta":" + ensure"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":39,"delta":" + I follow"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":40,"delta":" + the constraint"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":41,"delta":" + \"Return only"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":42,"delta":" + the sum.\""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":43,"delta":" + strictly"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":44,"delta":".\n\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":45,"delta":"Therefore"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":46,"delta":", + the final"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":47,"delta":" + output"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":48,"delta":" + should be \""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":49,"delta":"8\".\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.done + + ' + - 'data: {"type":"response.reasoning_text.done","content_index":0,"item_id":"rs_019f226a-e629-7381-a319-1f008265bf93","output_index":0,"sequence_number":50,"text":"The + user wants me to use the output from the tool call I just made.\nThe tool `agentic_ns__mcp__agentic_fixture__add_numbers` + was called with `numbers [8, 0]`.\nThe output was `{\"sum\":8,\"count\":2}`.\nThe + user instruction is \"Return only the sum.\"\nThe sum in the output is 8.\nI + should return just the number 8.\nI must ensure I follow the constraint \"Return + only the sum.\" strictly.\n\nTherefore, the final output should be \"8\".\n"} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"rs_019f226a-e629-7381-a319-1f008265bf93","summary":[],"type":"reasoning","content":[{"text":"The + user wants me to use the output from the tool call I just made.\nThe tool `agentic_ns__mcp__agentic_fixture__add_numbers` + was called with `numbers [8, 0]`.\nThe output was `{\"sum\":8,\"count\":2}`.\nThe + user instruction is \"Return only the sum.\"\nThe sum in the output is 8.\nI + should return just the number 8.\nI must ensure I follow the constraint \"Return + only the sum.\" strictly.\n\nTherefore, the final output should be \"8\".\n","type":"reasoning_text"}]},"output_index":0,"sequence_number":51} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"role":"assistant","type":"message","content":[],"id":"msg_019f226a-e645-7630-bf02-78afd0497474","status":"in_progress"},"output_index":1,"sequence_number":52} + + ' + - ' + + ' + - 'event: response.content_part.added + + ' + - 'data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_019f226a-e645-7630-bf02-78afd0497474","output_index":1,"sequence_number":53,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"}} + + ' + - ' + + ' + - 'event: response.output_text.delta + + ' + - 'data: {"type":"response.output_text.delta","content_index":0,"item_id":"msg_019f226a-e645-7630-bf02-78afd0497474","output_index":1,"sequence_number":54,"delta":"\n\n8","logprobs":[]} + + ' + - ' + + ' + - 'event: response.output_text.done + + ' + - 'data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_019f226a-e645-7630-bf02-78afd0497474","output_index":1,"sequence_number":55,"text":"\n\n8","logprobs":[]} + + ' + - ' + + ' + - 'event: response.content_part.done + + ' + - 'data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_019f226a-e645-7630-bf02-78afd0497474","output_index":1,"sequence_number":56,"part":{"annotations":[],"logprobs":[],"text":"\n\n8","type":"output_text"}} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"role":"assistant","type":"message","content":[{"annotations":[],"logprobs":[],"text":"\n\n8","type":"output_text"}],"id":"msg_019f226a-e645-7630-bf02-78afd0497474","status":"completed"},"output_index":1,"sequence_number":57} + + ' + - ' + + ' + - 'event: response.completed + + ' + - 'data: {"type":"response.completed","sequence_number":58,"response":{"background":false,"completed_at":1782988858,"conversation":null,"created_at":1782988858,"error":null,"id":"resp_019f226a-e32a-7101-98e5-ac4ac27cef62","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"id":"rs_019f226a-e629-7381-a319-1f008265bf93","summary":[],"type":"reasoning","content":[{"text":"The + user wants me to use the output from the tool call I just made.\nThe tool `agentic_ns__mcp__agentic_fixture__add_numbers` + was called with `numbers [8, 0]`.\nThe output was `{\"sum\":8,\"count\":2}`.\nThe + user instruction is \"Return only the sum.\"\nThe sum in the output is 8.\nI + should return just the number 8.\nI must ensure I follow the constraint \"Return + only the sum.\" strictly.\n\nTherefore, the final output should be \"8\".\n","type":"reasoning_text"}]},{"role":"assistant","type":"message","content":[{"annotations":[],"logprobs":[],"text":"\n\n8","type":"output_text"}],"id":"msg_019f226a-e645-7630-bf02-78afd0497474","status":"completed"}],"parallel_tool_calls":true,"previous_response_id":"resp_019f226a-ddbb-7ac1-988f-9165c8b31569","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"completed","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true,"type":"function","description":"Flattened + form of mcp__agentic_fixture.add_numbers for direct vLLM recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":414,"input_tokens_details":{"cached_tokens":0},"output_tokens":124,"output_tokens_details":{"reasoning_tokens":112},"total_tokens":538},"user":null}} + + ' + - ' + + ' + - 'data: [DONE] + + ' + - ' + + ' + status_code: 200 diff --git a/crates/agentic-core/tests/cassettes/codex/codex-direct-vllm-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml b/crates/agentic-core/tests/cassettes/codex/codex-direct-vllm-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml new file mode 100644 index 0000000..b557f12 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/codex-direct-vllm-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml @@ -0,0 +1,2406 @@ +turns: +- filename: t1 + request: + body: + input: You must call the agentic_plain_echo tool with text exactly "plain fixture" + before answering. + model: Qwen/Qwen3.6-35B-A3B + store: true + stream: true + tools: + - description: Echo text for Codex cassette recording. + name: agentic_plain_echo + parameters: + additionalProperties: false + properties: + text: + type: string + required: + - text + type: object + strict: true + type: function + headers: + accept: '*/*' + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'event: response.created + + ' + - 'data: {"type":"response.created","sequence_number":0,"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988851,"error":null,"id":"resp_019f226a-caa0-7e31-8d97-6940665cc306","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"in_progress","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true,"type":"function","description":"Echo + text for Codex cassette recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null}} + + ' + - ' + + ' + - 'event: response.in_progress + + ' + - 'data: {"type":"response.in_progress","sequence_number":1,"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988851,"error":null,"id":"resp_019f226a-caa0-7e31-8d97-6940665cc306","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"in_progress","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true,"type":"function","description":"Echo + text for Codex cassette recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null}} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"rs_019f226a-d409-7590-ad03-977deb6d6603","summary":[],"type":"reasoning","content":[]},"output_index":0,"sequence_number":2} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":3,"delta":"Thinking"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":4,"delta":" + Process:\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":5,"delta":"1. + "} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":6,"delta":" + **Analyze"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":7,"delta":" + the input"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":8,"delta":":**"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":9,"delta":" + The user wants"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":10,"delta":" + me to call"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":11,"delta":" + the `ag"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":12,"delta":"entic_plain_echo"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":13,"delta":"` + tool with"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":14,"delta":" + the text \""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":15,"delta":"plain + fixture\""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":16,"delta":" + before"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":17,"delta":" + answering.\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":18,"delta":"2. + "} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":19,"delta":" + **Identify"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":20,"delta":" + constraints"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":21,"delta":":** + The instruction"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":22,"delta":" + explicitly requires"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":23,"delta":" + calling"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":24,"delta":" + the tool with"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":25,"delta":" + the exact text"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":26,"delta":" + \"plain fixture"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":27,"delta":"\"."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":28,"delta":"\n3."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":29,"delta":" **Check"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":30,"delta":" + available"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":31,"delta":" + tools:** `"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":32,"delta":"agentic_plain"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":33,"delta":"_echo` + is"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":34,"delta":" + available.\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":35,"delta":"4. + "} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":36,"delta":" + **Formulate"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":37,"delta":" + tool call:**"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":38,"delta":" + `"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":39,"delta":"agentic_plain"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":40,"delta":"_echo(text=\""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":41,"delta":"plain + fixture\")"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":42,"delta":"`"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":43,"delta":"\n5."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":44,"delta":" **Construct"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":45,"delta":" + response:** Call"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":46,"delta":" + the tool first"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":47,"delta":", + then answer"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":48,"delta":"."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":49,"delta":" + Wait"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":50,"delta":", + the prompt"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":51,"delta":" + says \"before"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":52,"delta":" + answering\", which"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":53,"delta":" + usually means I"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":54,"delta":" + should just execute"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":55,"delta":" + the tool call"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":56,"delta":" + and then provide"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":57,"delta":" + the answer"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":58,"delta":"/response"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":59,"delta":" + after"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":60,"delta":" + the tool output"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":61,"delta":","} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":62,"delta":" + or just output"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":63,"delta":" + the tool call"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":64,"delta":" + as"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":65,"delta":" + the first"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":66,"delta":" + step. Since"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":67,"delta":" + I''m"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":68,"delta":" + in"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":69,"delta":" + a single-turn"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":70,"delta":" + or"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":71,"delta":" + multi-turn setup"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":72,"delta":", + I will"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":73,"delta":" + output"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":74,"delta":" + the tool call"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":75,"delta":" + now"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":76,"delta":".\n\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":77,"delta":"Action"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":78,"delta":": + Call `"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":79,"delta":"agentic_plain"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":80,"delta":"_echo` + with"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":81,"delta":" + `text`"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":82,"delta":" + = \"plain"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":83,"delta":" + fixture\".\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":84,"delta":"Check"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":85,"delta":" + format"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":86,"delta":": + `{\""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":87,"delta":"name"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":88,"delta":"\": + \"ag"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":89,"delta":"entic_plain_echo"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":90,"delta":"\", + \"arguments"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":91,"delta":"\": + {\"text"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":92,"delta":"\": + \"plain"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":93,"delta":" + fixture\"}}`"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":94,"delta":"\nProceed"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":95,"delta":". + \nWait"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":96,"delta":", + let"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":97,"delta":"''s + verify the"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":98,"delta":" + tool"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":99,"delta":" + signature"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":100,"delta":": + `ag"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":101,"delta":"entic_plain_echo"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":102,"delta":"`"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":103,"delta":" + takes `text"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":104,"delta":"` + (string"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":105,"delta":"). + Matches"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":106,"delta":"."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":107,"delta":"\nOutput"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":108,"delta":" + the tool call"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":109,"delta":".\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":110,"delta":"No"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":111,"delta":" + further"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":112,"delta":" + thinking needed."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":113,"delta":" + Proceed"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":114,"delta":". + \nGenerating"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":115,"delta":" + tool call."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":116,"delta":" + \n`"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":117,"delta":"ag"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":118,"delta":"entic_plain_echo"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":119,"delta":"`"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":120,"delta":" + ->"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":121,"delta":" + `{\""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":122,"delta":"text\": + \""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":123,"delta":"plain + fixture\""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":124,"delta":"}`\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":125,"delta":"Done. + \n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":126,"delta":"Self"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":127,"delta":"-Correction"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":128,"delta":"/Verification + during"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":129,"delta":" + thought: The"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":130,"delta":" + instruction"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":131,"delta":" + is"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":132,"delta":" + strict"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":133,"delta":". + I will"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":134,"delta":" + just call the"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":135,"delta":" + tool.\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":136,"delta":"Output"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":137,"delta":" + matches the"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":138,"delta":" + required format."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":139,"delta":"\nProceeds"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":140,"delta":". + \n["} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":141,"delta":"Tool + Call Generation"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":142,"delta":"] + ->"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":143,"delta":" + `agentic"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":144,"delta":"_plain_echo(text"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":145,"delta":"=\"plain + fixture"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":146,"delta":"\")`\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":147,"delta":"Done. + \n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":148,"delta":"Output + matches expectation"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":149,"delta":". + \n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":150,"delta":"Proceeds + to"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":151,"delta":" + output"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":152,"delta":". + \n*("} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":153,"delta":"Self-Cor"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":154,"delta":"rection/Ref"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":155,"delta":"inement + during thought"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":156,"delta":")*: + I"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":157,"delta":" + should make"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":158,"delta":" + sure I"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":159,"delta":" + don''t add"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":160,"delta":" + extra"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":161,"delta":" + text before the"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":162,"delta":" + tool call."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":163,"delta":" + The prompt says"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":164,"delta":" + \"You must"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":165,"delta":" + call..."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":166,"delta":" + before answering.\""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":167,"delta":" + I will output"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":168,"delta":" + the tool call"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":169,"delta":" + immediately"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":170,"delta":".\nDone"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":171,"delta":". + \n["} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":172,"delta":"Output + Generation]"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":173,"delta":" + -> Tool"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":174,"delta":" + call. \n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":175,"delta":"Wait"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":176,"delta":", + sometimes"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":177,"delta":" + the system expects"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":178,"delta":" + just the tool"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":179,"delta":" + call block."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":180,"delta":" + I will generate"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":181,"delta":" + it. \n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":182,"delta":"`"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":183,"delta":"\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.done + + ' + - 'data: {"type":"response.reasoning_text.done","content_index":0,"item_id":"rs_019f226a-d409-7590-ad03-977deb6d6603","output_index":0,"sequence_number":184,"text":"Thinking + Process:\n1. **Analyze the input:** The user wants me to call the `agentic_plain_echo` + tool with the text \"plain fixture\" before answering.\n2. **Identify constraints:** + The instruction explicitly requires calling the tool with the exact text \"plain + fixture\".\n3. **Check available tools:** `agentic_plain_echo` is available.\n4. **Formulate + tool call:** `agentic_plain_echo(text=\"plain fixture\")`\n5. **Construct response:** + Call the tool first, then answer. Wait, the prompt says \"before answering\", + which usually means I should just execute the tool call and then provide the + answer/response after the tool output, or just output the tool call as the first + step. Since I''m in a single-turn or multi-turn setup, I will output the tool + call now.\n\nAction: Call `agentic_plain_echo` with `text` = \"plain fixture\".\nCheck + format: `{\"name\": \"agentic_plain_echo\", \"arguments\": {\"text\": \"plain + fixture\"}}`\nProceed. \nWait, let''s verify the tool signature: `agentic_plain_echo` + takes `text` (string). Matches.\nOutput the tool call.\nNo further thinking + needed. Proceed. \nGenerating tool call. \n`agentic_plain_echo` -> `{\"text\": + \"plain fixture\"}`\nDone. \nSelf-Correction/Verification during thought: The + instruction is strict. I will just call the tool.\nOutput matches the required + format.\nProceeds. \n[Tool Call Generation] -> `agentic_plain_echo(text=\"plain + fixture\")`\nDone. \nOutput matches expectation. \nProceeds to output. \n*(Self-Correction/Refinement + during thought)*: I should make sure I don''t add extra text before the tool + call. The prompt says \"You must call... before answering.\" I will output the + tool call immediately.\nDone. \n[Output Generation] -> Tool call. \nWait, sometimes + the system expects just the tool call block. I will generate it. \n`\n"} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"rs_019f226a-d409-7590-ad03-977deb6d6603","summary":[],"type":"reasoning","content":[{"text":"Thinking + Process:\n1. **Analyze the input:** The user wants me to call the `agentic_plain_echo` + tool with the text \"plain fixture\" before answering.\n2. **Identify constraints:** + The instruction explicitly requires calling the tool with the exact text \"plain + fixture\".\n3. **Check available tools:** `agentic_plain_echo` is available.\n4. **Formulate + tool call:** `agentic_plain_echo(text=\"plain fixture\")`\n5. **Construct response:** + Call the tool first, then answer. Wait, the prompt says \"before answering\", + which usually means I should just execute the tool call and then provide the + answer/response after the tool output, or just output the tool call as the first + step. Since I''m in a single-turn or multi-turn setup, I will output the tool + call now.\n\nAction: Call `agentic_plain_echo` with `text` = \"plain fixture\".\nCheck + format: `{\"name\": \"agentic_plain_echo\", \"arguments\": {\"text\": \"plain + fixture\"}}`\nProceed. \nWait, let''s verify the tool signature: `agentic_plain_echo` + takes `text` (string). Matches.\nOutput the tool call.\nNo further thinking + needed. Proceed. \nGenerating tool call. \n`agentic_plain_echo` -> `{\"text\": + \"plain fixture\"}`\nDone. \nSelf-Correction/Verification during thought: The + instruction is strict. I will just call the tool.\nOutput matches the required + format.\nProceeds. \n[Tool Call Generation] -> `agentic_plain_echo(text=\"plain + fixture\")`\nDone. \nOutput matches expectation. \nProceeds to output. \n*(Self-Correction/Refinement + during thought)*: I should make sure I don''t add extra text before the tool + call. The prompt says \"You must call... before answering.\" I will output the + tool call immediately.\nDone. \n[Output Generation] -> Tool call. \nWait, sometimes + the system expects just the tool call block. I will generate it. \n`\n","type":"reasoning_text"}]},"output_index":0,"sequence_number":185} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"role":"assistant","type":"message","content":[],"id":"msg_019f226a-d45e-7290-8352-51a58b66ebcb","status":"in_progress"},"output_index":1,"sequence_number":186} + + ' + - ' + + ' + - 'event: response.content_part.added + + ' + - 'data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_019f226a-d45e-7290-8352-51a58b66ebcb","output_index":1,"sequence_number":187,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"}} + + ' + - ' + + ' + - 'event: response.output_text.delta + + ' + - 'data: {"type":"response.output_text.delta","content_index":0,"item_id":"msg_019f226a-d45e-7290-8352-51a58b66ebcb","output_index":1,"sequence_number":188,"delta":"\n\n","logprobs":[]} + + ' + - ' + + ' + - 'event: response.output_text.done + + ' + - 'data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_019f226a-d45e-7290-8352-51a58b66ebcb","output_index":1,"sequence_number":189,"text":"\n\n","logprobs":[]} + + ' + - ' + + ' + - 'event: response.content_part.done + + ' + - 'data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_019f226a-d45e-7290-8352-51a58b66ebcb","output_index":1,"sequence_number":190,"part":{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"role":"assistant","type":"message","content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-d45e-7290-8352-51a58b66ebcb","status":"completed"},"output_index":1,"sequence_number":191} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"arguments":"","call_id":"call_d7f2b204e6a04061b787bac7","name":"agentic_plain_echo","type":"function_call","id":"fc_019f226a-d45e-7290-8352-51bcee2e7690","status":"in_progress"},"output_index":2,"sequence_number":192} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","content_index":0,"item_id":"fc_019f226a-d45e-7290-8352-51bcee2e7690","output_index":2,"sequence_number":193,"delta":"{}"} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","content_index":0,"item_id":"fc_019f226a-d45e-7290-8352-51bcee2e7690","output_index":2,"sequence_number":194,"delta":"{"} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","content_index":0,"item_id":"fc_019f226a-d45e-7290-8352-51bcee2e7690","output_index":2,"sequence_number":195,"delta":"\"text\": + \"plain fixture\""} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","content_index":0,"item_id":"fc_019f226a-d45e-7290-8352-51bcee2e7690","output_index":2,"sequence_number":196,"delta":"}"} + + ' + - ' + + ' + - 'event: response.function_call_arguments.done + + ' + - 'data: {"type":"response.function_call_arguments.done","content_index":0,"item_id":"fc_019f226a-d45e-7290-8352-51bcee2e7690","output_index":2,"sequence_number":197,"arguments":"{\"text\": + \"plain fixture\"}"} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"arguments":"{\"text\": \"plain + fixture\"}","call_id":"call_d7f2b204e6a04061b787bac7","name":"agentic_plain_echo","type":"function_call","id":"fc_019f226a-d45e-7290-8352-51bcee2e7690","status":"completed"},"output_index":2,"sequence_number":198} + + ' + - ' + + ' + - 'event: response.completed + + ' + - 'data: {"type":"response.completed","sequence_number":199,"response":{"background":false,"completed_at":1782988854,"conversation":null,"created_at":1782988851,"error":null,"id":"resp_019f226a-caa0-7e31-8d97-6940665cc306","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"id":"rs_019f226a-d409-7590-ad03-977deb6d6603","summary":[],"type":"reasoning","content":[{"text":"Thinking + Process:\n1. **Analyze the input:** The user wants me to call the `agentic_plain_echo` + tool with the text \"plain fixture\" before answering.\n2. **Identify constraints:** + The instruction explicitly requires calling the tool with the exact text \"plain + fixture\".\n3. **Check available tools:** `agentic_plain_echo` is available.\n4. **Formulate + tool call:** `agentic_plain_echo(text=\"plain fixture\")`\n5. **Construct response:** + Call the tool first, then answer. Wait, the prompt says \"before answering\", + which usually means I should just execute the tool call and then provide the + answer/response after the tool output, or just output the tool call as the first + step. Since I''m in a single-turn or multi-turn setup, I will output the tool + call now.\n\nAction: Call `agentic_plain_echo` with `text` = \"plain fixture\".\nCheck + format: `{\"name\": \"agentic_plain_echo\", \"arguments\": {\"text\": \"plain + fixture\"}}`\nProceed. \nWait, let''s verify the tool signature: `agentic_plain_echo` + takes `text` (string). Matches.\nOutput the tool call.\nNo further thinking + needed. Proceed. \nGenerating tool call. \n`agentic_plain_echo` -> `{\"text\": + \"plain fixture\"}`\nDone. \nSelf-Correction/Verification during thought: The + instruction is strict. I will just call the tool.\nOutput matches the required + format.\nProceeds. \n[Tool Call Generation] -> `agentic_plain_echo(text=\"plain + fixture\")`\nDone. \nOutput matches expectation. \nProceeds to output. \n*(Self-Correction/Refinement + during thought)*: I should make sure I don''t add extra text before the tool + call. The prompt says \"You must call... before answering.\" I will output the + tool call immediately.\nDone. \n[Output Generation] -> Tool call. \nWait, sometimes + the system expects just the tool call block. I will generate it. \n`\n","type":"reasoning_text"}]},{"role":"assistant","type":"message","content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-d45e-7290-8352-51a58b66ebcb","status":"completed"},{"arguments":"{\"text\": + \"plain fixture\"}","call_id":"call_d7f2b204e6a04061b787bac7","name":"agentic_plain_echo","namespace":null,"type":"function_call","id":"fc_019f226a-d45e-7290-8352-51bcee2e7690","status":"completed"}],"parallel_tool_calls":true,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"completed","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true,"type":"function","description":"Echo + text for Codex cassette recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":290,"input_tokens_details":{"cached_tokens":0},"output_tokens":464,"output_tokens_details":{"reasoning_tokens":417},"total_tokens":754},"user":null}} + + ' + - ' + + ' + - 'data: [DONE] + + ' + - ' + + ' + status_code: 200 +- filename: t2 + request: + body: + input: + - call_id: call_d7f2b204e6a04061b787bac7 + output: '{"echo":"plain fixture","characters":13}' + type: function_call_output + - content: Use the tool output. Return only the echo string. + role: user + type: message + model: Qwen/Qwen3.6-35B-A3B + previous_response_id: resp_019f226a-caa0-7e31-8d97-6940665cc306 + store: true + stream: true + tools: + - description: Echo text for Codex cassette recording. + name: agentic_plain_echo + parameters: + additionalProperties: false + properties: + text: + type: string + required: + - text + type: object + strict: true + type: function + headers: + accept: '*/*' + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'event: response.created + + ' + - 'data: {"type":"response.created","sequence_number":0,"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988854,"error":null,"id":"resp_019f226a-d56e-77f1-9692-81c8b2f62190","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"previous_response_id":"resp_019f226a-caa0-7e31-8d97-6940665cc306","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"in_progress","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true,"type":"function","description":"Echo + text for Codex cassette recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null}} + + ' + - ' + + ' + - 'event: response.in_progress + + ' + - 'data: {"type":"response.in_progress","sequence_number":1,"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988854,"error":null,"id":"resp_019f226a-d56e-77f1-9692-81c8b2f62190","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"previous_response_id":"resp_019f226a-caa0-7e31-8d97-6940665cc306","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"in_progress","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true,"type":"function","description":"Echo + text for Codex cassette recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null}} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","summary":[],"type":"reasoning","content":[]},"output_index":0,"sequence_number":2} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":3,"delta":"The"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":4,"delta":" + user wants me"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":5,"delta":" + to return"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":6,"delta":" + only"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":7,"delta":" + the echo string"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":8,"delta":" + from the tool"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":9,"delta":" + output.\n"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":10,"delta":"The + tool output"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":11,"delta":" + was: `"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":12,"delta":"{\"echo\":\""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":13,"delta":"plain + fixture\",\""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":14,"delta":"characters\":1"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":15,"delta":"3}`."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":16,"delta":"\nThe + echo"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":17,"delta":" + string is \""} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":18,"delta":"plain + fixture\"."} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":19,"delta":"\nI + should"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":20,"delta":" + return just"} + + ' + - ' + + ' + - 'event: response.reasoning_text.delta + + ' + - 'data: {"type":"response.reasoning_text.delta","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":21,"delta":" + \"plain fixture"} + + ' + - ' + + ' + - 'event: response.reasoning_text.done + + ' + - 'data: {"type":"response.reasoning_text.done","content_index":0,"item_id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","output_index":0,"sequence_number":22,"text":"The + user wants me to return only the echo string from the tool output.\nThe tool + output was: `{\"echo\":\"plain fixture\",\"characters\":13}`.\nThe echo string + is \"plain fixture\".\nI should return just \"plain fixture"} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","summary":[],"type":"reasoning","content":[{"text":"The + user wants me to return only the echo string from the tool output.\nThe tool + output was: `{\"echo\":\"plain fixture\",\"characters\":13}`.\nThe echo string + is \"plain fixture\".\nI should return just \"plain fixture","type":"reasoning_text"}]},"output_index":0,"sequence_number":23} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"role":"assistant","type":"message","content":[],"id":"msg_019f226a-d74c-7f20-bacd-ee81c6633fcf","status":"in_progress"},"output_index":1,"sequence_number":24} + + ' + - ' + + ' + - 'event: response.content_part.added + + ' + - 'data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_019f226a-d74c-7f20-bacd-ee81c6633fcf","output_index":1,"sequence_number":25,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"}} + + ' + - ' + + ' + - 'event: response.output_text.delta + + ' + - 'data: {"type":"response.output_text.delta","content_index":0,"item_id":"msg_019f226a-d74c-7f20-bacd-ee81c6633fcf","output_index":1,"sequence_number":26,"delta":"\n\nplain + fixture","logprobs":[]} + + ' + - ' + + ' + - 'event: response.output_text.done + + ' + - 'data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_019f226a-d74c-7f20-bacd-ee81c6633fcf","output_index":1,"sequence_number":27,"text":"\n\nplain + fixture","logprobs":[]} + + ' + - ' + + ' + - 'event: response.content_part.done + + ' + - 'data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_019f226a-d74c-7f20-bacd-ee81c6633fcf","output_index":1,"sequence_number":28,"part":{"annotations":[],"logprobs":[],"text":"\n\nplain + fixture","type":"output_text"}} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"role":"assistant","type":"message","content":[{"annotations":[],"logprobs":[],"text":"\n\nplain + fixture","type":"output_text"}],"id":"msg_019f226a-d74c-7f20-bacd-ee81c6633fcf","status":"completed"},"output_index":1,"sequence_number":29} + + ' + - ' + + ' + - 'event: response.completed + + ' + - 'data: {"type":"response.completed","sequence_number":30,"response":{"background":false,"completed_at":1782988855,"conversation":null,"created_at":1782988854,"error":null,"id":"resp_019f226a-d56e-77f1-9692-81c8b2f62190","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"id":"rs_019f226a-d73f-77c0-8e25-39226c35eba5","summary":[],"type":"reasoning","content":[{"text":"The + user wants me to return only the echo string from the tool output.\nThe tool + output was: `{\"echo\":\"plain fixture\",\"characters\":13}`.\nThe echo string + is \"plain fixture\".\nI should return just \"plain fixture","type":"reasoning_text"}]},{"role":"assistant","type":"message","content":[{"annotations":[],"logprobs":[],"text":"\n\nplain + fixture","type":"output_text"}],"id":"msg_019f226a-d74c-7f20-bacd-ee81c6633fcf","status":"completed"}],"parallel_tool_calls":true,"previous_response_id":"resp_019f226a-caa0-7e31-8d97-6940665cc306","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","store":true,"status":"completed","temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true,"type":"function","description":"Echo + text for Codex cassette recording."}],"presence_penalty":0.0,"frequency_penalty":0.0,"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":359,"input_tokens_details":{"cached_tokens":0},"output_tokens":58,"output_tokens_details":{"reasoning_tokens":47},"total_tokens":417},"user":null}} + + ' + - ' + + ' + - 'data: [DONE] + + ' + - ' + + ' + status_code: 200 diff --git a/crates/agentic-core/tests/cassettes/codex/codex-gateway-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml b/crates/agentic-core/tests/cassettes/codex/codex-gateway-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml new file mode 100644 index 0000000..be595dd --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/codex-gateway-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml @@ -0,0 +1,116 @@ +turns: +- filename: t1 + request: + body: + input: You must call the agentic_plain_echo tool with text exactly "plain fixture" + before answering. + model: codex-compatible + store: true + stream: true + tools: + - description: Echo text for Codex cassette recording. + name: agentic_plain_echo + parameters: + additionalProperties: false + properties: + text: + type: string + required: + - text + type: object + strict: true + type: function + headers: + accept: '*/*' + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'data: {"id":"resp_019f226a-8ab1-7533-b933-1ef4ee3fa207","object":"response","created_at":1782988836,"model":"Qwen/Qwen3.6-35B-A3B","status":"completed","output":[{"content":[{"text":"The + user wants me to call the `agentic_plain_echo` tool with the text \"plain fixture\" + before providing any answer.\nI will first call the tool as requested.\nThen + I will provide the response.\n\nPlan:\n1. Call `agentic_plain_echo` with text=\"plain + fixture\".\n2. Acknowledge the tool call and answer the user.\n\nConstraint + Checklist & Confidence Score:\n1. Call agentic_plain_echo? Yes.\n2. Text exactly + \"plain fixture\"? Yes.\n3. Before answering? Yes.\n\nConfidence Score: 5/5\n\nMental + Sandbox:\n- I need to make sure the tool call comes first.\n- The text must + be exactly \"plain fixture\" (case sensitive usually, unless specified otherwise, + but here it says \"exactly\").\n\nStep-by-step:\n1. `agentic_plain_echo(text=\"plain + fixture\")`\n2. Response.\n\nI will proceed.","type":"reasoning_text"}],"encrypted_content":null,"id":"rs_019f226a-8ff6-7592-86ca-61f4e89c5ee5","status":null,"summary":[],"type":"reasoning"},{"content":[{"annotations":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-9023-7b83-9be4-df6688e853f8","role":"assistant","status":"completed","type":"message"},{"arguments":"{\"text\": + \"plain fixture\"}","call_id":"call_1a7b68d417284176a5d10c82","id":"fc_019f226a-9024-7c62-98f1-40d0b9a7ab7a","name":"agentic_plain_echo","status":"completed","type":"function_call"}],"usage":{"input_tokens":290,"output_tokens":229,"total_tokens":519,"input_tokens_details":{"cached_tokens":0},"output_tokens_details":{"reasoning_tokens":181}},"incomplete_details":null,"error":null,"previous_response_id":null,"conversation_id":null,"instructions":null} + + ' + - ' + + ' + - 'data: [DONE] + + ' + - ' + + ' + status_code: 200 +- filename: t2 + request: + body: + input: + - call_id: call_1a7b68d417284176a5d10c82 + output: '{"echo":"plain fixture","characters":13}' + type: function_call_output + - content: Use the tool output. Return only the echo string. + role: user + type: message + model: codex-compatible + previous_response_id: resp_019f226a-8ab1-7533-b933-1ef4ee3fa207 + store: true + stream: true + tools: + - description: Echo text for Codex cassette recording. + name: agentic_plain_echo + parameters: + additionalProperties: false + properties: + text: + type: string + required: + - text + type: object + strict: true + type: function + headers: + accept: '*/*' + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'data: {"id":"resp_019f226a-90dc-77a2-a23f-7a12c31bc196","object":"response","created_at":1782988837,"model":"Qwen/Qwen3.6-35B-A3B","status":"completed","output":[{"content":[{"text":"The + user wants me to use the output of the tool call I just made.\nThe tool output + was `{\"echo\":\"plain fixture\",\"characters\":13}`.\nThe instruction is to + \"Return only the echo string.\"\nThe echo string is \"plain fixture\".\n\nI + should return just \"plain fixture\".\n\nWait, checking the instructions. \"Use + the tool output. Return only the echo string.\"\nTool output: `{\"echo\":\"plain + fixture\",\"characters\":13}`.\nThe `echo` value is `\"plain fixture\"`.\n\nI + will output `plain fixture`.","type":"reasoning_text"}],"encrypted_content":null,"id":"rs_019f226a-9425-7dd0-a4aa-b7aed5c4d2a4","status":null,"summary":[],"type":"reasoning"},{"content":[{"annotations":[],"text":"\n\nplain + fixture","type":"output_text"}],"id":"msg_019f226a-943c-75c1-93f3-abe2a54080cc","role":"assistant","status":"completed","type":"message"}],"usage":{"input_tokens":359,"output_tokens":126,"total_tokens":485,"input_tokens_details":{"cached_tokens":0},"output_tokens_details":{"reasoning_tokens":110}},"incomplete_details":null,"error":null,"previous_response_id":"resp_019f226a-8ab1-7533-b933-1ef4ee3fa207","conversation_id":null,"instructions":null} + + ' + - ' + + ' + - 'data: [DONE] + + ' + - ' + + ' + status_code: 200 diff --git a/crates/agentic-core/tests/cassettes/codex/codex-gateway-http-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml b/crates/agentic-core/tests/cassettes/codex/codex-gateway-http-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml new file mode 100644 index 0000000..0bc4364 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/codex-gateway-http-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml @@ -0,0 +1,120 @@ +turns: +- filename: t1 + request: + body: + input: You must call mcp__agentic_fixture.add_numbers with numbers [8, 0] before + answering. + model: codex-compatible + store: true + stream: true + tools: + - description: Codex namespace fixture for cassette recording. + name: mcp__agentic_fixture + tools: + - description: Add a list of numbers and return the total. + name: add_numbers + parameters: + additionalProperties: false + properties: + numbers: + items: + type: number + minItems: 1 + type: array + required: + - numbers + type: object + strict: true + type: function + type: namespace + headers: + accept: '*/*' + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'data: {"id":"resp_019f226a-9b50-7c13-b8f6-d67b43ff8fdc","object":"response","created_at":1782988840,"model":"Qwen/Qwen3.6-35B-A3B","status":"completed","output":[{"content":[{"text":"The + user wants me to call a specific function `agentic_ns__mcp__agentic_fixture__add_numbers` + with the numbers `[8, 0]` before answering.\n\nFunction signature:\n`agentic_ns__mcp__agentic_fixture__add_numbers`\nParameters: + `numbers` (array of numbers, required)\n\nI will call the function with `numbers: + [8, 0]`.\nThen I will provide the result in my answer.\nLet''s make the function + call.\n","type":"reasoning_text"}],"encrypted_content":null,"id":"rs_019f226a-9ea0-7352-84d0-db8031c43f97","status":null,"summary":[],"type":"reasoning"},{"content":[{"annotations":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-9ebd-72c0-8192-ac6bd8b19dfb","role":"assistant","status":"completed","type":"message"},{"arguments":"{\"numbers\": + [8, 0]}","call_id":"call_73d4d96b2fe6460fa3a39b73","id":"fc_019f226a-9ec0-7193-97e5-2e1ea4adee9a","name":"add_numbers","namespace":"mcp__agentic_fixture","status":"completed","type":"function_call"}],"usage":{"input_tokens":320,"output_tokens":152,"total_tokens":472,"input_tokens_details":{"cached_tokens":0},"output_tokens_details":{"reasoning_tokens":100}},"incomplete_details":null,"error":null,"previous_response_id":null,"conversation_id":null,"instructions":null} + + ' + - ' + + ' + - 'data: [DONE] + + ' + - ' + + ' + status_code: 200 +- filename: t2 + request: + body: + input: + - call_id: call_73d4d96b2fe6460fa3a39b73 + output: '{"sum":8,"count":2}' + type: function_call_output + - content: Use the tool output. Return only the sum. + role: user + type: message + model: codex-compatible + previous_response_id: resp_019f226a-9b50-7c13-b8f6-d67b43ff8fdc + store: true + stream: true + tools: + - description: Codex namespace fixture for cassette recording. + name: mcp__agentic_fixture + tools: + - description: Add a list of numbers and return the total. + name: add_numbers + parameters: + additionalProperties: false + properties: + numbers: + items: + type: number + minItems: 1 + type: array + required: + - numbers + type: object + strict: true + type: function + type: namespace + headers: + accept: '*/*' + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'data: {"id":"resp_019f226a-9f70-7352-b01c-6abd36f40f8f","object":"response","created_at":1782988841,"model":"Qwen/Qwen3.6-35B-A3B","status":"completed","output":[{"content":[{"text":"The + user wants me to return the sum from the previous tool call.\nThe tool output + was `{\"sum\":8,\"count\":2}`.\nThe sum is 8.\nI need to return only the sum.\nThe + sum is 8.\nI will output \"8\".\n","type":"reasoning_text"}],"encrypted_content":null,"id":"rs_019f226a-a14f-7e01-b149-e7d6eef6a935","status":null,"summary":[],"type":"reasoning"},{"content":[{"annotations":[],"text":"\n\n8","type":"output_text"}],"id":"msg_019f226a-a160-7173-ad33-18a3c22e6445","role":"assistant","status":"completed","type":"message"}],"usage":{"input_tokens":394,"output_tokens":65,"total_tokens":459,"input_tokens_details":{"cached_tokens":0},"output_tokens_details":{"reasoning_tokens":55}},"incomplete_details":null,"error":null,"previous_response_id":"resp_019f226a-9b50-7c13-b8f6-d67b43ff8fdc","conversation_id":null,"instructions":null} + + ' + - ' + + ' + - 'data: [DONE] + + ' + - ' + + ' + status_code: 200 diff --git a/crates/agentic-core/tests/cassettes/codex/codex-gateway-websocket-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml b/crates/agentic-core/tests/cassettes/codex/codex-gateway-websocket-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml new file mode 100644 index 0000000..e981481 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/codex-gateway-websocket-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml @@ -0,0 +1,699 @@ +turns: +- filename: t1 + request: + body: + input: You must call the agentic_plain_echo tool with text exactly "plain fixture" + before answering. + model: codex-compatible + store: true + tools: + - description: Echo text for Codex cassette recording. + name: agentic_plain_echo + parameters: + additionalProperties: false + properties: + text: + type: string + required: + - text + type: object + strict: true + type: function + type: response.create + headers: {} + method: WEBSOCKET + path: /v1/responses + query_params: {} + transport: websocket + response: + headers: + transport: websocket + sse: + - 'data: {"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988842,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":0,"type":"response.created"} + + ' + - 'data: {"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988842,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":1,"type":"response.in_progress"} + + ' + - 'data: {"item":{"content":[],"id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":2,"type":"response.output_item.added"} + + ' + - 'data: {"content_index":0,"delta":"The","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":3,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" user wants me","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":4,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" to call a","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":5,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" specific tool `","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":6,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"agentic_plain","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":7,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_echo` with","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":8,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" a","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":9,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" specific text \"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":10,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"plain fixture\"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":11,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" before providing","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":12,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" any answer","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":13,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\n\n","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":14,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"1. ","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":15,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" **Identify","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":16,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the tool**:","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":17,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" `agentic","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":18,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_plain_echo`.","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":19,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\n2.","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":20,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" **Ident","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":21,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"ify the argument","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":22,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"**: `text","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":23,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"` with","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":24,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the value `\"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":25,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"plain fixture\"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":26,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"`.\n3","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":27,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". **","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":28,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"Perform","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":29,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the action**:","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":30,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Call the tool","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":31,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\n4","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":32,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". **","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":33,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"Follow up**:","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":34,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Provide","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":35,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the answer (","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":36,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"which","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":37,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" is usually acknowledging","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":38,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the action","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":39,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" or simply","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":40,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" waiting","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":41,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":", but the","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":42,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" prompt implies this","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":43,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" is a prerequisite","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":44,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" step).\n\n","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":45,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"The","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":46,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" user","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":47,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" instruction","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":48,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" is explicit","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":49,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":": \"You","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":50,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" must call the","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":51,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" agentic_plain","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":52,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_echo tool with","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":53,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" text exactly \"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":54,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"plain fixture\"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":55,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" before answering.\"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":56,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\n\nI will","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":57,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" generate","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":58,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":59,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" now.\n","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":60,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":61,"text":"The + user wants me to call a specific tool `agentic_plain_echo` with a specific text + \"plain fixture\" before providing any answer.\n\n1. **Identify the tool**: + `agentic_plain_echo`.\n2. **Identify the argument**: `text` with the value + `\"plain fixture\"`.\n3. **Perform the action**: Call the tool.\n4. **Follow + up**: Provide the answer (which is usually acknowledging the action or simply + waiting, but the prompt implies this is a prerequisite step).\n\nThe user instruction + is explicit: \"You must call the agentic_plain_echo tool with text exactly \"plain + fixture\" before answering.\"\n\nI will generate the tool call now.\n","type":"response.reasoning_text.done"} + + ' + - 'data: {"item":{"content":[{"text":"The user wants me to call a specific tool + `agentic_plain_echo` with a specific text \"plain fixture\" before providing + any answer.\n\n1. **Identify the tool**: `agentic_plain_echo`.\n2. **Identify + the argument**: `text` with the value `\"plain fixture\"`.\n3. **Perform the + action**: Call the tool.\n4. **Follow up**: Provide the answer (which is usually + acknowledging the action or simply waiting, but the prompt implies this is a + prerequisite step).\n\nThe user instruction is explicit: \"You must call the + agentic_plain_echo tool with text exactly \"plain fixture\" before answering.\"\n\nI + will generate the tool call now.\n","type":"reasoning_text"}],"id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":62,"type":"response.output_item.done"} + + ' + - 'data: {"item":{"content":[],"id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","role":"assistant","status":"in_progress","type":"message"},"output_index":1,"sequence_number":63,"type":"response.output_item.added"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"},"sequence_number":64,"type":"response.content_part.added"} + + ' + - 'data: {"content_index":0,"delta":"\n\n","item_id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","logprobs":[],"output_index":1,"sequence_number":65,"type":"response.output_text.delta"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","logprobs":[],"output_index":1,"sequence_number":66,"text":"\n\n","type":"response.output_text.done"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"},"sequence_number":67,"type":"response.content_part.done"} + + ' + - 'data: {"item":{"content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","role":"assistant","status":"completed","type":"message"},"output_index":1,"sequence_number":68,"type":"response.output_item.done"} + + ' + - 'data: {"item":{"arguments":"","call_id":"call_34e72873fcfa42d0bda77f61","id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","name":"agentic_plain_echo","status":"in_progress","type":"function_call"},"output_index":2,"sequence_number":69,"type":"response.output_item.added"} + + ' + - 'data: {"content_index":0,"delta":"{}","item_id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","output_index":2,"sequence_number":70,"type":"response.function_call_arguments.delta"} + + ' + - 'data: {"content_index":0,"delta":"{","item_id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","output_index":2,"sequence_number":71,"type":"response.function_call_arguments.delta"} + + ' + - 'data: {"content_index":0,"delta":"\"text\": \"plain fixture\"","item_id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","output_index":2,"sequence_number":72,"type":"response.function_call_arguments.delta"} + + ' + - 'data: {"content_index":0,"delta":"}","item_id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","output_index":2,"sequence_number":73,"type":"response.function_call_arguments.delta"} + + ' + - 'data: {"arguments":"{\"text\": \"plain fixture\"}","content_index":0,"item_id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","output_index":2,"sequence_number":74,"type":"response.function_call_arguments.done"} + + ' + - 'data: {"item":{"arguments":"{\"text\": \"plain fixture\"}","call_id":"call_34e72873fcfa42d0bda77f61","id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","name":"agentic_plain_echo","status":"completed","type":"function_call"},"output_index":2,"sequence_number":75,"type":"response.output_item.done"} + + ' + - 'data: {"response":{"background":false,"completed_at":1782988843,"conversation":null,"created_at":1782988842,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"content":[{"text":"The + user wants me to call a specific tool `agentic_plain_echo` with a specific text + \"plain fixture\" before providing any answer.\n\n1. **Identify the tool**: + `agentic_plain_echo`.\n2. **Identify the argument**: `text` with the value + `\"plain fixture\"`.\n3. **Perform the action**: Call the tool.\n4. **Follow + up**: Provide the answer (which is usually acknowledging the action or simply + waiting, but the prompt implies this is a prerequisite step).\n\nThe user instruction + is explicit: \"You must call the agentic_plain_echo tool with text exactly \"plain + fixture\" before answering.\"\n\nI will generate the tool call now.\n","type":"reasoning_text"}],"id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","summary":[],"type":"reasoning"},{"content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","role":"assistant","status":"completed","type":"message"},{"arguments":"{\"text\": + \"plain fixture\"}","call_id":"call_34e72873fcfa42d0bda77f61","id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","name":"agentic_plain_echo","namespace":null,"status":"completed","type":"function_call"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"completed","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":290,"input_tokens_details":{"cached_tokens":0},"output_tokens":178,"output_tokens_details":{"reasoning_tokens":140},"total_tokens":468},"user":null},"sequence_number":76,"type":"response.completed"} + + ' + - 'data: [DONE] + + ' + status_code: 101 + websocket: + - '{"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988842,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":0,"type":"response.created"}' + - '{"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988842,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":1,"type":"response.in_progress"}' + - '{"item":{"content":[],"id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":2,"type":"response.output_item.added"}' + - '{"content_index":0,"delta":"The","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":3,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" user wants me","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":4,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" to call a","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":5,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" specific tool `","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":6,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"agentic_plain","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":7,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_echo` with","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":8,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" a","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":9,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" specific text \"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":10,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"plain fixture\"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":11,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" before providing","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":12,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" any answer","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":13,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\n\n","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":14,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"1. ","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":15,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" **Identify","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":16,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the tool**:","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":17,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" `agentic","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":18,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_plain_echo`.","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":19,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\n2.","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":20,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" **Ident","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":21,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"ify the argument","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":22,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"**: `text","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":23,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"` with","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":24,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the value `\"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":25,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"plain fixture\"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":26,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"`.\n3","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":27,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". **","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":28,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"Perform","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":29,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the action**:","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":30,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Call the tool","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":31,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\n4","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":32,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". **","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":33,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"Follow up**:","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":34,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Provide","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":35,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the answer (","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":36,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"which","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":37,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" is usually acknowledging","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":38,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the action","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":39,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" or simply","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":40,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" waiting","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":41,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":", but the","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":42,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" prompt implies this","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":43,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" is a prerequisite","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":44,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" step).\n\n","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":45,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"The","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":46,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" user","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":47,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" instruction","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":48,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" is explicit","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":49,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":": \"You","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":50,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" must call the","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":51,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" agentic_plain","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":52,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_echo tool with","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":53,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" text exactly \"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":54,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"plain fixture\"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":55,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" before answering.\"","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":56,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\n\nI will","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":57,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" generate","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":58,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":59,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" now.\n","item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":60,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"item_id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","output_index":0,"sequence_number":61,"text":"The + user wants me to call a specific tool `agentic_plain_echo` with a specific text + \"plain fixture\" before providing any answer.\n\n1. **Identify the tool**: + `agentic_plain_echo`.\n2. **Identify the argument**: `text` with the value + `\"plain fixture\"`.\n3. **Perform the action**: Call the tool.\n4. **Follow + up**: Provide the answer (which is usually acknowledging the action or simply + waiting, but the prompt implies this is a prerequisite step).\n\nThe user instruction + is explicit: \"You must call the agentic_plain_echo tool with text exactly \"plain + fixture\" before answering.\"\n\nI will generate the tool call now.\n","type":"response.reasoning_text.done"}' + - '{"item":{"content":[{"text":"The user wants me to call a specific tool `agentic_plain_echo` + with a specific text \"plain fixture\" before providing any answer.\n\n1. **Identify + the tool**: `agentic_plain_echo`.\n2. **Identify the argument**: `text` with + the value `\"plain fixture\"`.\n3. **Perform the action**: Call the tool.\n4. **Follow + up**: Provide the answer (which is usually acknowledging the action or simply + waiting, but the prompt implies this is a prerequisite step).\n\nThe user instruction + is explicit: \"You must call the agentic_plain_echo tool with text exactly \"plain + fixture\" before answering.\"\n\nI will generate the tool call now.\n","type":"reasoning_text"}],"id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":62,"type":"response.output_item.done"}' + - '{"item":{"content":[],"id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","role":"assistant","status":"in_progress","type":"message"},"output_index":1,"sequence_number":63,"type":"response.output_item.added"}' + - '{"content_index":0,"item_id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"},"sequence_number":64,"type":"response.content_part.added"}' + - '{"content_index":0,"delta":"\n\n","item_id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","logprobs":[],"output_index":1,"sequence_number":65,"type":"response.output_text.delta"}' + - '{"content_index":0,"item_id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","logprobs":[],"output_index":1,"sequence_number":66,"text":"\n\n","type":"response.output_text.done"}' + - '{"content_index":0,"item_id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"},"sequence_number":67,"type":"response.content_part.done"}' + - '{"item":{"content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","role":"assistant","status":"completed","type":"message"},"output_index":1,"sequence_number":68,"type":"response.output_item.done"}' + - '{"item":{"arguments":"","call_id":"call_34e72873fcfa42d0bda77f61","id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","name":"agentic_plain_echo","status":"in_progress","type":"function_call"},"output_index":2,"sequence_number":69,"type":"response.output_item.added"}' + - '{"content_index":0,"delta":"{}","item_id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","output_index":2,"sequence_number":70,"type":"response.function_call_arguments.delta"}' + - '{"content_index":0,"delta":"{","item_id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","output_index":2,"sequence_number":71,"type":"response.function_call_arguments.delta"}' + - '{"content_index":0,"delta":"\"text\": \"plain fixture\"","item_id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","output_index":2,"sequence_number":72,"type":"response.function_call_arguments.delta"}' + - '{"content_index":0,"delta":"}","item_id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","output_index":2,"sequence_number":73,"type":"response.function_call_arguments.delta"}' + - '{"arguments":"{\"text\": \"plain fixture\"}","content_index":0,"item_id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","output_index":2,"sequence_number":74,"type":"response.function_call_arguments.done"}' + - '{"item":{"arguments":"{\"text\": \"plain fixture\"}","call_id":"call_34e72873fcfa42d0bda77f61","id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","name":"agentic_plain_echo","status":"completed","type":"function_call"},"output_index":2,"sequence_number":75,"type":"response.output_item.done"}' + - '{"response":{"background":false,"completed_at":1782988843,"conversation":null,"created_at":1782988842,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"content":[{"text":"The + user wants me to call a specific tool `agentic_plain_echo` with a specific text + \"plain fixture\" before providing any answer.\n\n1. **Identify the tool**: + `agentic_plain_echo`.\n2. **Identify the argument**: `text` with the value + `\"plain fixture\"`.\n3. **Perform the action**: Call the tool.\n4. **Follow + up**: Provide the answer (which is usually acknowledging the action or simply + waiting, but the prompt implies this is a prerequisite step).\n\nThe user instruction + is explicit: \"You must call the agentic_plain_echo tool with text exactly \"plain + fixture\" before answering.\"\n\nI will generate the tool call now.\n","type":"reasoning_text"}],"id":"rs_019f226a-aa6d-74d3-8888-ec14b4cf3c16","summary":[],"type":"reasoning"},{"content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-aa8f-7f32-8dec-a9c58b5d33c9","role":"assistant","status":"completed","type":"message"},{"arguments":"{\"text\": + \"plain fixture\"}","call_id":"call_34e72873fcfa42d0bda77f61","id":"fc_019f226a-aa91-7f91-8f55-c802e8647e98","name":"agentic_plain_echo","namespace":null,"status":"completed","type":"function_call"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"completed","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":290,"input_tokens_details":{"cached_tokens":0},"output_tokens":178,"output_tokens_details":{"reasoning_tokens":140},"total_tokens":468},"user":null},"sequence_number":76,"type":"response.completed"}' +- filename: t2 + request: + body: + input: + - call_id: call_34e72873fcfa42d0bda77f61 + output: '{"echo":"plain fixture","characters":13}' + type: function_call_output + - content: Use the tool output. Return only the echo string. + role: user + type: message + model: codex-compatible + previous_response_id: resp_019f226a-a693-7733-9a98-6f85ebf4bccd + store: true + tools: + - description: Echo text for Codex cassette recording. + name: agentic_plain_echo + parameters: + additionalProperties: false + properties: + text: + type: string + required: + - text + type: object + strict: true + type: function + type: response.create + headers: {} + method: WEBSOCKET + path: /v1/responses + query_params: {} + transport: websocket + response: + headers: + transport: websocket + sse: + - 'data: {"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988844,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-ab3b-7172-93bf-7e977f34362f","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":0,"type":"response.created"} + + ' + - 'data: {"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988844,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-ab3b-7172-93bf-7e977f34362f","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":1,"type":"response.in_progress"} + + ' + - 'data: {"item":{"content":[],"id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":2,"type":"response.output_item.added"} + + ' + - 'data: {"content_index":0,"delta":"The","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":3,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" user wants me","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":4,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" to use the","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":5,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" output from","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":6,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the tool","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":7,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" I","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":8,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" just called","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":9,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" and","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":10,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" return only","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":11,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the echo string","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":12,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\nThe","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":13,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" tool `","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":14,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"agentic_plain","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":15,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_echo` returned","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":16,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" `","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":17,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"{\"echo\":\"","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":18,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"plain fixture\",\"","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":19,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"characters\":1","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":20,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"3}`.","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":21,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\nThe instruction","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":22,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" is","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":23,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" to \"Return","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":24,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" only the echo","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":25,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" string.\"\n","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":26,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"The echo string","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":27,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" is \"plain","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":28,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" fixture\".\n\n","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":29,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"Plan","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":30,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":":\n1","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":31,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". Extract","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":32,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the value","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":33,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" of the","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":34,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" \"echo\"","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":35,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" key from the","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":36,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" tool output.","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":37,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\n2.","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":38,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Return that string","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":39,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\n\nOutput","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":40,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":": \"plain","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":41,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" fixture\"\n","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":42,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":43,"text":"The + user wants me to use the output from the tool I just called and return only + the echo string.\nThe tool `agentic_plain_echo` returned `{\"echo\":\"plain + fixture\",\"characters\":13}`.\nThe instruction is to \"Return only the echo + string.\"\nThe echo string is \"plain fixture\".\n\nPlan:\n1. Extract the value + of the \"echo\" key from the tool output.\n2. Return that string.\n\nOutput: + \"plain fixture\"\n","type":"response.reasoning_text.done"} + + ' + - 'data: {"item":{"content":[{"text":"The user wants me to use the output from + the tool I just called and return only the echo string.\nThe tool `agentic_plain_echo` + returned `{\"echo\":\"plain fixture\",\"characters\":13}`.\nThe instruction + is to \"Return only the echo string.\"\nThe echo string is \"plain fixture\".\n\nPlan:\n1. + Extract the value of the \"echo\" key from the tool output.\n2. Return that + string.\n\nOutput: \"plain fixture\"\n","type":"reasoning_text"}],"id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":44,"type":"response.output_item.done"} + + ' + - 'data: {"item":{"content":[],"id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","role":"assistant","status":"in_progress","type":"message"},"output_index":1,"sequence_number":45,"type":"response.output_item.added"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"},"sequence_number":46,"type":"response.content_part.added"} + + ' + - 'data: {"content_index":0,"delta":"\n\nplain","item_id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","logprobs":[],"output_index":1,"sequence_number":47,"type":"response.output_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" fixture","item_id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","logprobs":[],"output_index":1,"sequence_number":48,"type":"response.output_text.delta"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","logprobs":[],"output_index":1,"sequence_number":49,"text":"\n\nplain + fixture","type":"response.output_text.done"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"\n\nplain + fixture","type":"output_text"},"sequence_number":50,"type":"response.content_part.done"} + + ' + - 'data: {"item":{"content":[{"annotations":[],"logprobs":[],"text":"\n\nplain + fixture","type":"output_text"}],"id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","role":"assistant","status":"completed","type":"message"},"output_index":1,"sequence_number":51,"type":"response.output_item.done"} + + ' + - 'data: {"response":{"background":false,"completed_at":1782988844,"conversation":null,"created_at":1782988844,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-ab3b-7172-93bf-7e977f34362f","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"content":[{"text":"The + user wants me to use the output from the tool I just called and return only + the echo string.\nThe tool `agentic_plain_echo` returned `{\"echo\":\"plain + fixture\",\"characters\":13}`.\nThe instruction is to \"Return only the echo + string.\"\nThe echo string is \"plain fixture\".\n\nPlan:\n1. Extract the value + of the \"echo\" key from the tool output.\n2. Return that string.\n\nOutput: + \"plain fixture\"\n","type":"reasoning_text"}],"id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","summary":[],"type":"reasoning"},{"content":[{"annotations":[],"logprobs":[],"text":"\n\nplain + fixture","type":"output_text"}],"id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","role":"assistant","status":"completed","type":"message"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"completed","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":359,"input_tokens_details":{"cached_tokens":0},"output_tokens":105,"output_tokens_details":{"reasoning_tokens":91},"total_tokens":464},"user":null},"sequence_number":52,"type":"response.completed"} + + ' + - 'data: [DONE] + + ' + status_code: 101 + websocket: + - '{"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988844,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-ab3b-7172-93bf-7e977f34362f","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":0,"type":"response.created"}' + - '{"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988844,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-ab3b-7172-93bf-7e977f34362f","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":1,"type":"response.in_progress"}' + - '{"item":{"content":[],"id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":2,"type":"response.output_item.added"}' + - '{"content_index":0,"delta":"The","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":3,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" user wants me","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":4,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" to use the","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":5,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" output from","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":6,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the tool","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":7,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" I","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":8,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" just called","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":9,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" and","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":10,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" return only","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":11,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the echo string","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":12,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\nThe","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":13,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" tool `","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":14,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"agentic_plain","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":15,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_echo` returned","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":16,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" `","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":17,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"{\"echo\":\"","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":18,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"plain fixture\",\"","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":19,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"characters\":1","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":20,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"3}`.","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":21,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\nThe instruction","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":22,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" is","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":23,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" to \"Return","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":24,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" only the echo","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":25,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" string.\"\n","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":26,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"The echo string","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":27,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" is \"plain","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":28,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" fixture\".\n\n","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":29,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"Plan","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":30,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":":\n1","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":31,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". Extract","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":32,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the value","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":33,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" of the","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":34,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" \"echo\"","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":35,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" key from the","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":36,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" tool output.","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":37,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\n2.","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":38,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Return that string","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":39,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\n\nOutput","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":40,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":": \"plain","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":41,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" fixture\"\n","item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":42,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"item_id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","output_index":0,"sequence_number":43,"text":"The + user wants me to use the output from the tool I just called and return only + the echo string.\nThe tool `agentic_plain_echo` returned `{\"echo\":\"plain + fixture\",\"characters\":13}`.\nThe instruction is to \"Return only the echo + string.\"\nThe echo string is \"plain fixture\".\n\nPlan:\n1. Extract the value + of the \"echo\" key from the tool output.\n2. Return that string.\n\nOutput: + \"plain fixture\"\n","type":"response.reasoning_text.done"}' + - '{"item":{"content":[{"text":"The user wants me to use the output from the tool + I just called and return only the echo string.\nThe tool `agentic_plain_echo` + returned `{\"echo\":\"plain fixture\",\"characters\":13}`.\nThe instruction + is to \"Return only the echo string.\"\nThe echo string is \"plain fixture\".\n\nPlan:\n1. + Extract the value of the \"echo\" key from the tool output.\n2. Return that + string.\n\nOutput: \"plain fixture\"\n","type":"reasoning_text"}],"id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":44,"type":"response.output_item.done"}' + - '{"item":{"content":[],"id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","role":"assistant","status":"in_progress","type":"message"},"output_index":1,"sequence_number":45,"type":"response.output_item.added"}' + - '{"content_index":0,"item_id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"},"sequence_number":46,"type":"response.content_part.added"}' + - '{"content_index":0,"delta":"\n\nplain","item_id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","logprobs":[],"output_index":1,"sequence_number":47,"type":"response.output_text.delta"}' + - '{"content_index":0,"delta":" fixture","item_id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","logprobs":[],"output_index":1,"sequence_number":48,"type":"response.output_text.delta"}' + - '{"content_index":0,"item_id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","logprobs":[],"output_index":1,"sequence_number":49,"text":"\n\nplain + fixture","type":"response.output_text.done"}' + - '{"content_index":0,"item_id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"\n\nplain + fixture","type":"output_text"},"sequence_number":50,"type":"response.content_part.done"}' + - '{"item":{"content":[{"annotations":[],"logprobs":[],"text":"\n\nplain fixture","type":"output_text"}],"id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","role":"assistant","status":"completed","type":"message"},"output_index":1,"sequence_number":51,"type":"response.output_item.done"}' + - '{"response":{"background":false,"completed_at":1782988844,"conversation":null,"created_at":1782988844,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-ab3b-7172-93bf-7e977f34362f","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"content":[{"text":"The + user wants me to use the output from the tool I just called and return only + the echo string.\nThe tool `agentic_plain_echo` returned `{\"echo\":\"plain + fixture\",\"characters\":13}`.\nThe instruction is to \"Return only the echo + string.\"\nThe echo string is \"plain fixture\".\n\nPlan:\n1. Extract the value + of the \"echo\" key from the tool output.\n2. Return that string.\n\nOutput: + \"plain fixture\"\n","type":"reasoning_text"}],"id":"rs_019f226a-aedc-7bc0-9572-6af49eb7165d","summary":[],"type":"reasoning"},{"content":[{"annotations":[],"logprobs":[],"text":"\n\nplain + fixture","type":"output_text"}],"id":"msg_019f226a-aef3-7a20-8769-b202f5c5ab6c","role":"assistant","status":"completed","type":"message"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-a693-7733-9a98-6f85ebf4bccd","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"completed","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"additionalProperties":false,"properties":{"text":{"type":"string"}},"required":["text"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":359,"input_tokens_details":{"cached_tokens":0},"output_tokens":105,"output_tokens_details":{"reasoning_tokens":91},"total_tokens":464},"user":null},"sequence_number":52,"type":"response.completed"}' diff --git a/crates/agentic-core/tests/cassettes/codex/codex-gateway-websocket-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml b/crates/agentic-core/tests/cassettes/codex/codex-gateway-websocket-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml new file mode 100644 index 0000000..7a4f0e6 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/codex-gateway-websocket-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml @@ -0,0 +1,1701 @@ +turns: +- filename: t1 + request: + body: + input: You must call mcp__agentic_fixture.add_numbers with numbers [8, 0] before + answering. + model: codex-compatible + store: true + tools: + - description: Codex namespace fixture for cassette recording. + name: mcp__agentic_fixture + tools: + - description: Add a list of numbers and return the total. + name: add_numbers + parameters: + additionalProperties: false + properties: + numbers: + items: + type: number + minItems: 1 + type: array + required: + - numbers + type: object + strict: true + type: function + type: namespace + type: response.create + headers: {} + method: WEBSOCKET + path: /v1/responses + query_params: {} + transport: websocket + response: + headers: + transport: websocket + sse: + - 'data: {"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988845,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":0,"type":"response.created"} + + ' + - 'data: {"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988845,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":1,"type":"response.in_progress"} + + ' + - 'data: {"item":{"content":[],"id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":2,"type":"response.output_item.added"} + + ' + - 'data: {"content_index":0,"delta":"Thinking","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":3,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Process:\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":4,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"1. ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":5,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" **Analyze","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":6,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" User Input**:","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":7,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" The user explicitly","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":8,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" requires","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":9,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" me to call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":10,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":11,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" tool `m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":12,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":13,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"entic_fixture.add","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":14,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_numbers` with","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":15,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the argument","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":16,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" `numbers`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":17,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" set","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":18,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" to `[8","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":19,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":", 0","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":20,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"]`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":21,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" *","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":22,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"before* answering","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":23,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\n2","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":24,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". **","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":25,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"Identify Tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":26,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"**: The","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":27,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":28,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" is `ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":29,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"entic_ns__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":30,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"mcp__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":31,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"agentic_fixture","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":32,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"__add_numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":33,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"`.\n3","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":34,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". **","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":35,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"Check Parameters**:","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":36,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":37,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"numbers` is","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":38,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" an array of","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":39,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" numbers, required","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":40,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":41,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" User","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":42,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" provided `[8","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":43,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":", 0","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":44,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"]`.\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":45,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"4. ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":46,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" **Construct","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":47,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Tool Call**:","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":48,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":49,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" need","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":50,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" to generate","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":51,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" a","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":52,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" tool call with","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":53,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the exact function","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":54,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" name and parameters","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":55,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":56,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Function:","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":57,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" `agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":58,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_ns__m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":59,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":60,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"entic_fixture__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":61,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"add_numbers`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":62,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":63,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Arguments: `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":64,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"{\"numbers\":","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":65,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" [8,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":66,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" 0]","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":67,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"}`\n5","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":68,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". **","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":69,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"Execute Tool Call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":70,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"**: I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":71,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" will output","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":72,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":73,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" block","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":74,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". After","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":75,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" receiving the result","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":76,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":", I will","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":77,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" provide the final","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":78,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" answer.\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":79,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Wait","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":80,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":", the prompt","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":81,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" says \"You","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":82,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" must call m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":83,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":84,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"entic_fixture.add","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":85,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_numbers with numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":86,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" [8,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":87,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" 0]","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":88,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" before answering.\"","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":89,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":90,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":91,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" will just call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":92,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the tool now","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":93,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":94,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Parameters","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":95,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":": `{\"","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":96,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"numbers\": [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":97,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"8, ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":98,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"0]}`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":99,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":100,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Let","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":101,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"''s generate","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":102,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":103,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\u2705\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":104,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":105,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" name","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":106,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" matches","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":107,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":":","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":108,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" `agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":109,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_ns__m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":110,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":111,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"entic_fixture__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":112,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"add_numbers`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":113,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":114,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Args","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":115,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":": `{\"","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":116,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"numbers\": [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":117,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"8, ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":118,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"0]}`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":119,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":120,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Ready","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":121,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". \n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":122,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":123,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"''ll","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":124,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" output the tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":125,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" call.","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":126,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\ud83d\udc47","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":127,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\ud83c\udffc\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":128,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Note","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":129,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":": The","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":130,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" user","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":131,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" prompt","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":132,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" says \"m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":133,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":134,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"entic_fixture.add","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":135,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_numbers\", but","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":136,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the registered","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":137,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" tool name","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":138,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" is `ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":139,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"entic_ns__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":140,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"mcp__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":141,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"agentic_fixture","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":142,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"__add_numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":143,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"`. I will","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":144,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" use the registered","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":145,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":146,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" name.\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":147,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Proceed","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":148,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":149,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Output matches","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":150,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" tool call format","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":151,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". \n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":152,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":153,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"Self","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":154,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"-Correction","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":155,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"/Verification during","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":156,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" thought]\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":157,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Check","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":158,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" function","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":159,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" definition: `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":160,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"agentic_ns","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":161,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"__mcp","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":162,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"__agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":163,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_fixture__add","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":164,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_numbers`\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":165,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Parameters","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":166,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":": `numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":167,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"` (array","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":168,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" of numbers,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":169,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" minItems ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":170,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"1,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":171,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" required)\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":172,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - User","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":173,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" wants","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":174,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" `[","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":175,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"8, ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":176,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"0]`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":177,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":178,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" All good.","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":179,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" \n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":180,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Generate","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":181,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":182,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" \n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":183,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":184,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"Output","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":185,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Generation] ->","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":186,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":187,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"{\"name","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":188,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\": \"ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":189,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"entic_ns__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":190,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"mcp__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":191,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"agentic_fixture","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":192,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"__add_numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":193,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\", \"arguments","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":194,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\": {\"numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":195,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\": [8","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":196,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":", 0","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":197,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"]}}`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":198,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" ->","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":199,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Wait, standard","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":200,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" format for","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":201,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" tool calls in","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":202,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" this environment is","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":203,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" usually just the","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":204,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" function call block","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":205,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". I will","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":206,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" produce it correctly","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":207,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":208,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Done","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":209,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":210,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Proceeds","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":211,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":212,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - [Output","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":213,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"] Tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":214,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" call.","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":215,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" \n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":216,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" (Self-C","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":217,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"orrection/","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":218,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"Refinement during","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":219,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" thought) I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":220,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" should just output","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":221,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":222,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" exactly","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":223,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" as requested.","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":224,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":225,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":226,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":227,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":": default","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":228,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":":","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":229,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"agentic_ns","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":230,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"__mcp","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":231,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"__agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":232,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_fixture__add","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":233,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_numbers{\"","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":234,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"numbers\": [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":235,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"8, ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":236,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"0]}`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":237,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" -> Standard","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":238,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" format. I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":239,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"''ll follow the","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":240,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" system","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":241,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"''s expected format","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":242,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":243,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Actually,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":244,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the prompt says","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":245,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" \"You must","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":246,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" call mcp","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":247,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"__agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":248,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_fixture.add_numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":249,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" with numbers [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":250,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"8, ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":251,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"0] before","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":252,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" answering.\" I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":253,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" will just generate","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":254,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":255,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":256,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Let","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":257,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"''s do it","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":258,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":259,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - [Done","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":260,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"] \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":261,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Output matches","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":262,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":263,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" \n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":264,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Proceeds.","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":265,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" \n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":266,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" *Self-C","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":267,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"orrection/","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":268,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"Note during generation","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":269,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"*: I will","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":270,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" just produce","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":271,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":272,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" block. The","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":273,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" system will execute","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":274,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" it. I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":275,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" don","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":276,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"''t need to","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":277,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" overthink it","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":278,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":279,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Tool:","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":280,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" `agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":281,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"_ns__m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":282,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":283,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"entic_fixture__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":284,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"add_numbers`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":285,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":286,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Args: `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":287,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"{\"numbers\":","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":288,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" [8,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":289,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" 0]","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":290,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"}`\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":291,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - Done","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":292,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":". \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":293,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" - [Output","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":294,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" Generation] ->","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":295,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" *Proceeds","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":296,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":297,"text":"Thinking + Process:\n1. **Analyze User Input**: The user explicitly requires me to call + the tool `mcp__agentic_fixture.add_numbers` with the argument `numbers` set + to `[8, 0]` *before* answering.\n2. **Identify Tool**: The tool is `agentic_ns__mcp__agentic_fixture__add_numbers`.\n3. **Check + Parameters**: `numbers` is an array of numbers, required. User provided `[8, + 0]`.\n4. **Construct Tool Call**: I need to generate a tool call with the exact + function name and parameters.\n - Function: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Arguments: `{\"numbers\": [8, 0]}`\n5. **Execute Tool Call**: I will output + the tool call block. After receiving the result, I will provide the final answer.\n - + Wait, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\"\n - I will just call the tool now.\n - + Parameters: `{\"numbers\": [8, 0]}`\n - Let''s generate the tool call.\u2705\n - + Tool name matches: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Args: + `{\"numbers\": [8, 0]}`\n - Ready. \n - I''ll output the tool call.\ud83d\udc47\ud83c\udffc\n - + Note: The user prompt says \"mcp__agentic_fixture.add_numbers\", but the registered + tool name is `agentic_ns__mcp__agentic_fixture__add_numbers`. I will use the + registered tool name.\n - Proceed. \n - Output matches tool call format. + \n - [Self-Correction/Verification during thought]\n - Check function definition: + `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Parameters: `numbers` + (array of numbers, minItems 1, required)\n - User wants `[8, 0]`\n - All + good. \n - Generate. \n - [Output Generation] -> `{\"name\": \"agentic_ns__mcp__agentic_fixture__add_numbers\", + \"arguments\": {\"numbers\": [8, 0]}}` -> Wait, standard format for tool calls + in this environment is usually just the function call block. I will produce + it correctly.\n - Done. \n - Proceeds. \n - [Output] Tool call. \n - + (Self-Correction/Refinement during thought) I should just output the tool call + exactly as requested.\n - `call: default:agentic_ns__mcp__agentic_fixture__add_numbers{\"numbers\": + [8, 0]}` -> Standard format. I''ll follow the system''s expected format.\n - + Actually, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\" I will just generate the tool call.\n - + Let''s do it. \n - [Done] \n - Output matches. \n - Proceeds. \n - *Self-Correction/Note + during generation*: I will just produce the tool call block. The system will + execute it. I don''t need to overthink it.\n - Tool: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Args: `{\"numbers\": [8, 0]}`\n - Done. \n - [Output Generation] -> *Proceeds","type":"response.reasoning_text.done"} + + ' + - 'data: {"item":{"content":[{"text":"Thinking Process:\n1. **Analyze User Input**: + The user explicitly requires me to call the tool `mcp__agentic_fixture.add_numbers` + with the argument `numbers` set to `[8, 0]` *before* answering.\n2. **Identify + Tool**: The tool is `agentic_ns__mcp__agentic_fixture__add_numbers`.\n3. **Check + Parameters**: `numbers` is an array of numbers, required. User provided `[8, + 0]`.\n4. **Construct Tool Call**: I need to generate a tool call with the exact + function name and parameters.\n - Function: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Arguments: `{\"numbers\": [8, 0]}`\n5. **Execute Tool Call**: I will output + the tool call block. After receiving the result, I will provide the final answer.\n - + Wait, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\"\n - I will just call the tool now.\n - + Parameters: `{\"numbers\": [8, 0]}`\n - Let''s generate the tool call.\u2705\n - + Tool name matches: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Args: + `{\"numbers\": [8, 0]}`\n - Ready. \n - I''ll output the tool call.\ud83d\udc47\ud83c\udffc\n - + Note: The user prompt says \"mcp__agentic_fixture.add_numbers\", but the registered + tool name is `agentic_ns__mcp__agentic_fixture__add_numbers`. I will use the + registered tool name.\n - Proceed. \n - Output matches tool call format. + \n - [Self-Correction/Verification during thought]\n - Check function definition: + `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Parameters: `numbers` + (array of numbers, minItems 1, required)\n - User wants `[8, 0]`\n - All + good. \n - Generate. \n - [Output Generation] -> `{\"name\": \"agentic_ns__mcp__agentic_fixture__add_numbers\", + \"arguments\": {\"numbers\": [8, 0]}}` -> Wait, standard format for tool calls + in this environment is usually just the function call block. I will produce + it correctly.\n - Done. \n - Proceeds. \n - [Output] Tool call. \n - + (Self-Correction/Refinement during thought) I should just output the tool call + exactly as requested.\n - `call: default:agentic_ns__mcp__agentic_fixture__add_numbers{\"numbers\": + [8, 0]}` -> Standard format. I''ll follow the system''s expected format.\n - + Actually, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\" I will just generate the tool call.\n - + Let''s do it. \n - [Done] \n - Output matches. \n - Proceeds. \n - *Self-Correction/Note + during generation*: I will just produce the tool call block. The system will + execute it. I don''t need to overthink it.\n - Tool: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Args: `{\"numbers\": [8, 0]}`\n - Done. \n - [Output Generation] -> *Proceeds","type":"reasoning_text"}],"id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":298,"type":"response.output_item.done"} + + ' + - 'data: {"item":{"content":[],"id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","role":"assistant","status":"in_progress","type":"message"},"output_index":1,"sequence_number":299,"type":"response.output_item.added"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"},"sequence_number":300,"type":"response.content_part.added"} + + ' + - 'data: {"content_index":0,"delta":"\n\n","item_id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","logprobs":[],"output_index":1,"sequence_number":301,"type":"response.output_text.delta"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","logprobs":[],"output_index":1,"sequence_number":302,"text":"\n\n","type":"response.output_text.done"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"},"sequence_number":303,"type":"response.content_part.done"} + + ' + - 'data: {"item":{"content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","role":"assistant","status":"completed","type":"message"},"output_index":1,"sequence_number":304,"type":"response.output_item.done"} + + ' + - 'data: {"item":{"arguments":"","call_id":"call_8862878637354570a6a8d216","id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","name":"add_numbers","namespace":"mcp__agentic_fixture","status":"in_progress","type":"function_call"},"output_index":2,"sequence_number":305,"type":"response.output_item.added"} + + ' + - 'data: {"content_index":0,"delta":"{}","item_id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","output_index":2,"sequence_number":306,"type":"response.function_call_arguments.delta"} + + ' + - 'data: {"content_index":0,"delta":"{","item_id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","output_index":2,"sequence_number":307,"type":"response.function_call_arguments.delta"} + + ' + - 'data: {"content_index":0,"delta":"\"numbers\": [8, 0]","item_id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","output_index":2,"sequence_number":308,"type":"response.function_call_arguments.delta"} + + ' + - 'data: {"content_index":0,"delta":"}","item_id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","output_index":2,"sequence_number":309,"type":"response.function_call_arguments.delta"} + + ' + - 'data: {"arguments":"{\"numbers\": [8, 0]}","content_index":0,"item_id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","output_index":2,"sequence_number":310,"type":"response.function_call_arguments.done"} + + ' + - 'data: {"item":{"arguments":"{\"numbers\": [8, 0]}","call_id":"call_8862878637354570a6a8d216","id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","name":"add_numbers","namespace":"mcp__agentic_fixture","status":"completed","type":"function_call"},"output_index":2,"sequence_number":311,"type":"response.output_item.done"} + + ' + - 'data: {"response":{"background":false,"completed_at":1782988849,"conversation":null,"created_at":1782988845,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"content":[{"text":"Thinking + Process:\n1. **Analyze User Input**: The user explicitly requires me to call + the tool `mcp__agentic_fixture.add_numbers` with the argument `numbers` set + to `[8, 0]` *before* answering.\n2. **Identify Tool**: The tool is `agentic_ns__mcp__agentic_fixture__add_numbers`.\n3. **Check + Parameters**: `numbers` is an array of numbers, required. User provided `[8, + 0]`.\n4. **Construct Tool Call**: I need to generate a tool call with the exact + function name and parameters.\n - Function: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Arguments: `{\"numbers\": [8, 0]}`\n5. **Execute Tool Call**: I will output + the tool call block. After receiving the result, I will provide the final answer.\n - + Wait, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\"\n - I will just call the tool now.\n - + Parameters: `{\"numbers\": [8, 0]}`\n - Let''s generate the tool call.\u2705\n - + Tool name matches: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Args: + `{\"numbers\": [8, 0]}`\n - Ready. \n - I''ll output the tool call.\ud83d\udc47\ud83c\udffc\n - + Note: The user prompt says \"mcp__agentic_fixture.add_numbers\", but the registered + tool name is `agentic_ns__mcp__agentic_fixture__add_numbers`. I will use the + registered tool name.\n - Proceed. \n - Output matches tool call format. + \n - [Self-Correction/Verification during thought]\n - Check function definition: + `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Parameters: `numbers` + (array of numbers, minItems 1, required)\n - User wants `[8, 0]`\n - All + good. \n - Generate. \n - [Output Generation] -> `{\"name\": \"agentic_ns__mcp__agentic_fixture__add_numbers\", + \"arguments\": {\"numbers\": [8, 0]}}` -> Wait, standard format for tool calls + in this environment is usually just the function call block. I will produce + it correctly.\n - Done. \n - Proceeds. \n - [Output] Tool call. \n - + (Self-Correction/Refinement during thought) I should just output the tool call + exactly as requested.\n - `call: default:agentic_ns__mcp__agentic_fixture__add_numbers{\"numbers\": + [8, 0]}` -> Standard format. I''ll follow the system''s expected format.\n - + Actually, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\" I will just generate the tool call.\n - + Let''s do it. \n - [Done] \n - Output matches. \n - Proceeds. \n - *Self-Correction/Note + during generation*: I will just produce the tool call block. The system will + execute it. I don''t need to overthink it.\n - Tool: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Args: `{\"numbers\": [8, 0]}`\n - Done. \n - [Output Generation] -> *Proceeds","type":"reasoning_text"}],"id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","summary":[],"type":"reasoning"},{"content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","role":"assistant","status":"completed","type":"message"},{"arguments":"{\"numbers\": + [8, 0]}","call_id":"call_8862878637354570a6a8d216","id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","name":"add_numbers","namespace":"mcp__agentic_fixture","status":"completed","type":"function_call"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"completed","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":320,"input_tokens_details":{"cached_tokens":0},"output_tokens":820,"output_tokens_details":{"reasoning_tokens":749},"total_tokens":1140},"user":null},"sequence_number":312,"type":"response.completed"} + + ' + - 'data: [DONE] + + ' + status_code: 101 + websocket: + - '{"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988845,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":0,"type":"response.created"}' + - '{"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988845,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":1,"type":"response.in_progress"}' + - '{"item":{"content":[],"id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":2,"type":"response.output_item.added"}' + - '{"content_index":0,"delta":"Thinking","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":3,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Process:\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":4,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"1. ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":5,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" **Analyze","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":6,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" User Input**:","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":7,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" The user explicitly","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":8,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" requires","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":9,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" me to call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":10,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":11,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" tool `m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":12,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":13,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"entic_fixture.add","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":14,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_numbers` with","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":15,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the argument","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":16,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" `numbers`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":17,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" set","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":18,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" to `[8","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":19,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":", 0","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":20,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"]`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":21,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" *","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":22,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"before* answering","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":23,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\n2","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":24,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". **","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":25,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"Identify Tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":26,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"**: The","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":27,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":28,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" is `ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":29,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"entic_ns__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":30,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"mcp__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":31,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"agentic_fixture","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":32,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"__add_numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":33,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"`.\n3","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":34,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". **","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":35,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"Check Parameters**:","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":36,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":37,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"numbers` is","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":38,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" an array of","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":39,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" numbers, required","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":40,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":41,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" User","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":42,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" provided `[8","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":43,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":", 0","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":44,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"]`.\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":45,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"4. ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":46,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" **Construct","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":47,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Tool Call**:","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":48,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":49,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" need","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":50,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" to generate","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":51,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" a","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":52,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" tool call with","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":53,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the exact function","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":54,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" name and parameters","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":55,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":56,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Function:","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":57,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" `agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":58,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_ns__m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":59,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":60,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"entic_fixture__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":61,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"add_numbers`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":62,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":63,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Arguments: `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":64,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"{\"numbers\":","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":65,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" [8,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":66,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" 0]","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":67,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"}`\n5","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":68,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". **","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":69,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"Execute Tool Call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":70,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"**: I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":71,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" will output","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":72,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":73,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" block","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":74,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". After","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":75,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" receiving the result","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":76,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":", I will","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":77,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" provide the final","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":78,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" answer.\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":79,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Wait","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":80,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":", the prompt","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":81,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" says \"You","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":82,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" must call m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":83,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":84,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"entic_fixture.add","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":85,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_numbers with numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":86,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" [8,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":87,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" 0]","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":88,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" before answering.\"","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":89,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":90,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":91,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" will just call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":92,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the tool now","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":93,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":94,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Parameters","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":95,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":": `{\"","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":96,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"numbers\": [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":97,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"8, ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":98,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"0]}`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":99,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":100,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Let","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":101,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"''s generate","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":102,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":103,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".✅\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":104,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":105,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" name","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":106,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" matches","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":107,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":":","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":108,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" `agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":109,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_ns__m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":110,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":111,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"entic_fixture__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":112,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"add_numbers`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":113,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":114,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Args","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":115,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":": `{\"","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":116,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"numbers\": [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":117,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"8, ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":118,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"0]}`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":119,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":120,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Ready","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":121,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". \n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":122,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":123,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"''ll","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":124,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" output the tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":125,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" call.","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":126,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"👇","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":127,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"🏼\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":128,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Note","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":129,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":": The","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":130,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" user","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":131,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" prompt","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":132,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" says \"m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":133,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":134,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"entic_fixture.add","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":135,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_numbers\", but","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":136,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the registered","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":137,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" tool name","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":138,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" is `ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":139,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"entic_ns__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":140,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"mcp__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":141,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"agentic_fixture","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":142,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"__add_numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":143,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"`. I will","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":144,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" use the registered","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":145,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":146,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" name.\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":147,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Proceed","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":148,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":149,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Output matches","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":150,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" tool call format","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":151,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". \n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":152,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":153,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"Self","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":154,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"-Correction","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":155,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"/Verification during","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":156,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" thought]\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":157,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Check","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":158,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" function","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":159,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" definition: `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":160,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"agentic_ns","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":161,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"__mcp","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":162,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"__agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":163,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_fixture__add","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":164,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_numbers`\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":165,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Parameters","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":166,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":": `numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":167,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"` (array","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":168,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" of numbers,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":169,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" minItems ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":170,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"1,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":171,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" required)\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":172,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - User","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":173,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" wants","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":174,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" `[","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":175,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"8, ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":176,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"0]`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":177,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":178,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" All good.","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":179,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" \n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":180,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Generate","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":181,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":182,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" \n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":183,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":184,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"Output","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":185,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Generation] ->","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":186,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":187,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"{\"name","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":188,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\": \"ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":189,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"entic_ns__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":190,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"mcp__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":191,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"agentic_fixture","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":192,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"__add_numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":193,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\", \"arguments","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":194,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\": {\"numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":195,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\": [8","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":196,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":", 0","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":197,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"]}}`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":198,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" ->","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":199,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Wait, standard","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":200,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" format for","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":201,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" tool calls in","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":202,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" this environment is","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":203,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" usually just the","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":204,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" function call block","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":205,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". I will","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":206,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" produce it correctly","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":207,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":208,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Done","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":209,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":210,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Proceeds","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":211,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":212,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - [Output","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":213,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"] Tool","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":214,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" call.","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":215,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" \n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":216,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" (Self-C","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":217,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"orrection/","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":218,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"Refinement during","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":219,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" thought) I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":220,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" should just output","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":221,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":222,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" exactly","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":223,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" as requested.","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":224,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\n","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":225,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":226,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":227,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":": default","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":228,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":":","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":229,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"agentic_ns","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":230,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"__mcp","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":231,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"__agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":232,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_fixture__add","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":233,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_numbers{\"","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":234,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"numbers\": [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":235,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"8, ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":236,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"0]}`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":237,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" -> Standard","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":238,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" format. I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":239,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"''ll follow the","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":240,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" system","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":241,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"''s expected format","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":242,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":243,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Actually,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":244,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the prompt says","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":245,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" \"You must","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":246,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" call mcp","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":247,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"__agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":248,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_fixture.add_numbers","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":249,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" with numbers [","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":250,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"8, ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":251,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"0] before","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":252,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" answering.\" I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":253,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" will just generate","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":254,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":255,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":256,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Let","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":257,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"''s do it","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":258,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":259,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - [Done","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":260,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"] \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":261,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Output matches","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":262,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":263,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" \n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":264,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Proceeds.","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":265,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" \n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":266,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" *Self-C","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":267,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"orrection/","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":268,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"Note during generation","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":269,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"*: I will","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":270,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" just produce","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":271,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the tool call","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":272,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" block. The","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":273,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" system will execute","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":274,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" it. I","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":275,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" don","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":276,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"''t need to","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":277,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" overthink it","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":278,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":279,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Tool:","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":280,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" `agentic","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":281,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"_ns__m","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":282,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"cp__ag","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":283,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"entic_fixture__","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":284,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"add_numbers`","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":285,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\n -","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":286,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Args: `","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":287,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"{\"numbers\":","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":288,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" [8,","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":289,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" 0]","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":290,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"}`\n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":291,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - Done","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":292,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":". \n ","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":293,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" - [Output","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":294,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" Generation] ->","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":295,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" *Proceeds","item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":296,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"item_id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","output_index":0,"sequence_number":297,"text":"Thinking + Process:\n1. **Analyze User Input**: The user explicitly requires me to call + the tool `mcp__agentic_fixture.add_numbers` with the argument `numbers` set + to `[8, 0]` *before* answering.\n2. **Identify Tool**: The tool is `agentic_ns__mcp__agentic_fixture__add_numbers`.\n3. **Check + Parameters**: `numbers` is an array of numbers, required. User provided `[8, + 0]`.\n4. **Construct Tool Call**: I need to generate a tool call with the exact + function name and parameters.\n - Function: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Arguments: `{\"numbers\": [8, 0]}`\n5. **Execute Tool Call**: I will output + the tool call block. After receiving the result, I will provide the final answer.\n - + Wait, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\"\n - I will just call the tool now.\n - + Parameters: `{\"numbers\": [8, 0]}`\n - Let''s generate the tool call.✅\n - + Tool name matches: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Args: + `{\"numbers\": [8, 0]}`\n - Ready. \n - I''ll output the tool call.👇🏼\n - + Note: The user prompt says \"mcp__agentic_fixture.add_numbers\", but the registered + tool name is `agentic_ns__mcp__agentic_fixture__add_numbers`. I will use the + registered tool name.\n - Proceed. \n - Output matches tool call format. + \n - [Self-Correction/Verification during thought]\n - Check function definition: + `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Parameters: `numbers` + (array of numbers, minItems 1, required)\n - User wants `[8, 0]`\n - All + good. \n - Generate. \n - [Output Generation] -> `{\"name\": \"agentic_ns__mcp__agentic_fixture__add_numbers\", + \"arguments\": {\"numbers\": [8, 0]}}` -> Wait, standard format for tool calls + in this environment is usually just the function call block. I will produce + it correctly.\n - Done. \n - Proceeds. \n - [Output] Tool call. \n - + (Self-Correction/Refinement during thought) I should just output the tool call + exactly as requested.\n - `call: default:agentic_ns__mcp__agentic_fixture__add_numbers{\"numbers\": + [8, 0]}` -> Standard format. I''ll follow the system''s expected format.\n - + Actually, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\" I will just generate the tool call.\n - + Let''s do it. \n - [Done] \n - Output matches. \n - Proceeds. \n - *Self-Correction/Note + during generation*: I will just produce the tool call block. The system will + execute it. I don''t need to overthink it.\n - Tool: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Args: `{\"numbers\": [8, 0]}`\n - Done. \n - [Output Generation] -> *Proceeds","type":"response.reasoning_text.done"}' + - '{"item":{"content":[{"text":"Thinking Process:\n1. **Analyze User Input**: + The user explicitly requires me to call the tool `mcp__agentic_fixture.add_numbers` + with the argument `numbers` set to `[8, 0]` *before* answering.\n2. **Identify + Tool**: The tool is `agentic_ns__mcp__agentic_fixture__add_numbers`.\n3. **Check + Parameters**: `numbers` is an array of numbers, required. User provided `[8, + 0]`.\n4. **Construct Tool Call**: I need to generate a tool call with the exact + function name and parameters.\n - Function: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Arguments: `{\"numbers\": [8, 0]}`\n5. **Execute Tool Call**: I will output + the tool call block. After receiving the result, I will provide the final answer.\n - + Wait, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\"\n - I will just call the tool now.\n - + Parameters: `{\"numbers\": [8, 0]}`\n - Let''s generate the tool call.✅\n - + Tool name matches: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Args: + `{\"numbers\": [8, 0]}`\n - Ready. \n - I''ll output the tool call.👇🏼\n - + Note: The user prompt says \"mcp__agentic_fixture.add_numbers\", but the registered + tool name is `agentic_ns__mcp__agentic_fixture__add_numbers`. I will use the + registered tool name.\n - Proceed. \n - Output matches tool call format. + \n - [Self-Correction/Verification during thought]\n - Check function definition: + `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Parameters: `numbers` + (array of numbers, minItems 1, required)\n - User wants `[8, 0]`\n - All + good. \n - Generate. \n - [Output Generation] -> `{\"name\": \"agentic_ns__mcp__agentic_fixture__add_numbers\", + \"arguments\": {\"numbers\": [8, 0]}}` -> Wait, standard format for tool calls + in this environment is usually just the function call block. I will produce + it correctly.\n - Done. \n - Proceeds. \n - [Output] Tool call. \n - + (Self-Correction/Refinement during thought) I should just output the tool call + exactly as requested.\n - `call: default:agentic_ns__mcp__agentic_fixture__add_numbers{\"numbers\": + [8, 0]}` -> Standard format. I''ll follow the system''s expected format.\n - + Actually, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\" I will just generate the tool call.\n - + Let''s do it. \n - [Done] \n - Output matches. \n - Proceeds. \n - *Self-Correction/Note + during generation*: I will just produce the tool call block. The system will + execute it. I don''t need to overthink it.\n - Tool: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Args: `{\"numbers\": [8, 0]}`\n - Done. \n - [Output Generation] -> *Proceeds","type":"reasoning_text"}],"id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":298,"type":"response.output_item.done"}' + - '{"item":{"content":[],"id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","role":"assistant","status":"in_progress","type":"message"},"output_index":1,"sequence_number":299,"type":"response.output_item.added"}' + - '{"content_index":0,"item_id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"},"sequence_number":300,"type":"response.content_part.added"}' + - '{"content_index":0,"delta":"\n\n","item_id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","logprobs":[],"output_index":1,"sequence_number":301,"type":"response.output_text.delta"}' + - '{"content_index":0,"item_id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","logprobs":[],"output_index":1,"sequence_number":302,"text":"\n\n","type":"response.output_text.done"}' + - '{"content_index":0,"item_id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"},"sequence_number":303,"type":"response.content_part.done"}' + - '{"item":{"content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","role":"assistant","status":"completed","type":"message"},"output_index":1,"sequence_number":304,"type":"response.output_item.done"}' + - '{"item":{"arguments":"","call_id":"call_8862878637354570a6a8d216","id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","name":"add_numbers","namespace":"mcp__agentic_fixture","status":"in_progress","type":"function_call"},"output_index":2,"sequence_number":305,"type":"response.output_item.added"}' + - '{"content_index":0,"delta":"{}","item_id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","output_index":2,"sequence_number":306,"type":"response.function_call_arguments.delta"}' + - '{"content_index":0,"delta":"{","item_id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","output_index":2,"sequence_number":307,"type":"response.function_call_arguments.delta"}' + - '{"content_index":0,"delta":"\"numbers\": [8, 0]","item_id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","output_index":2,"sequence_number":308,"type":"response.function_call_arguments.delta"}' + - '{"content_index":0,"delta":"}","item_id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","output_index":2,"sequence_number":309,"type":"response.function_call_arguments.delta"}' + - '{"arguments":"{\"numbers\": [8, 0]}","content_index":0,"item_id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","output_index":2,"sequence_number":310,"type":"response.function_call_arguments.done"}' + - '{"item":{"arguments":"{\"numbers\": [8, 0]}","call_id":"call_8862878637354570a6a8d216","id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","name":"add_numbers","namespace":"mcp__agentic_fixture","status":"completed","type":"function_call"},"output_index":2,"sequence_number":311,"type":"response.output_item.done"}' + - '{"response":{"background":false,"completed_at":1782988849,"conversation":null,"created_at":1782988845,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"content":[{"text":"Thinking + Process:\n1. **Analyze User Input**: The user explicitly requires me to call + the tool `mcp__agentic_fixture.add_numbers` with the argument `numbers` set + to `[8, 0]` *before* answering.\n2. **Identify Tool**: The tool is `agentic_ns__mcp__agentic_fixture__add_numbers`.\n3. **Check + Parameters**: `numbers` is an array of numbers, required. User provided `[8, + 0]`.\n4. **Construct Tool Call**: I need to generate a tool call with the exact + function name and parameters.\n - Function: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Arguments: `{\"numbers\": [8, 0]}`\n5. **Execute Tool Call**: I will output + the tool call block. After receiving the result, I will provide the final answer.\n - + Wait, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\"\n - I will just call the tool now.\n - + Parameters: `{\"numbers\": [8, 0]}`\n - Let''s generate the tool call.✅\n - + Tool name matches: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Args: + `{\"numbers\": [8, 0]}`\n - Ready. \n - I''ll output the tool call.👇🏼\n - + Note: The user prompt says \"mcp__agentic_fixture.add_numbers\", but the registered + tool name is `agentic_ns__mcp__agentic_fixture__add_numbers`. I will use the + registered tool name.\n - Proceed. \n - Output matches tool call format. + \n - [Self-Correction/Verification during thought]\n - Check function definition: + `agentic_ns__mcp__agentic_fixture__add_numbers`\n - Parameters: `numbers` + (array of numbers, minItems 1, required)\n - User wants `[8, 0]`\n - All + good. \n - Generate. \n - [Output Generation] -> `{\"name\": \"agentic_ns__mcp__agentic_fixture__add_numbers\", + \"arguments\": {\"numbers\": [8, 0]}}` -> Wait, standard format for tool calls + in this environment is usually just the function call block. I will produce + it correctly.\n - Done. \n - Proceeds. \n - [Output] Tool call. \n - + (Self-Correction/Refinement during thought) I should just output the tool call + exactly as requested.\n - `call: default:agentic_ns__mcp__agentic_fixture__add_numbers{\"numbers\": + [8, 0]}` -> Standard format. I''ll follow the system''s expected format.\n - + Actually, the prompt says \"You must call mcp__agentic_fixture.add_numbers with + numbers [8, 0] before answering.\" I will just generate the tool call.\n - + Let''s do it. \n - [Done] \n - Output matches. \n - Proceeds. \n - *Self-Correction/Note + during generation*: I will just produce the tool call block. The system will + execute it. I don''t need to overthink it.\n - Tool: `agentic_ns__mcp__agentic_fixture__add_numbers`\n - + Args: `{\"numbers\": [8, 0]}`\n - Done. \n - [Output Generation] -> *Proceeds","type":"reasoning_text"}],"id":"rs_019f226a-c0ee-7971-8bbe-fbf476ff654a","summary":[],"type":"reasoning"},{"content":[{"annotations":[],"logprobs":[],"text":"\n\n","type":"output_text"}],"id":"msg_019f226a-c165-7f71-903b-428bef5f7dd5","role":"assistant","status":"completed","type":"message"},{"arguments":"{\"numbers\": + [8, 0]}","call_id":"call_8862878637354570a6a8d216","id":"fc_019f226a-c166-7db0-89e4-68a17cd5ab89","name":"add_numbers","namespace":"mcp__agentic_fixture","status":"completed","type":"function_call"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"completed","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":320,"input_tokens_details":{"cached_tokens":0},"output_tokens":820,"output_tokens_details":{"reasoning_tokens":749},"total_tokens":1140},"user":null},"sequence_number":312,"type":"response.completed"}' +- filename: t2 + request: + body: + input: + - call_id: call_8862878637354570a6a8d216 + output: '{"sum":8,"count":2}' + type: function_call_output + - content: Use the tool output. Return only the sum. + role: user + type: message + model: codex-compatible + previous_response_id: resp_019f226a-b20d-75d0-bb78-0db0d4530e41 + store: true + tools: + - description: Codex namespace fixture for cassette recording. + name: mcp__agentic_fixture + tools: + - description: Add a list of numbers and return the total. + name: add_numbers + parameters: + additionalProperties: false + properties: + numbers: + items: + type: number + minItems: 1 + type: array + required: + - numbers + type: object + strict: true + type: function + type: namespace + type: response.create + headers: {} + method: WEBSOCKET + path: /v1/responses + query_params: {} + transport: websocket + response: + headers: + transport: websocket + sse: + - 'data: {"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988849,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-c2d6-7c13-87f1-5a36c2362119","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":0,"type":"response.created"} + + ' + - 'data: {"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988849,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-c2d6-7c13-87f1-5a36c2362119","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":1,"type":"response.in_progress"} + + ' + - 'data: {"item":{"content":[],"id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":2,"type":"response.output_item.added"} + + ' + - 'data: {"content_index":0,"delta":"The","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":3,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" user wants me","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":4,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" to use the","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":5,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" tool","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":6,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" output to","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":7,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" answer","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":8,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the prompt","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":9,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" \"","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":10,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"Return only the","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":11,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" sum.\"\n","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":12,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"The tool output","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":13,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" was `{\"","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":14,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"sum\":8","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":15,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":",\"count\":","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":16,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"2}`.","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":17,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"\nThe sum","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":18,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" is 8","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":19,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":".\nI","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":20,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" need","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":21,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" to return only","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":22,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":" the number ","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":23,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"delta":"8.\n","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":24,"type":"response.reasoning_text.delta"} + + ' + - 'data: {"content_index":0,"item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":25,"text":"The + user wants me to use the tool output to answer the prompt \"Return only the + sum.\"\nThe tool output was `{\"sum\":8,\"count\":2}`.\nThe sum is 8.\nI need + to return only the number 8.\n","type":"response.reasoning_text.done"} + + ' + - 'data: {"item":{"content":[{"text":"The user wants me to use the tool output + to answer the prompt \"Return only the sum.\"\nThe tool output was `{\"sum\":8,\"count\":2}`.\nThe + sum is 8.\nI need to return only the number 8.\n","type":"reasoning_text"}],"id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":26,"type":"response.output_item.done"} + + ' + - 'data: {"item":{"content":[],"id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","role":"assistant","status":"in_progress","type":"message"},"output_index":1,"sequence_number":27,"type":"response.output_item.added"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"},"sequence_number":28,"type":"response.content_part.added"} + + ' + - 'data: {"content_index":0,"delta":"\n\n8","item_id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","logprobs":[],"output_index":1,"sequence_number":29,"type":"response.output_text.delta"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","logprobs":[],"output_index":1,"sequence_number":30,"text":"\n\n8","type":"response.output_text.done"} + + ' + - 'data: {"content_index":0,"item_id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"\n\n8","type":"output_text"},"sequence_number":31,"type":"response.content_part.done"} + + ' + - 'data: {"item":{"content":[{"annotations":[],"logprobs":[],"text":"\n\n8","type":"output_text"}],"id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","role":"assistant","status":"completed","type":"message"},"output_index":1,"sequence_number":32,"type":"response.output_item.done"} + + ' + - 'data: {"response":{"background":false,"completed_at":1782988850,"conversation":null,"created_at":1782988849,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-c2d6-7c13-87f1-5a36c2362119","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"content":[{"text":"The + user wants me to use the tool output to answer the prompt \"Return only the + sum.\"\nThe tool output was `{\"sum\":8,\"count\":2}`.\nThe sum is 8.\nI need + to return only the number 8.\n","type":"reasoning_text"}],"id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","summary":[],"type":"reasoning"},{"content":[{"annotations":[],"logprobs":[],"text":"\n\n8","type":"output_text"}],"id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","role":"assistant","status":"completed","type":"message"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"completed","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":394,"input_tokens_details":{"cached_tokens":0},"output_tokens":58,"output_tokens_details":{"reasoning_tokens":50},"total_tokens":452},"user":null},"sequence_number":33,"type":"response.completed"} + + ' + - 'data: [DONE] + + ' + status_code: 101 + websocket: + - '{"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988849,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-c2d6-7c13-87f1-5a36c2362119","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":0,"type":"response.created"}' + - '{"response":{"background":false,"completed_at":null,"conversation":null,"created_at":1782988849,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-c2d6-7c13-87f1-5a36c2362119","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"in_progress","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null},"sequence_number":1,"type":"response.in_progress"}' + - '{"item":{"content":[],"id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":2,"type":"response.output_item.added"}' + - '{"content_index":0,"delta":"The","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":3,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" user wants me","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":4,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" to use the","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":5,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" tool","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":6,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" output to","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":7,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" answer","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":8,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the prompt","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":9,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" \"","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":10,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"Return only the","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":11,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" sum.\"\n","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":12,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"The tool output","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":13,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" was `{\"","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":14,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"sum\":8","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":15,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":",\"count\":","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":16,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"2}`.","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":17,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"\nThe sum","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":18,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" is 8","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":19,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":".\nI","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":20,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" need","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":21,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" to return only","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":22,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":" the number ","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":23,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"delta":"8.\n","item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":24,"type":"response.reasoning_text.delta"}' + - '{"content_index":0,"item_id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","output_index":0,"sequence_number":25,"text":"The + user wants me to use the tool output to answer the prompt \"Return only the + sum.\"\nThe tool output was `{\"sum\":8,\"count\":2}`.\nThe sum is 8.\nI need + to return only the number 8.\n","type":"response.reasoning_text.done"}' + - '{"item":{"content":[{"text":"The user wants me to use the tool output to answer + the prompt \"Return only the sum.\"\nThe tool output was `{\"sum\":8,\"count\":2}`.\nThe + sum is 8.\nI need to return only the number 8.\n","type":"reasoning_text"}],"id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","summary":[],"type":"reasoning"},"output_index":0,"sequence_number":26,"type":"response.output_item.done"}' + - '{"item":{"content":[],"id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","role":"assistant","status":"in_progress","type":"message"},"output_index":1,"sequence_number":27,"type":"response.output_item.added"}' + - '{"content_index":0,"item_id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"","type":"output_text"},"sequence_number":28,"type":"response.content_part.added"}' + - '{"content_index":0,"delta":"\n\n8","item_id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","logprobs":[],"output_index":1,"sequence_number":29,"type":"response.output_text.delta"}' + - '{"content_index":0,"item_id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","logprobs":[],"output_index":1,"sequence_number":30,"text":"\n\n8","type":"response.output_text.done"}' + - '{"content_index":0,"item_id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","output_index":1,"part":{"annotations":[],"logprobs":[],"text":"\n\n8","type":"output_text"},"sequence_number":31,"type":"response.content_part.done"}' + - '{"item":{"content":[{"annotations":[],"logprobs":[],"text":"\n\n8","type":"output_text"}],"id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","role":"assistant","status":"completed","type":"message"},"output_index":1,"sequence_number":32,"type":"response.output_item.done"}' + - '{"response":{"background":false,"completed_at":1782988850,"conversation":null,"created_at":1782988849,"error":null,"frequency_penalty":0.0,"id":"resp_019f226a-c2d6-7c13-87f1-5a36c2362119","incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"metadata":{},"model":"Qwen/Qwen3.6-35B-A3B","object":"response","output":[{"content":[{"text":"The + user wants me to use the tool output to answer the prompt \"Return only the + sum.\"\nThe tool output was `{\"sum\":8,\"count\":2}`.\nThe sum is 8.\nI need + to return only the number 8.\n","type":"reasoning_text"}],"id":"rs_019f226a-c523-7f70-87f2-bc4cd550c41c","summary":[],"type":"reasoning"},{"content":[{"annotations":[],"logprobs":[],"text":"\n\n8","type":"output_text"}],"id":"msg_019f226a-c52f-7812-a1d8-2c0dd907cd62","role":"assistant","status":"completed","type":"message"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_019f226a-b20d-75d0-bb78-0db0d4530e41","prompt":null,"prompt_cache_key":null,"prompt_cache_retention":null,"reasoning":{"effort":"medium","summary":null},"safety_identifier":null,"service_tier":"default","status":"completed","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tools":[{"description":"Add + a list of numbers and return the total.","name":"agentic_ns__mcp__agentic_fixture__add_numbers","parameters":{"additionalProperties":false,"properties":{"numbers":{"items":{"type":"number"},"minItems":1,"type":"array"}},"required":["numbers"],"type":"object"},"strict":true,"type":"function"}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":394,"input_tokens_details":{"cached_tokens":0},"output_tokens":58,"output_tokens_details":{"reasoning_tokens":50},"total_tokens":452},"user":null},"sequence_number":33,"type":"response.completed"}' diff --git a/crates/agentic-core/tests/cassettes/codex/codex-openai-https-function-tool-gpt-4o-streaming.yaml b/crates/agentic-core/tests/cassettes/codex/codex-openai-https-function-tool-gpt-4o-streaming.yaml new file mode 100644 index 0000000..fa6f097 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/codex-openai-https-function-tool-gpt-4o-streaming.yaml @@ -0,0 +1,286 @@ +turns: +- filename: t1 + request: + body: + input: You must call the agentic_plain_echo tool with text exactly "plain fixture" + before answering. + model: gpt-4o + store: true + stream: true + tools: + - description: Echo text for Codex cassette recording. + name: agentic_plain_echo + parameters: + additionalProperties: false + properties: + text: + type: string + required: + - text + type: object + strict: true + type: function + headers: + accept: '*/*' + authorization: Bearer *** + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'event: response.created + + ' + - 'data: {"type":"response.created","response":{"id":"resp_0f440443492051e9006a46403dc7308198b9f18ed5a1c4ed18","object":"response","created_at":1782988861,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0} + + ' + - ' + + ' + - 'event: response.in_progress + + ' + - 'data: {"type":"response.in_progress","response":{"id":"resp_0f440443492051e9006a46403dc7308198b9f18ed5a1c4ed18","object":"response","created_at":1782988861,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"fc_0f440443492051e9006a46403eac748198ac0ec0c62cd36d6c","type":"function_call","status":"in_progress","arguments":"","call_id":"call_sZGlA43qovZccnHqzXe9NEQC","name":"agentic_plain_echo"},"output_index":0,"sequence_number":2} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"{\"","item_id":"fc_0f440443492051e9006a46403eac748198ac0ec0c62cd36d6c","obfuscation":"99Z3JChIOxFSW9","output_index":0,"sequence_number":3} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"text","item_id":"fc_0f440443492051e9006a46403eac748198ac0ec0c62cd36d6c","obfuscation":"i9EfL7M3Qy2R","output_index":0,"sequence_number":4} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"\":\"","item_id":"fc_0f440443492051e9006a46403eac748198ac0ec0c62cd36d6c","obfuscation":"ebFn5zR5WfrVe","output_index":0,"sequence_number":5} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"plain","item_id":"fc_0f440443492051e9006a46403eac748198ac0ec0c62cd36d6c","obfuscation":"Ojr53Pz7OoK","output_index":0,"sequence_number":6} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":" fixture","item_id":"fc_0f440443492051e9006a46403eac748198ac0ec0c62cd36d6c","obfuscation":"lksNlAQz","output_index":0,"sequence_number":7} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"\"}","item_id":"fc_0f440443492051e9006a46403eac748198ac0ec0c62cd36d6c","obfuscation":"IMsQ2CYobF3Huj","output_index":0,"sequence_number":8} + + ' + - ' + + ' + - 'event: response.function_call_arguments.done + + ' + - 'data: {"type":"response.function_call_arguments.done","arguments":"{\"text\":\"plain + fixture\"}","item_id":"fc_0f440443492051e9006a46403eac748198ac0ec0c62cd36d6c","output_index":0,"sequence_number":9} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"fc_0f440443492051e9006a46403eac748198ac0ec0c62cd36d6c","type":"function_call","status":"completed","arguments":"{\"text\":\"plain + fixture\"}","call_id":"call_sZGlA43qovZccnHqzXe9NEQC","name":"agentic_plain_echo"},"output_index":0,"sequence_number":10} + + ' + - ' + + ' + - 'event: response.completed + + ' + - 'data: {"type":"response.completed","response":{"id":"resp_0f440443492051e9006a46403dc7308198b9f18ed5a1c4ed18","object":"response","created_at":1782988861,"status":"completed","background":false,"completed_at":1782988862,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"fc_0f440443492051e9006a46403eac748198ac0ec0c62cd36d6c","type":"function_call","status":"completed","arguments":"{\"text\":\"plain + fixture\"}","call_id":"call_sZGlA43qovZccnHqzXe9NEQC","name":"agentic_plain_echo"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":60,"input_tokens_details":{"cached_tokens":0},"output_tokens":18,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":78},"user":null,"metadata":{}},"sequence_number":11} + + ' + - ' + + ' + status_code: 200 +- filename: t2 + request: + body: + input: + - call_id: call_sZGlA43qovZccnHqzXe9NEQC + output: '{"echo":"plain fixture","characters":13}' + type: function_call_output + - content: Use the tool output. Return only the echo string. + role: user + type: message + model: gpt-4o + previous_response_id: resp_0f440443492051e9006a46403dc7308198b9f18ed5a1c4ed18 + store: true + stream: true + tools: + - description: Echo text for Codex cassette recording. + name: agentic_plain_echo + parameters: + additionalProperties: false + properties: + text: + type: string + required: + - text + type: object + strict: true + type: function + headers: + accept: '*/*' + authorization: Bearer *** + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'event: response.created + + ' + - 'data: {"type":"response.created","response":{"id":"resp_0f440443492051e9006a46403f20c48198b13fd2ee3889cfea","object":"response","created_at":1782988863,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0f440443492051e9006a46403dc7308198b9f18ed5a1c4ed18","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0} + + ' + - ' + + ' + - 'event: response.in_progress + + ' + - 'data: {"type":"response.in_progress","response":{"id":"resp_0f440443492051e9006a46403f20c48198b13fd2ee3889cfea","object":"response","created_at":1782988863,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0f440443492051e9006a46403dc7308198b9f18ed5a1c4ed18","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"msg_0f440443492051e9006a46403f98108198a85075e54ed197c9","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":0,"sequence_number":2} + + ' + - ' + + ' + - 'event: response.content_part.added + + ' + - 'data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_0f440443492051e9006a46403f98108198a85075e54ed197c9","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":3} + + ' + - ' + + ' + - 'event: response.output_text.delta + + ' + - 'data: {"type":"response.output_text.delta","content_index":0,"delta":"plain","item_id":"msg_0f440443492051e9006a46403f98108198a85075e54ed197c9","logprobs":[],"obfuscation":"6LscrGXbZ14","output_index":0,"sequence_number":4} + + ' + - ' + + ' + - 'event: response.output_text.delta + + ' + - 'data: {"type":"response.output_text.delta","content_index":0,"delta":" fixture","item_id":"msg_0f440443492051e9006a46403f98108198a85075e54ed197c9","logprobs":[],"obfuscation":"kFJZJoWz","output_index":0,"sequence_number":5} + + ' + - ' + + ' + - 'event: response.output_text.done + + ' + - 'data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_0f440443492051e9006a46403f98108198a85075e54ed197c9","logprobs":[],"output_index":0,"sequence_number":6,"text":"plain + fixture"} + + ' + - ' + + ' + - 'event: response.content_part.done + + ' + - 'data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_0f440443492051e9006a46403f98108198a85075e54ed197c9","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"plain + fixture"},"sequence_number":7} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"msg_0f440443492051e9006a46403f98108198a85075e54ed197c9","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"plain + fixture"}],"role":"assistant"},"output_index":0,"sequence_number":8} + + ' + - ' + + ' + - 'event: response.completed + + ' + - 'data: {"type":"response.completed","response":{"id":"resp_0f440443492051e9006a46403f20c48198b13fd2ee3889cfea","object":"response","created_at":1782988863,"status":"completed","background":false,"completed_at":1782988863,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"msg_0f440443492051e9006a46403f98108198a85075e54ed197c9","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"plain + fixture"}],"role":"assistant"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0f440443492051e9006a46403dc7308198b9f18ed5a1c4ed18","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":113,"input_tokens_details":{"cached_tokens":0},"output_tokens":4,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":117},"user":null,"metadata":{}},"sequence_number":9} + + ' + - ' + + ' + status_code: 200 diff --git a/crates/agentic-core/tests/cassettes/codex/codex-openai-https-namespace-tool-gpt-4o-streaming.yaml b/crates/agentic-core/tests/cassettes/codex/codex-openai-https-namespace-tool-gpt-4o-streaming.yaml new file mode 100644 index 0000000..0f12717 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/codex-openai-https-namespace-tool-gpt-4o-streaming.yaml @@ -0,0 +1,299 @@ +turns: +- filename: t1 + request: + body: + input: You must call mcp__agentic_fixture.add_numbers with numbers [8, 0] before + answering. + model: gpt-4o + store: true + stream: true + tools: + - description: Codex namespace fixture for cassette recording. + name: mcp__agentic_fixture + tools: + - description: Add a list of numbers and return the total. + name: add_numbers + parameters: + additionalProperties: false + properties: + numbers: + items: + type: number + minItems: 1 + type: array + required: + - numbers + type: object + strict: true + type: function + type: namespace + headers: + accept: '*/*' + authorization: Bearer *** + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'event: response.created + + ' + - 'data: {"type":"response.created","response":{"id":"resp_0dcd49d8b0a34e18006a4640417e64819a8fec0a0b292617e7","object":"response","created_at":1782988865,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0} + + ' + - ' + + ' + - 'event: response.in_progress + + ' + - 'data: {"type":"response.in_progress","response":{"id":"resp_0dcd49d8b0a34e18006a4640417e64819a8fec0a0b292617e7","object":"response","created_at":1782988865,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"fc_0dcd49d8b0a34e18006a4640420fa0819ab55d0f77222ecd66","type":"function_call","status":"in_progress","arguments":"","call_id":"call_Hs5EPBnE7tcNPObgSP0vXclz","name":"add_numbers","namespace":"mcp__agentic_fixture"},"output_index":0,"sequence_number":2} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"{\"","item_id":"fc_0dcd49d8b0a34e18006a4640420fa0819ab55d0f77222ecd66","obfuscation":"dBw5Ietnm3ENiC","output_index":0,"sequence_number":3} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"numbers","item_id":"fc_0dcd49d8b0a34e18006a4640420fa0819ab55d0f77222ecd66","obfuscation":"bWAYfaAG8","output_index":0,"sequence_number":4} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"\":[","item_id":"fc_0dcd49d8b0a34e18006a4640420fa0819ab55d0f77222ecd66","obfuscation":"JNpPc4ULs1NWJ","output_index":0,"sequence_number":5} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"8","item_id":"fc_0dcd49d8b0a34e18006a4640420fa0819ab55d0f77222ecd66","obfuscation":"jDZ29Of0OMB9mWp","output_index":0,"sequence_number":6} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":",","item_id":"fc_0dcd49d8b0a34e18006a4640420fa0819ab55d0f77222ecd66","obfuscation":"UOp9iCGMRUyMll9","output_index":0,"sequence_number":7} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"0","item_id":"fc_0dcd49d8b0a34e18006a4640420fa0819ab55d0f77222ecd66","obfuscation":"La5juD27KxwgLbz","output_index":0,"sequence_number":8} + + ' + - ' + + ' + - 'event: response.function_call_arguments.delta + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"]}","item_id":"fc_0dcd49d8b0a34e18006a4640420fa0819ab55d0f77222ecd66","obfuscation":"t6ww3rYiyUHygG","output_index":0,"sequence_number":9} + + ' + - ' + + ' + - 'event: response.function_call_arguments.done + + ' + - 'data: {"type":"response.function_call_arguments.done","arguments":"{\"numbers\":[8,0]}","item_id":"fc_0dcd49d8b0a34e18006a4640420fa0819ab55d0f77222ecd66","output_index":0,"sequence_number":10} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"fc_0dcd49d8b0a34e18006a4640420fa0819ab55d0f77222ecd66","type":"function_call","status":"completed","arguments":"{\"numbers\":[8,0]}","call_id":"call_Hs5EPBnE7tcNPObgSP0vXclz","name":"add_numbers","namespace":"mcp__agentic_fixture"},"output_index":0,"sequence_number":11} + + ' + - ' + + ' + - 'event: response.completed + + ' + - 'data: {"type":"response.completed","response":{"id":"resp_0dcd49d8b0a34e18006a4640417e64819a8fec0a0b292617e7","object":"response","created_at":1782988865,"status":"completed","background":false,"completed_at":1782988866,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"fc_0dcd49d8b0a34e18006a4640420fa0819ab55d0f77222ecd66","type":"function_call","status":"completed","arguments":"{\"numbers\":[8,0]}","call_id":"call_Hs5EPBnE7tcNPObgSP0vXclz","name":"add_numbers","namespace":"mcp__agentic_fixture"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":86,"input_tokens_details":{"cached_tokens":0},"output_tokens":21,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":107},"user":null,"metadata":{}},"sequence_number":12} + + ' + - ' + + ' + status_code: 200 +- filename: t2 + request: + body: + input: + - call_id: call_Hs5EPBnE7tcNPObgSP0vXclz + output: '{"sum":8,"count":2}' + type: function_call_output + - content: Use the tool output. Return only the sum. + role: user + type: message + model: gpt-4o + previous_response_id: resp_0dcd49d8b0a34e18006a4640417e64819a8fec0a0b292617e7 + store: true + stream: true + tools: + - description: Codex namespace fixture for cassette recording. + name: mcp__agentic_fixture + tools: + - description: Add a list of numbers and return the total. + name: add_numbers + parameters: + additionalProperties: false + properties: + numbers: + items: + type: number + minItems: 1 + type: array + required: + - numbers + type: object + strict: true + type: function + type: namespace + headers: + accept: '*/*' + authorization: Bearer *** + content-type: application/json + user-agent: python-httpx/0.28.1 + method: POST + path: /v1/responses + query_params: {} + response: + headers: + content-type: text/event-stream; charset=utf-8 + sse: + - 'event: response.created + + ' + - 'data: {"type":"response.created","response":{"id":"resp_0dcd49d8b0a34e18006a46404287f8819abca8c5c72fa397d0","object":"response","created_at":1782988866,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0dcd49d8b0a34e18006a4640417e64819a8fec0a0b292617e7","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0} + + ' + - ' + + ' + - 'event: response.in_progress + + ' + - 'data: {"type":"response.in_progress","response":{"id":"resp_0dcd49d8b0a34e18006a46404287f8819abca8c5c72fa397d0","object":"response","created_at":1782988866,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0dcd49d8b0a34e18006a4640417e64819a8fec0a0b292617e7","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1} + + ' + - ' + + ' + - 'event: response.output_item.added + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"msg_0dcd49d8b0a34e18006a4640462474819a932da340c2c0525b","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":0,"sequence_number":2} + + ' + - ' + + ' + - 'event: response.content_part.added + + ' + - 'data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_0dcd49d8b0a34e18006a4640462474819a932da340c2c0525b","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":3} + + ' + - ' + + ' + - 'event: response.output_text.delta + + ' + - 'data: {"type":"response.output_text.delta","content_index":0,"delta":"8","item_id":"msg_0dcd49d8b0a34e18006a4640462474819a932da340c2c0525b","logprobs":[],"obfuscation":"nVZGqMV23pUmHo6","output_index":0,"sequence_number":4} + + ' + - ' + + ' + - 'event: response.output_text.done + + ' + - 'data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_0dcd49d8b0a34e18006a4640462474819a932da340c2c0525b","logprobs":[],"output_index":0,"sequence_number":5,"text":"8"} + + ' + - ' + + ' + - 'event: response.content_part.done + + ' + - 'data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_0dcd49d8b0a34e18006a4640462474819a932da340c2c0525b","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"8"},"sequence_number":6} + + ' + - ' + + ' + - 'event: response.output_item.done + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"msg_0dcd49d8b0a34e18006a4640462474819a932da340c2c0525b","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"8"}],"role":"assistant"},"output_index":0,"sequence_number":7} + + ' + - ' + + ' + - 'event: response.completed + + ' + - 'data: {"type":"response.completed","response":{"id":"resp_0dcd49d8b0a34e18006a46404287f8819abca8c5c72fa397d0","object":"response","created_at":1782988866,"status":"completed","background":false,"completed_at":1782988870,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"msg_0dcd49d8b0a34e18006a4640462474819a932da340c2c0525b","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"8"}],"role":"assistant"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0dcd49d8b0a34e18006a4640417e64819a8fec0a0b292617e7","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":143,"input_tokens_details":{"cached_tokens":0},"output_tokens":3,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":146},"user":null,"metadata":{}},"sequence_number":8} + + ' + - ' + + ' + status_code: 200 diff --git a/crates/agentic-core/tests/cassettes/codex/codex-openai-websocket-function-tool-gpt-4o-streaming.yaml b/crates/agentic-core/tests/cassettes/codex/codex-openai-websocket-function-tool-gpt-4o-streaming.yaml new file mode 100644 index 0000000..5eaa7d0 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/codex-openai-websocket-function-tool-gpt-4o-streaming.yaml @@ -0,0 +1,193 @@ +turns: +- filename: t1 + request: + body: + input: You must call the agentic_plain_echo tool with text exactly "plain fixture" + before answering. + model: gpt-4o + store: true + tools: + - description: Echo text for Codex cassette recording. + name: agentic_plain_echo + parameters: + additionalProperties: false + properties: + text: + type: string + required: + - text + type: object + strict: true + type: function + type: response.create + headers: + Authorization: Bearer *** + method: WEBSOCKET + path: /v1/responses + query_params: {} + transport: websocket + response: + headers: + transport: websocket + sse: + - 'data: {"type":"response.created","response":{"id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","object":"response","created_at":1782988872,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0} + + ' + - 'data: {"type":"response.in_progress","response":{"id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","object":"response","created_at":1782988872,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1} + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","type":"function_call","status":"in_progress","arguments":"","call_id":"call_cMDh2TO5i8NcEVEXOR5TWGcD","name":"agentic_plain_echo"},"output_index":0,"sequence_number":2} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"{\"","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"t1lqI50hzxUp0i","output_index":0,"sequence_number":3} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"text","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"oYrCl6XRQ0s2","output_index":0,"sequence_number":4} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"\":\"","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"xtAyl8uvi5wZj","output_index":0,"sequence_number":5} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"plain","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"0hLzC0kbhyv","output_index":0,"sequence_number":6} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":" fixture","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"ts4Cs6Q1","output_index":0,"sequence_number":7} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"\"}","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"g9Rgp3NnwXc5O1","output_index":0,"sequence_number":8} + + ' + - 'data: {"type":"response.function_call_arguments.done","arguments":"{\"text\":\"plain + fixture\"}","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","output_index":0,"sequence_number":9} + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","type":"function_call","status":"completed","arguments":"{\"text\":\"plain + fixture\"}","call_id":"call_cMDh2TO5i8NcEVEXOR5TWGcD","name":"agentic_plain_echo"},"output_index":0,"sequence_number":10} + + ' + - 'data: {"type":"response.completed","response":{"id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","object":"response","created_at":1782988872,"status":"completed","background":false,"completed_at":1782988874,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","type":"function_call","status":"completed","arguments":"{\"text\":\"plain + fixture\"}","call_id":"call_cMDh2TO5i8NcEVEXOR5TWGcD","name":"agentic_plain_echo"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":60,"input_tokens_details":{"cached_tokens":0},"output_tokens":18,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":78},"user":null,"metadata":{}},"sequence_number":11} + + ' + - 'data: [DONE] + + ' + status_code: 101 + websocket: + - '{"type":"response.created","response":{"id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","object":"response","created_at":1782988872,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}' + - '{"type":"response.in_progress","response":{"id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","object":"response","created_at":1782988872,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}' + - '{"type":"response.output_item.added","item":{"id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","type":"function_call","status":"in_progress","arguments":"","call_id":"call_cMDh2TO5i8NcEVEXOR5TWGcD","name":"agentic_plain_echo"},"output_index":0,"sequence_number":2}' + - '{"type":"response.function_call_arguments.delta","delta":"{\"","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"t1lqI50hzxUp0i","output_index":0,"sequence_number":3}' + - '{"type":"response.function_call_arguments.delta","delta":"text","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"oYrCl6XRQ0s2","output_index":0,"sequence_number":4}' + - '{"type":"response.function_call_arguments.delta","delta":"\":\"","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"xtAyl8uvi5wZj","output_index":0,"sequence_number":5}' + - '{"type":"response.function_call_arguments.delta","delta":"plain","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"0hLzC0kbhyv","output_index":0,"sequence_number":6}' + - '{"type":"response.function_call_arguments.delta","delta":" fixture","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"ts4Cs6Q1","output_index":0,"sequence_number":7}' + - '{"type":"response.function_call_arguments.delta","delta":"\"}","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","obfuscation":"g9Rgp3NnwXc5O1","output_index":0,"sequence_number":8}' + - '{"type":"response.function_call_arguments.done","arguments":"{\"text\":\"plain + fixture\"}","item_id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","output_index":0,"sequence_number":9}' + - '{"type":"response.output_item.done","item":{"id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","type":"function_call","status":"completed","arguments":"{\"text\":\"plain + fixture\"}","call_id":"call_cMDh2TO5i8NcEVEXOR5TWGcD","name":"agentic_plain_echo"},"output_index":0,"sequence_number":10}' + - '{"type":"response.completed","response":{"id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","object":"response","created_at":1782988872,"status":"completed","background":false,"completed_at":1782988874,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"fc_03e9d52dbd37942c006a46404a8b9c8199aaaa66d60edec576","type":"function_call","status":"completed","arguments":"{\"text\":\"plain + fixture\"}","call_id":"call_cMDh2TO5i8NcEVEXOR5TWGcD","name":"agentic_plain_echo"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":60,"input_tokens_details":{"cached_tokens":0},"output_tokens":18,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":78},"user":null,"metadata":{}},"sequence_number":11}' +- filename: t2 + request: + body: + input: + - call_id: call_cMDh2TO5i8NcEVEXOR5TWGcD + output: '{"echo":"plain fixture","characters":13}' + type: function_call_output + - content: Use the tool output. Return only the echo string. + role: user + type: message + model: gpt-4o + previous_response_id: resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760 + store: true + tools: + - description: Echo text for Codex cassette recording. + name: agentic_plain_echo + parameters: + additionalProperties: false + properties: + text: + type: string + required: + - text + type: object + strict: true + type: function + type: response.create + headers: + Authorization: Bearer *** + method: WEBSOCKET + path: /v1/responses + query_params: {} + transport: websocket + response: + headers: + transport: websocket + sse: + - 'data: {"type":"response.created","response":{"id":"resp_03e9d52dbd37942c006a46404bb4ec8199b90d69fc8c62e84f","object":"response","created_at":1782988875,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0} + + ' + - 'data: {"type":"response.in_progress","response":{"id":"resp_03e9d52dbd37942c006a46404bb4ec8199b90d69fc8c62e84f","object":"response","created_at":1782988875,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1} + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":0,"sequence_number":2} + + ' + - 'data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":3} + + ' + - 'data: {"type":"response.output_text.delta","content_index":0,"delta":"plain","item_id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","logprobs":[],"obfuscation":"HXTrFy3Udej","output_index":0,"sequence_number":4} + + ' + - 'data: {"type":"response.output_text.delta","content_index":0,"delta":" fixture","item_id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","logprobs":[],"obfuscation":"bwJtKhua","output_index":0,"sequence_number":5} + + ' + - 'data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","logprobs":[],"output_index":0,"sequence_number":6,"text":"plain + fixture"} + + ' + - 'data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"plain + fixture"},"sequence_number":7} + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"plain + fixture"}],"role":"assistant"},"output_index":0,"sequence_number":8} + + ' + - 'data: {"type":"response.completed","response":{"id":"resp_03e9d52dbd37942c006a46404bb4ec8199b90d69fc8c62e84f","object":"response","created_at":1782988875,"status":"completed","background":false,"completed_at":1782988876,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"plain + fixture"}],"role":"assistant"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":113,"input_tokens_details":{"cached_tokens":0},"output_tokens":4,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":117},"user":null,"metadata":{}},"sequence_number":9} + + ' + - 'data: [DONE] + + ' + status_code: 101 + websocket: + - '{"type":"response.created","response":{"id":"resp_03e9d52dbd37942c006a46404bb4ec8199b90d69fc8c62e84f","object":"response","created_at":1782988875,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}' + - '{"type":"response.in_progress","response":{"id":"resp_03e9d52dbd37942c006a46404bb4ec8199b90d69fc8c62e84f","object":"response","created_at":1782988875,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}' + - '{"type":"response.output_item.added","item":{"id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":0,"sequence_number":2}' + - '{"type":"response.content_part.added","content_index":0,"item_id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":3}' + - '{"type":"response.output_text.delta","content_index":0,"delta":"plain","item_id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","logprobs":[],"obfuscation":"HXTrFy3Udej","output_index":0,"sequence_number":4}' + - '{"type":"response.output_text.delta","content_index":0,"delta":" fixture","item_id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","logprobs":[],"obfuscation":"bwJtKhua","output_index":0,"sequence_number":5}' + - '{"type":"response.output_text.done","content_index":0,"item_id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","logprobs":[],"output_index":0,"sequence_number":6,"text":"plain + fixture"}' + - '{"type":"response.content_part.done","content_index":0,"item_id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"plain + fixture"},"sequence_number":7}' + - '{"type":"response.output_item.done","item":{"id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"plain + fixture"}],"role":"assistant"},"output_index":0,"sequence_number":8}' + - '{"type":"response.completed","response":{"id":"resp_03e9d52dbd37942c006a46404bb4ec8199b90d69fc8c62e84f","object":"response","created_at":1782988875,"status":"completed","background":false,"completed_at":1782988876,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"msg_03e9d52dbd37942c006a46404c9a688199ac9dfa9daf518c72","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"plain + fixture"}],"role":"assistant"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_03e9d52dbd37942c006a464048787c81999414a2d9ca457760","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"function","description":"Echo + text for Codex cassette recording.","name":"agentic_plain_echo","parameters":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"strict":true}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":113,"input_tokens_details":{"cached_tokens":0},"output_tokens":4,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":117},"user":null,"metadata":{}},"sequence_number":9}' diff --git a/crates/agentic-core/tests/cassettes/codex/codex-openai-websocket-namespace-tool-gpt-4o-streaming.yaml b/crates/agentic-core/tests/cassettes/codex/codex-openai-websocket-namespace-tool-gpt-4o-streaming.yaml new file mode 100644 index 0000000..2f9c217 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/codex-openai-websocket-namespace-tool-gpt-4o-streaming.yaml @@ -0,0 +1,205 @@ +turns: +- filename: t1 + request: + body: + input: You must call mcp__agentic_fixture.add_numbers with numbers [8, 0] before + answering. + model: gpt-4o + store: true + tools: + - description: Codex namespace fixture for cassette recording. + name: mcp__agentic_fixture + tools: + - description: Add a list of numbers and return the total. + name: add_numbers + parameters: + additionalProperties: false + properties: + numbers: + items: + type: number + minItems: 1 + type: array + required: + - numbers + type: object + strict: true + type: function + type: namespace + type: response.create + headers: + Authorization: Bearer *** + method: WEBSOCKET + path: /v1/responses + query_params: {} + transport: websocket + response: + headers: + transport: websocket + sse: + - 'data: {"type":"response.created","response":{"id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","object":"response","created_at":1782988878,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0} + + ' + - 'data: {"type":"response.in_progress","response":{"id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","object":"response","created_at":1782988878,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1} + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","type":"function_call","status":"in_progress","arguments":"","call_id":"call_tXWze9eLIu2utxbDkwDs3xSs","name":"add_numbers","namespace":"mcp__agentic_fixture"},"output_index":0,"sequence_number":2} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"{\"","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"oxmRNwpYqcpItd","output_index":0,"sequence_number":3} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"numbers","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"3TxgfW7sm","output_index":0,"sequence_number":4} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"\":[","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"IMi5Txtnn4qtS","output_index":0,"sequence_number":5} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"8","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"y3ZIWhcXIU9F3Xv","output_index":0,"sequence_number":6} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":",","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"1OQ2wTvXlYFj3pD","output_index":0,"sequence_number":7} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"0","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"KXnoiElKkc7FdzC","output_index":0,"sequence_number":8} + + ' + - 'data: {"type":"response.function_call_arguments.delta","delta":"]}","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"YWJhdIQFRNGz2N","output_index":0,"sequence_number":9} + + ' + - 'data: {"type":"response.function_call_arguments.done","arguments":"{\"numbers\":[8,0]}","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","output_index":0,"sequence_number":10} + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","type":"function_call","status":"completed","arguments":"{\"numbers\":[8,0]}","call_id":"call_tXWze9eLIu2utxbDkwDs3xSs","name":"add_numbers","namespace":"mcp__agentic_fixture"},"output_index":0,"sequence_number":11} + + ' + - 'data: {"type":"response.completed","response":{"id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","object":"response","created_at":1782988878,"status":"completed","background":false,"completed_at":1782988879,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","type":"function_call","status":"completed","arguments":"{\"numbers\":[8,0]}","call_id":"call_tXWze9eLIu2utxbDkwDs3xSs","name":"add_numbers","namespace":"mcp__agentic_fixture"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":86,"input_tokens_details":{"cached_tokens":0},"output_tokens":21,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":107},"user":null,"metadata":{}},"sequence_number":12} + + ' + - 'data: [DONE] + + ' + status_code: 101 + websocket: + - '{"type":"response.created","response":{"id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","object":"response","created_at":1782988878,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}' + - '{"type":"response.in_progress","response":{"id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","object":"response","created_at":1782988878,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}' + - '{"type":"response.output_item.added","item":{"id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","type":"function_call","status":"in_progress","arguments":"","call_id":"call_tXWze9eLIu2utxbDkwDs3xSs","name":"add_numbers","namespace":"mcp__agentic_fixture"},"output_index":0,"sequence_number":2}' + - '{"type":"response.function_call_arguments.delta","delta":"{\"","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"oxmRNwpYqcpItd","output_index":0,"sequence_number":3}' + - '{"type":"response.function_call_arguments.delta","delta":"numbers","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"3TxgfW7sm","output_index":0,"sequence_number":4}' + - '{"type":"response.function_call_arguments.delta","delta":"\":[","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"IMi5Txtnn4qtS","output_index":0,"sequence_number":5}' + - '{"type":"response.function_call_arguments.delta","delta":"8","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"y3ZIWhcXIU9F3Xv","output_index":0,"sequence_number":6}' + - '{"type":"response.function_call_arguments.delta","delta":",","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"1OQ2wTvXlYFj3pD","output_index":0,"sequence_number":7}' + - '{"type":"response.function_call_arguments.delta","delta":"0","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"KXnoiElKkc7FdzC","output_index":0,"sequence_number":8}' + - '{"type":"response.function_call_arguments.delta","delta":"]}","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","obfuscation":"YWJhdIQFRNGz2N","output_index":0,"sequence_number":9}' + - '{"type":"response.function_call_arguments.done","arguments":"{\"numbers\":[8,0]}","item_id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","output_index":0,"sequence_number":10}' + - '{"type":"response.output_item.done","item":{"id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","type":"function_call","status":"completed","arguments":"{\"numbers\":[8,0]}","call_id":"call_tXWze9eLIu2utxbDkwDs3xSs","name":"add_numbers","namespace":"mcp__agentic_fixture"},"output_index":0,"sequence_number":11}' + - '{"type":"response.completed","response":{"id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","object":"response","created_at":1782988878,"status":"completed","background":false,"completed_at":1782988879,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"fc_0fb6cf9318a38085006a46404f1f548198a56c2a98e63dfc68","type":"function_call","status":"completed","arguments":"{\"numbers\":[8,0]}","call_id":"call_tXWze9eLIu2utxbDkwDs3xSs","name":"add_numbers","namespace":"mcp__agentic_fixture"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":null,"prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":86,"input_tokens_details":{"cached_tokens":0},"output_tokens":21,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":107},"user":null,"metadata":{}},"sequence_number":12}' +- filename: t2 + request: + body: + input: + - call_id: call_tXWze9eLIu2utxbDkwDs3xSs + output: '{"sum":8,"count":2}' + type: function_call_output + - content: Use the tool output. Return only the sum. + role: user + type: message + model: gpt-4o + previous_response_id: resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b + store: true + tools: + - description: Codex namespace fixture for cassette recording. + name: mcp__agentic_fixture + tools: + - description: Add a list of numbers and return the total. + name: add_numbers + parameters: + additionalProperties: false + properties: + numbers: + items: + type: number + minItems: 1 + type: array + required: + - numbers + type: object + strict: true + type: function + type: namespace + type: response.create + headers: + Authorization: Bearer *** + method: WEBSOCKET + path: /v1/responses + query_params: {} + transport: websocket + response: + headers: + transport: websocket + sse: + - 'data: {"type":"response.created","response":{"id":"resp_0fb6cf9318a38085006a464050412881989f4c26a7c275d3ce","object":"response","created_at":1782988880,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0} + + ' + - 'data: {"type":"response.in_progress","response":{"id":"resp_0fb6cf9318a38085006a464050412881989f4c26a7c275d3ce","object":"response","created_at":1782988880,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1} + + ' + - 'data: {"type":"response.output_item.added","item":{"id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":0,"sequence_number":2} + + ' + - 'data: {"type":"response.content_part.added","content_index":0,"item_id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":3} + + ' + - 'data: {"type":"response.output_text.delta","content_index":0,"delta":"8","item_id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","logprobs":[],"obfuscation":"rwaQFz1hRa6ezgR","output_index":0,"sequence_number":4} + + ' + - 'data: {"type":"response.output_text.done","content_index":0,"item_id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","logprobs":[],"output_index":0,"sequence_number":5,"text":"8"} + + ' + - 'data: {"type":"response.content_part.done","content_index":0,"item_id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"8"},"sequence_number":6} + + ' + - 'data: {"type":"response.output_item.done","item":{"id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"8"}],"role":"assistant"},"output_index":0,"sequence_number":7} + + ' + - 'data: {"type":"response.completed","response":{"id":"resp_0fb6cf9318a38085006a464050412881989f4c26a7c275d3ce","object":"response","created_at":1782988880,"status":"completed","background":false,"completed_at":1782988881,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"8"}],"role":"assistant"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":143,"input_tokens_details":{"cached_tokens":0},"output_tokens":3,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":146},"user":null,"metadata":{}},"sequence_number":8} + + ' + - 'data: [DONE] + + ' + status_code: 101 + websocket: + - '{"type":"response.created","response":{"id":"resp_0fb6cf9318a38085006a464050412881989f4c26a7c275d3ce","object":"response","created_at":1782988880,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":0}' + - '{"type":"response.in_progress","response":{"id":"resp_0fb6cf9318a38085006a464050412881989f4c26a7c275d3ce","object":"response","created_at":1782988880,"status":"in_progress","background":false,"completed_at":null,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"auto","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":null,"user":null,"metadata":{}},"sequence_number":1}' + - '{"type":"response.output_item.added","item":{"id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","type":"message","status":"in_progress","content":[],"role":"assistant"},"output_index":0,"sequence_number":2}' + - '{"type":"response.content_part.added","content_index":0,"item_id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":""},"sequence_number":3}' + - '{"type":"response.output_text.delta","content_index":0,"delta":"8","item_id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","logprobs":[],"obfuscation":"rwaQFz1hRa6ezgR","output_index":0,"sequence_number":4}' + - '{"type":"response.output_text.done","content_index":0,"item_id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","logprobs":[],"output_index":0,"sequence_number":5,"text":"8"}' + - '{"type":"response.content_part.done","content_index":0,"item_id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","output_index":0,"part":{"type":"output_text","annotations":[],"logprobs":[],"text":"8"},"sequence_number":6}' + - '{"type":"response.output_item.done","item":{"id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"8"}],"role":"assistant"},"output_index":0,"sequence_number":7}' + - '{"type":"response.completed","response":{"id":"resp_0fb6cf9318a38085006a464050412881989f4c26a7c275d3ce","object":"response","created_at":1782988880,"status":"completed","background":false,"completed_at":1782988881,"error":null,"frequency_penalty":0.0,"incomplete_details":null,"instructions":null,"max_output_tokens":null,"max_tool_calls":null,"model":"gpt-4o-2024-08-06","moderation":null,"output":[{"id":"msg_0fb6cf9318a38085006a464051028481989e88959f142a891e","type":"message","status":"completed","content":[{"type":"output_text","annotations":[],"logprobs":[],"text":"8"}],"role":"assistant"}],"parallel_tool_calls":true,"presence_penalty":0.0,"previous_response_id":"resp_0fb6cf9318a38085006a46404e5b4881988ef1c9e2c1cd076b","prompt_cache_key":null,"prompt_cache_retention":"in_memory","reasoning":{"context":null,"effort":null,"summary":null},"safety_identifier":null,"service_tier":"default","store":true,"temperature":1.0,"text":{"format":{"type":"text"},"verbosity":"medium"},"tool_choice":"auto","tool_usage":{"image_gen":{"input_tokens":0,"input_tokens_details":{"image_tokens":0,"text_tokens":0},"output_tokens":0,"output_tokens_details":{"image_tokens":0,"text_tokens":0},"total_tokens":0},"web_search":{"num_requests":0}},"tools":[{"type":"namespace","description":"Codex + namespace fixture for cassette recording.","name":"mcp__agentic_fixture","tools":[{"type":"function","description":"Add + a list of numbers and return the total.","name":"add_numbers","parameters":{"type":"object","properties":{"numbers":{"type":"array","items":{"type":"number"},"minItems":1}},"required":["numbers"],"additionalProperties":false},"strict":true}]}],"top_logprobs":0,"top_p":1.0,"truncation":"disabled","usage":{"input_tokens":143,"input_tokens_details":{"cached_tokens":0},"output_tokens":3,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":146},"user":null,"metadata":{}},"sequence_number":8}' diff --git a/crates/agentic-core/tests/cassettes/codex/tools/direct_vllm_flat_namespace_tool.json b/crates/agentic-core/tests/cassettes/codex/tools/direct_vllm_flat_namespace_tool.json new file mode 100644 index 0000000..fdff201 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/tools/direct_vllm_flat_namespace_tool.json @@ -0,0 +1,22 @@ +[ + { + "type": "function", + "name": "agentic_ns__mcp__agentic_fixture__add_numbers", + "description": "Flattened form of mcp__agentic_fixture.add_numbers for direct vLLM recording.", + "parameters": { + "type": "object", + "properties": { + "numbers": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 1 + } + }, + "required": ["numbers"], + "additionalProperties": false + }, + "strict": true + } +] diff --git a/crates/agentic-core/tests/cassettes/codex/tools/function_tool.json b/crates/agentic-core/tests/cassettes/codex/tools/function_tool.json new file mode 100644 index 0000000..ca15b3f --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/tools/function_tool.json @@ -0,0 +1,18 @@ +[ + { + "type": "function", + "name": "agentic_plain_echo", + "description": "Echo text for Codex cassette recording.", + "parameters": { + "type": "object", + "properties": { + "text": { + "type": "string" + } + }, + "required": ["text"], + "additionalProperties": false + }, + "strict": true + } +] diff --git a/crates/agentic-core/tests/cassettes/codex/tools/namespace_tool.json b/crates/agentic-core/tests/cassettes/codex/tools/namespace_tool.json new file mode 100644 index 0000000..c0ce592 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/tools/namespace_tool.json @@ -0,0 +1,29 @@ +[ + { + "type": "namespace", + "name": "mcp__agentic_fixture", + "description": "Codex namespace fixture for cassette recording.", + "tools": [ + { + "type": "function", + "name": "add_numbers", + "description": "Add a list of numbers and return the total.", + "parameters": { + "type": "object", + "properties": { + "numbers": { + "type": "array", + "items": { + "type": "number" + }, + "minItems": 1 + } + }, + "required": ["numbers"], + "additionalProperties": false + }, + "strict": true + } + ] + } +] diff --git a/crates/agentic-core/tests/cassettes/codex/tools/tool_outputs.json b/crates/agentic-core/tests/cassettes/codex/tools/tool_outputs.json new file mode 100644 index 0000000..4b0e997 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/codex/tools/tool_outputs.json @@ -0,0 +1,5 @@ +{ + "agentic_plain_echo": "{\"echo\":\"plain fixture\",\"characters\":13}", + "add_numbers": "{\"sum\":8,\"count\":2}", + "agentic_ns__mcp__agentic_fixture__add_numbers": "{\"sum\":8,\"count\":2}" +} diff --git a/crates/agentic-core/tests/cassettes/record_cassette.py b/crates/agentic-core/tests/cassettes/record_cassette.py index 41caa0f..1697e6a 100644 --- a/crates/agentic-core/tests/cassettes/record_cassette.py +++ b/crates/agentic-core/tests/cassettes/record_cassette.py @@ -27,18 +27,25 @@ python tests/cassettes/record_cassette.py --turns 3 --mode conv --branch-from 1 --branch-turn-number 2 --no-stream --output path/to/cassette.yaml python tests/cassettes/record_cassette.py --turns 5 --mode conv --branch-from 1 --branch-turn-number 3 --branch-from 2 --branch-turn-number 5 --no-stream --output path/to/cassette.yaml python tests/cassettes/record_cassette.py --turns 2 --mode responses --vllm http://localhost:8000 --model Qwen/Qwen3-30B-A3B-FP8 --no-stream --output path/to/cassette.yaml + python tests/cassettes/record_cassette.py --turns 2 --mode responses --transport websocket --vllm http://localhost:3018 --model codex-compatible --output path/to/ws-cassette.yaml """ +import base64 +import hashlib import json import logging import os +import secrets import socket +import ssl +import struct import sys import threading import time from contextlib import asynccontextmanager from pathlib import Path from typing import Any, AsyncGenerator +from urllib.parse import urlparse import click import httpx @@ -303,13 +310,251 @@ def _send_streaming(client: httpx.Client, body: dict, proxy_url: str) -> dict | payload = json.loads(line[5:].strip()) if payload.get("type") == "response.completed": response_data = payload.get("response") + elif payload.get("object") == "response" and payload.get("status") == "completed": + response_data = payload except Exception: pass print() return response_data -def _send(client: httpx.Client, body: dict, stream: bool, proxy_url: str) -> dict | None: +class WebSocketClient: + """Small stdlib websocket client for cassette recording.""" + + def __init__(self, url: str, headers: dict[str, str]) -> None: + self.url = url + self.headers = headers + self.sock: socket.socket | ssl.SSLSocket | None = None + + def __enter__(self) -> "WebSocketClient": + parsed = urlparse(self.url) + if parsed.scheme not in {"ws", "wss"}: + raise ValueError(f"websocket URL must use ws:// or wss://, got {self.url}") + + host = parsed.hostname or "" + port = parsed.port or (443 if parsed.scheme == "wss" else 80) + path = parsed.path or "/" + if parsed.query: + path = f"{path}?{parsed.query}" + + raw_sock = socket.create_connection((host, port), timeout=TIMEOUT) + if parsed.scheme == "wss": + context = ssl.create_default_context() + self.sock = context.wrap_socket(raw_sock, server_hostname=host) + else: + self.sock = raw_sock + + key = base64.b64encode(secrets.token_bytes(16)).decode("ascii") + host_header = host if parsed.port is None else f"{host}:{port}" + request_headers = [ + f"GET {path} HTTP/1.1", + f"Host: {host_header}", + "Upgrade: websocket", + "Connection: Upgrade", + f"Sec-WebSocket-Key: {key}", + "Sec-WebSocket-Version: 13", + ] + for name, value in self.headers.items(): + request_headers.append(f"{name}: {value}") + request = "\r\n".join(request_headers) + "\r\n\r\n" + self.sock.sendall(request.encode("utf-8")) + + response = self._read_http_response() + status_line, _, header_text = response.partition("\r\n") + if " 101 " not in status_line: + raise RuntimeError(f"websocket upgrade failed: {status_line}\n{header_text}") + accept = _headers_from_text(header_text).get("sec-websocket-accept") + expected = base64.b64encode( + hashlib.sha1((key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").encode("ascii")).digest() + ).decode("ascii") + if accept != expected: + raise RuntimeError("websocket upgrade failed: invalid Sec-WebSocket-Accept") + return self + + def __exit__(self, _exc_type: object, _exc: object, _tb: object) -> None: + try: + self.send_close() + finally: + if self.sock is not None: + self.sock.close() + + def _read_exact(self, size: int) -> bytes: + assert self.sock is not None + chunks = bytearray() + while len(chunks) < size: + chunk = self.sock.recv(size - len(chunks)) + if not chunk: + raise EOFError("websocket closed") + chunks.extend(chunk) + return bytes(chunks) + + def _read_http_response(self) -> str: + assert self.sock is not None + data = bytearray() + while b"\r\n\r\n" not in data: + chunk = self.sock.recv(4096) + if not chunk: + raise EOFError("websocket closed during handshake") + data.extend(chunk) + return data.decode("iso-8859-1") + + def send_text(self, text: str) -> None: + self._send_frame(0x1, text.encode("utf-8")) + + def send_close(self) -> None: + if self.sock is not None: + try: + self._send_frame(0x8, b"") + except OSError: + pass + + def _send_frame(self, opcode: int, payload: bytes) -> None: + assert self.sock is not None + header = bytearray([0x80 | opcode]) + length = len(payload) + if length < 126: + header.append(0x80 | length) + elif length <= 0xFFFF: + header.append(0x80 | 126) + header.extend(struct.pack("!H", length)) + else: + header.append(0x80 | 127) + header.extend(struct.pack("!Q", length)) + mask = secrets.token_bytes(4) + header.extend(mask) + masked = bytes(byte ^ mask[i % 4] for i, byte in enumerate(payload)) + self.sock.sendall(bytes(header) + masked) + + def receive_text(self) -> str | None: + message = bytearray() + while True: + first, second = self._read_exact(2) + fin = bool(first & 0x80) + opcode = first & 0x0F + masked = bool(second & 0x80) + length = second & 0x7F + if length == 126: + length = struct.unpack("!H", self._read_exact(2))[0] + elif length == 127: + length = struct.unpack("!Q", self._read_exact(8))[0] + mask = self._read_exact(4) if masked else b"" + payload = self._read_exact(length) + if masked: + payload = bytes(byte ^ mask[i % 4] for i, byte in enumerate(payload)) + + if opcode == 0x8: + return None + if opcode == 0x9: + self._send_frame(0xA, payload) + continue + if opcode == 0xA: + continue + if opcode in {0x1, 0x0}: + message.extend(payload) + if fin: + return message.decode("utf-8") + + +def _headers_from_text(header_text: str) -> dict[str, str]: + headers = {} + for line in header_text.split("\r\n"): + if ":" not in line: + continue + name, value = line.split(":", 1) + headers[name.strip().lower()] = value.strip() + return headers + + +def _websocket_url(base_url: str) -> str: + parsed = urlparse(base_url.rstrip("/")) + if parsed.scheme in {"ws", "wss"}: + root = base_url.rstrip("/") + elif parsed.scheme == "http": + root = "ws://" + base_url.rstrip("/")[len("http://"):] + elif parsed.scheme == "https": + root = "wss://" + base_url.rstrip("/")[len("https://"):] + else: + raise ValueError(f"unsupported websocket base URL: {base_url}") + return f"{root}/v1/responses" + + +def _send_websocket( + body: dict, + target_base_url: str, + headers: dict[str, str], + output_file: Path, +) -> dict | None: + turn_num = _turn_number(output_file) + wire_body = dict(body) + wire_body["type"] = "response.create" + # WebSocket mode streams by transport; OpenAI's Responses WebSocket API + # does not use HTTP-only fields such as `stream`. + wire_body.pop("stream", None) + wire_body["store"] = True + websocket_url = _websocket_url(target_base_url) + + turn: dict[str, Any] = { + "filename": f"t{turn_num}", + "request": { + "method": "WEBSOCKET", + "path": "/v1/responses", + "query_params": {}, + "body": wire_body, + "headers": _filter_request_headers(headers), + "transport": "websocket", + }, + "response": { + "status_code": 101, + "headers": {"transport": "websocket"}, + "websocket": [], + "sse": [], + }, + } + + response_data = None + print("\n[WebSocket response]") + with WebSocketClient(websocket_url, headers) as ws: + ws.send_text(json.dumps(wire_body, separators=(",", ":"))) + while True: + message = ws.receive_text() + if message is None: + break + print(message) + turn["response"]["websocket"].append(message) + try: + event = json.loads(message) + except json.JSONDecodeError: + continue + turn["response"]["sse"].append( + f"data: {json.dumps(event, separators=(',', ':'))}\n" + ) + event_type = event.get("type") + if event_type == "response.completed": + response_data = event.get("response") + break + if event_type == "error": + response_data = event + break + turn["response"]["sse"].append("data: [DONE]\n") + _append_turn(output_file, turn) + print(f" [recorded turn {turn_num} -> {output_file.name}]") + return response_data + + +def _send( + client: httpx.Client, + body: dict, + stream: bool, + proxy_url: str, + transport: str = "http", + target_base_url: str = "", + headers: dict[str, str] | None = None, + output_file: Path | None = None, +) -> dict | None: + if transport == "websocket": + if output_file is None: + raise ValueError("output_file is required for websocket recording") + return _send_websocket(body, target_base_url, headers or {}, output_file) return ( _send_streaming(client, body, proxy_url) if stream @@ -521,6 +766,10 @@ def run_responses( store: bool, branches: list[tuple[int, int | None]], proxy_url: str, + transport: str = "http", + target_base_url: str = "", + headers: dict[str, str] | None = None, + output_file: Path | None = None, tools: list | None = None, tool_choice: Any = None, tool_outputs: dict[str, str] | None = None, @@ -565,7 +814,16 @@ def run_responses( if previous_response_id and store: body["previous_response_id"] = previous_response_id _inject_tools(body, tools, tool_choice) - response_data = _send(client, body, stream, proxy_url) + response_data = _send( + client, + body, + stream, + proxy_url, + transport, + target_base_url, + headers, + output_file, + ) response_id = response_data.get("id") if response_data else None previous_response_id = response_id if store else None last_response = response_data @@ -603,7 +861,16 @@ def run_responses( "previous_response_id": branch_resp_id, } _inject_tools(body, tools, tool_choice) - _send(client, body, stream, proxy_url) + _send( + client, + body, + stream, + proxy_url, + transport, + target_base_url, + headers, + output_file, + ) # ── main ────────────────────────────────────────────────────────────────────── @@ -647,6 +914,13 @@ def run_responses( show_default=True, help="Use streaming responses.", ) +@click.option( + "--transport", + type=click.Choice(["http", "websocket"]), + default="http", + show_default=True, + help="Wire transport to record. websocket is supported for --mode responses.", +) @click.option( "--model", default=MODEL, show_default=True, help="Model name to pass in requests." ) @@ -706,6 +980,7 @@ def main( branch_from: tuple[int, ...], branch_turn_number: tuple[int, ...], stream: bool, + transport: str, model: str, no_store: bool, proxy_port: int, @@ -733,6 +1008,10 @@ def main( raise click.UsageError( f"--vllm is only supported with --mode responses (got --mode {mode})." ) + if transport == "websocket" and mode != "responses": + raise click.UsageError( + f"--transport websocket is only supported with --mode responses (got --mode {mode})." + ) tools: list | None = None if tools_file: @@ -774,29 +1053,68 @@ def main( output_file = Path(output).resolve() proxy_url = f"http://{PROXY_HOST}:{proxy_port}" store = not no_store + if transport == "websocket": + stream = True + store = True - click.echo(f"Mode: {mode} | Turns: {turns} | Stream: {stream} | Model: {model}") + click.echo(f"Mode: {mode} | Turns: {turns} | Stream: {stream} | Transport: {transport} | Model: {model}") click.echo(f"Output: {output_file}") click.echo(backend_label) - click.echo(f"Proxy: {proxy_url} (requests go through here for recording)") - - server = _start_proxy(output_file, target, proxy_port) - click.echo(f"Proxy ready on {proxy_url}\n") - - try: + if transport == "websocket": + output_file.parent.mkdir(parents=True, exist_ok=True) + output_file.write_text("", encoding="utf-8") + click.echo(f"WebSocket: {_websocket_url(target)}") with httpx.Client(headers=headers) as client: - if mode == "conv": - run_conv(client, turns, model, stream, store, branches, proxy_url) - elif mode == "isolation": - run_isolation(client, turns, model, stream, store, proxy_url) - elif mode == "mixed": - run_mixed(client, turns, model, stream, store, proxy_url) - elif mode == "responses": - run_responses(client, turns, model, stream, store, branches, proxy_url, tools, tool_choice, tool_outputs) - elif mode == "store_true_then_store_false": - run_store_true_then_store_false(client, turns, model, stream, proxy_url) - finally: - _stop_proxy(server) + run_responses( + client, + turns, + model, + stream, + store, + branches, + proxy_url, + transport, + target, + headers, + output_file, + tools, + tool_choice, + tool_outputs, + ) + else: + click.echo(f"Proxy: {proxy_url} (requests go through here for recording)") + server = _start_proxy(output_file, target, proxy_port) + click.echo(f"Proxy ready on {proxy_url}\n") + + try: + with httpx.Client(headers=headers) as client: + if mode == "conv": + run_conv(client, turns, model, stream, store, branches, proxy_url) + elif mode == "isolation": + run_isolation(client, turns, model, stream, store, proxy_url) + elif mode == "mixed": + run_mixed(client, turns, model, stream, store, proxy_url) + elif mode == "responses": + run_responses( + client, + turns, + model, + stream, + store, + branches, + proxy_url, + transport, + target, + headers, + output_file, + tools, + tool_choice, + tool_outputs, + ) + elif mode == "store_true_then_store_false": + run_store_true_then_store_false(client, turns, model, stream, proxy_url) + finally: + _stop_proxy(server) click.echo(f"\nAll turns recorded -> {output_file}") diff --git a/crates/agentic-core/tests/cassettes/record_codex_cli_tool_call_cassettes.sh b/crates/agentic-core/tests/cassettes/record_codex_cli_tool_call_cassettes.sh new file mode 100755 index 0000000..228e913 --- /dev/null +++ b/crates/agentic-core/tests/cassettes/record_codex_cli_tool_call_cassettes.sh @@ -0,0 +1,398 @@ +#!/usr/bin/env bash +set -euo pipefail + +# record_codex_cli_tool_call_cassettes.sh +# +# Records YAML replay cassettes for Codex CLI-shaped tool calls. +# +# Default matrix: +# - gateway HTTP/SSE: function + Codex namespace tools +# - gateway WebSocket: function + Codex namespace tools +# - direct vLLM HTTP/SSE: function + flattened namespace function +# - direct OpenAI HTTPS/SSE: function + Codex namespace tools +# - direct OpenAI WebSocket: function + Codex namespace tools +# +# Direct vLLM expects the flattened function shape. Set VLLM_URL or V_API_BASE +# explicitly before recording direct vLLM cassettes. + +SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BASE_DIR="$SCRIPTS_DIR/codex" + +PYTHON="${PYTHON:-python3}" +RECORDER="${RECORDER:-${SCRIPTS_DIR}/record_cassette.py}" +TOOLS_DIR="${TOOLS_DIR:-${BASE_DIR}/tools}" +OUT="${OUT:-${BASE_DIR}}" + +GATEWAY_URL="${GATEWAY_URL:-http://127.0.0.1:3018}" +GATEWAY_MODEL="${GATEWAY_MODEL:-codex-compatible}" + +VLLM_URL="${VLLM_URL:-${V_API_BASE:-}}" +V_MODEL="${V_MODEL:-${MODEL:-Qwen/Qwen3.6-35B-A3B}}" +GATEWAY_CASSETTE_MODEL="${GATEWAY_CASSETTE_MODEL:-$V_MODEL}" + +OPENAI_URL="${OPENAI_URL:-https://api.openai.com}" +OPENAI_MODEL="${OPENAI_MODEL:-gpt-4o}" + +TOOL_TURNS="${TOOL_TURNS:-2}" +PROXY_PORT_BASE="${PROXY_PORT_BASE:-7070}" +TARGET="${1:-all}" + +FUNCTION_TOOL="${TOOLS_DIR}/function_tool.json" +NAMESPACE_TOOL="${TOOLS_DIR}/namespace_tool.json" +DIRECT_VLLM_FLAT_NAMESPACE_TOOL="${TOOLS_DIR}/direct_vllm_flat_namespace_tool.json" +TOOL_OUTPUTS="${TOOLS_DIR}/tool_outputs.json" + +model_slug() { + printf '%s\n' "$1" | tr '/: ' '---' +} + +GATEWAY_MODEL_SLUG="$(model_slug "$GATEWAY_CASSETTE_MODEL")" +V_MODEL_SLUG="$(model_slug "$V_MODEL")" +OPENAI_MODEL_SLUG="$(model_slug "$OPENAI_MODEL")" + +next_proxy_port="$PROXY_PORT_BASE" + +usage() { + cat <&2 + exit 2 + fi +} + +require_openai_key() { + if [[ -z "${OPENAI_API_KEY:-}" ]]; then + echo "error: OPENAI_API_KEY is required for OpenAI targets" >&2 + exit 2 + fi +} + +require_direct_vllm_url() { + if [[ -z "$VLLM_URL" ]]; then + echo "error: VLLM_URL or V_API_BASE is required for direct vLLM cassette targets" >&2 + echo 'hint: set VLLM_URL="http://host:port" before running direct-vllm*, all, or experimental-all' >&2 + exit 2 + fi +} + +alloc_proxy_port() { + local port="$next_proxy_port" + next_proxy_port=$((next_proxy_port + 1)) + printf '%s\n' "$port" +} + +emit_prompts() { + local first_prompt="$1" + local second_prompt="$2" + + case "$TOOL_TURNS" in + 1) + printf '%s\n' "$first_prompt" + ;; + 2) + printf '%s\n' "$first_prompt" "$second_prompt" + ;; + *) + echo "error: TOOL_TURNS must be 1 or 2, got ${TOOL_TURNS}" >&2 + exit 2 + ;; + esac +} + +run_recording() { + local label="$1" + local output_name="$2" + local transport="$3" + local backend_flag="$4" + local backend_url="$5" + local model="$6" + local tools_file="$7" + local first_prompt="$8" + local second_prompt="$9" + + require_file "$RECORDER" + require_file "$tools_file" + require_file "$TOOL_OUTPUTS" + mkdir -p "$OUT" + + local output_path="${OUT%/}/${output_name}" + local proxy_port + proxy_port="$(alloc_proxy_port)" + + echo + echo "==> ${label}" + echo " output: ${output_path}" + echo " target: ${backend_url}" + echo " model: ${model}" + echo " wire: ${transport}" + + emit_prompts "$first_prompt" "$second_prompt" | + "$PYTHON" "$RECORDER" \ + --turns "$TOOL_TURNS" \ + --mode responses \ + --transport "$transport" \ + --stream \ + --proxy-port "$proxy_port" \ + "$backend_flag" "$backend_url" \ + --model "$model" \ + --tools "$tools_file" \ + --tool-outputs "$TOOL_OUTPUTS" \ + --output "$output_path" +} + +record_gateway_http() { + run_recording \ + "gateway HTTP/SSE function tool" \ + "codex-gateway-http-function-tool-${GATEWAY_MODEL_SLUG}-streaming.yaml" \ + "http" \ + "--vllm" \ + "$GATEWAY_URL" \ + "$GATEWAY_MODEL" \ + "$FUNCTION_TOOL" \ + 'You must call the agentic_plain_echo tool with text exactly "plain fixture" before answering.' \ + 'Use the tool output. Return only the echo string.' + + run_recording \ + "gateway HTTP/SSE namespace tool" \ + "codex-gateway-http-namespace-tool-${GATEWAY_MODEL_SLUG}-streaming.yaml" \ + "http" \ + "--vllm" \ + "$GATEWAY_URL" \ + "$GATEWAY_MODEL" \ + "$NAMESPACE_TOOL" \ + 'You must call mcp__agentic_fixture.add_numbers with numbers [8, 0] before answering.' \ + 'Use the tool output. Return only the sum.' +} + +record_gateway_ws() { + run_recording \ + "gateway WebSocket function tool" \ + "codex-gateway-websocket-function-tool-${GATEWAY_MODEL_SLUG}-streaming.yaml" \ + "websocket" \ + "--vllm" \ + "$GATEWAY_URL" \ + "$GATEWAY_MODEL" \ + "$FUNCTION_TOOL" \ + 'You must call the agentic_plain_echo tool with text exactly "plain fixture" before answering.' \ + 'Use the tool output. Return only the echo string.' + + run_recording \ + "gateway WebSocket namespace tool" \ + "codex-gateway-websocket-namespace-tool-${GATEWAY_MODEL_SLUG}-streaming.yaml" \ + "websocket" \ + "--vllm" \ + "$GATEWAY_URL" \ + "$GATEWAY_MODEL" \ + "$NAMESPACE_TOOL" \ + 'You must call mcp__agentic_fixture.add_numbers with numbers [8, 0] before answering.' \ + 'Use the tool output. Return only the sum.' +} + +record_direct_vllm_http() { + require_direct_vllm_url + + run_recording \ + "direct vLLM HTTP/SSE function tool" \ + "codex-direct-vllm-http-function-tool-${V_MODEL_SLUG}-streaming.yaml" \ + "http" \ + "--vllm" \ + "$VLLM_URL" \ + "$V_MODEL" \ + "$FUNCTION_TOOL" \ + 'You must call the agentic_plain_echo tool with text exactly "plain fixture" before answering.' \ + 'Use the tool output. Return only the echo string.' + + run_recording \ + "direct vLLM HTTP/SSE flattened namespace tool" \ + "codex-direct-vllm-http-flat-namespace-tool-${V_MODEL_SLUG}-streaming.yaml" \ + "http" \ + "--vllm" \ + "$VLLM_URL" \ + "$V_MODEL" \ + "$DIRECT_VLLM_FLAT_NAMESPACE_TOOL" \ + 'You must call agentic_ns__mcp__agentic_fixture__add_numbers with numbers [8, 0] before answering.' \ + 'Use the tool output. Return only the sum.' +} + +record_direct_vllm_ws() { + require_direct_vllm_url + + run_recording \ + "direct vLLM WebSocket function tool" \ + "codex-direct-vllm-websocket-function-tool-${V_MODEL_SLUG}-streaming.yaml" \ + "websocket" \ + "--vllm" \ + "$VLLM_URL" \ + "$V_MODEL" \ + "$FUNCTION_TOOL" \ + 'You must call the agentic_plain_echo tool with text exactly "plain fixture" before answering.' \ + 'Use the tool output. Return only the echo string.' + + run_recording \ + "direct vLLM WebSocket flattened namespace tool" \ + "codex-direct-vllm-websocket-flat-namespace-tool-${V_MODEL_SLUG}-streaming.yaml" \ + "websocket" \ + "--vllm" \ + "$VLLM_URL" \ + "$V_MODEL" \ + "$DIRECT_VLLM_FLAT_NAMESPACE_TOOL" \ + 'You must call agentic_ns__mcp__agentic_fixture__add_numbers with numbers [8, 0] before answering.' \ + 'Use the tool output. Return only the sum.' +} + +record_openai_https() { + require_openai_key + + run_recording \ + "direct OpenAI HTTPS/SSE function tool" \ + "codex-openai-https-function-tool-${OPENAI_MODEL_SLUG}-streaming.yaml" \ + "http" \ + "--openai" \ + "$OPENAI_URL" \ + "$OPENAI_MODEL" \ + "$FUNCTION_TOOL" \ + 'You must call the agentic_plain_echo tool with text exactly "plain fixture" before answering.' \ + 'Use the tool output. Return only the echo string.' +} + +record_openai_ws() { + require_openai_key + + run_recording \ + "direct OpenAI WebSocket function tool" \ + "codex-openai-websocket-function-tool-${OPENAI_MODEL_SLUG}-streaming.yaml" \ + "websocket" \ + "--openai" \ + "$OPENAI_URL" \ + "$OPENAI_MODEL" \ + "$FUNCTION_TOOL" \ + 'You must call the agentic_plain_echo tool with text exactly "plain fixture" before answering.' \ + 'Use the tool output. Return only the echo string.' +} + +record_openai_namespace_https() { + require_openai_key + + run_recording \ + "direct OpenAI HTTPS/SSE raw namespace tool" \ + "codex-openai-https-namespace-tool-${OPENAI_MODEL_SLUG}-streaming.yaml" \ + "http" \ + "--openai" \ + "$OPENAI_URL" \ + "$OPENAI_MODEL" \ + "$NAMESPACE_TOOL" \ + 'You must call mcp__agentic_fixture.add_numbers with numbers [8, 0] before answering.' \ + 'Use the tool output. Return only the sum.' +} + +record_openai_namespace_ws() { + require_openai_key + + run_recording \ + "direct OpenAI WebSocket raw namespace tool" \ + "codex-openai-websocket-namespace-tool-${OPENAI_MODEL_SLUG}-streaming.yaml" \ + "websocket" \ + "--openai" \ + "$OPENAI_URL" \ + "$OPENAI_MODEL" \ + "$NAMESPACE_TOOL" \ + 'You must call mcp__agentic_fixture.add_numbers with numbers [8, 0] before answering.' \ + 'Use the tool output. Return only the sum.' +} + +case "$TARGET" in + -h | --help | help) + usage + ;; + all) + require_openai_key + require_direct_vllm_url + record_gateway_http + record_gateway_ws + record_direct_vllm_http + record_openai_https + record_openai_namespace_https + record_openai_ws + record_openai_namespace_ws + ;; + gateway) + record_gateway_http + record_gateway_ws + ;; + gateway-http) + record_gateway_http + ;; + gateway-ws) + record_gateway_ws + ;; + direct-vllm | direct-vllm-http) + record_direct_vllm_http + ;; + direct-vllm-ws) + record_direct_vllm_ws + ;; + openai | openai-https) + record_openai_https + ;; + openai-ws) + record_openai_ws + ;; + openai-namespace) + record_openai_namespace_https + ;; + openai-ws-namespace) + record_openai_namespace_ws + ;; + experimental-all) + require_openai_key + require_direct_vllm_url + record_gateway_http + record_gateway_ws + record_direct_vllm_http + record_direct_vllm_ws + record_openai_https + record_openai_ws + record_openai_namespace_https + record_openai_namespace_ws + ;; + *) + usage >&2 + echo >&2 + echo "error: unknown target: ${TARGET}" >&2 + exit 2 + ;; +esac diff --git a/crates/agentic-core/tests/event_normalizer_test.rs b/crates/agentic-core/tests/event_normalizer_test.rs index c2e861f..e5c6422 100644 --- a/crates/agentic-core/tests/event_normalizer_test.rs +++ b/crates/agentic-core/tests/event_normalizer_test.rs @@ -177,7 +177,7 @@ fn test_output_item_added_message() { #[test] fn test_output_item_added_function_call() { - let line = r#"data: {"type":"response.output_item.added","item":{"id":"fc_1","type":"function_call","status":"in_progress","name":"get_weather","call_id":"call_1","arguments":""},"output_index":1,"sequence_number":5}"#; + let line = r#"data: {"type":"response.output_item.added","item":{"id":"fc_1","type":"function_call","status":"in_progress","name":"get_weather","namespace":"mcp__weather","call_id":"call_1","arguments":""},"output_index":1,"sequence_number":5}"#; let frame = normalize_sse_line(line).unwrap(); assert_eq!(frame.event_type, SSEEventType::OutputItemAdded); if let EventPayload::OutputItemAdded { @@ -185,6 +185,7 @@ fn test_output_item_added_function_call() { item_type, output_index, name, + namespace, call_id, } = &frame.payload { @@ -192,6 +193,7 @@ fn test_output_item_added_function_call() { assert_eq!(item_type, "function_call"); assert_eq!(*output_index, 1); assert_eq!(name.as_deref(), Some("get_weather")); + assert_eq!(namespace.as_deref(), Some("mcp__weather")); assert_eq!(call_id.as_deref(), Some("call_1")); } else { panic!("expected OutputItemAdded payload"); diff --git a/crates/agentic-core/tests/stateful_responses_integration.rs b/crates/agentic-core/tests/stateful_responses_integration.rs index 7114d4e..881b192 100644 --- a/crates/agentic-core/tests/stateful_responses_integration.rs +++ b/crates/agentic-core/tests/stateful_responses_integration.rs @@ -6,10 +6,12 @@ mod support; use agentic_core::executor::execute; +use agentic_core::{FunctionToolResultMessage, InputItem, ResponsesInput, ResponsesTool, ToolChoice}; +use serde_json::Value; use std::sync::Arc; use support::{ - TestFixture, collect_stream, expected_text, load_cassette, make_request, output_text, request_input_texts, - text_response, unwrap_blocking, + MockResponse, TestFixture, collect_stream, expected_text, load_cassette, make_request, output_text, + request_input_texts, text_response, unwrap_blocking, }; const DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/cassettes/text_only/responses"); @@ -209,6 +211,238 @@ async fn test_previous_response_id_rehydrates_full_checkpoint_history() { ); } +#[tokio::test] +async fn test_model_alias_resolves_before_executor_upstream_request() { + let fixture = TestFixture::new_with_responses(vec![text_response("aliased answer")]).await; + let mut exec_ctx = (*fixture.exec_ctx).clone(); + exec_ctx + .model_aliases + .insert("codex-auto-review".to_string(), "real-upstream-model".to_string()); + let exec_ctx = Arc::new(exec_ctx); + + let mut request = make_request("review this", true, false, None, None); + request.model = "codex-auto-review".to_string(); + let payload = unwrap_blocking(execute(request, exec_ctx).await.expect("execute")); + + assert_eq!(payload.model, "real-upstream-model"); + let requests = fixture.request_bodies().await; + assert_eq!(requests[0]["model"], "real-upstream-model"); +} + +#[tokio::test] +async fn test_codex_tool_shapes_rehydrate_from_previous_response_metadata() { + let fixture = TestFixture::new_with_responses(vec![ + text_response("seed answer"), + text_response("next answer"), + text_response("third answer"), + ]) + .await; + let tool_json = serde_json::json!([ + { + "type": "namespace", + "name": "mcp__shell", + "tools": [{"type": "function", "name": "run", "parameters": {"type": "object"}}] + }, + { + "type": "tool_search", + "execution": "client", + "parameters": {"type": "object"} + }, + { + "type": "custom", + "name": "apply_patch", + "format": {"type": "grammar"}, + "defer_loading": true + }, + { + "type": "future_tool", + "opaque": true + } + ]); + let tools: Vec = serde_json::from_value(tool_json.clone()).unwrap(); + + let mut first = make_request("seed", true, false, None, None); + first.tools = Some(tools); + let p1 = unwrap_blocking(execute(first, Arc::clone(&fixture.exec_ctx)).await.expect("first turn")); + + let second = make_request("next", true, false, Some(p1.id), None); + let p2 = unwrap_blocking( + execute(second, Arc::clone(&fixture.exec_ctx)) + .await + .expect("second turn"), + ); + let third = make_request("third", true, false, Some(p2.id), None); + let _p3 = unwrap_blocking(execute(third, Arc::clone(&fixture.exec_ctx)).await.expect("third turn")); + + let requests = fixture.request_bodies().await; + for request in &requests { + let tools = request["tools"].as_array().expect("typed upstream tools array"); + assert_eq!(tools.len(), 1); + assert_eq!(tools[0]["type"], "function"); + assert_eq!(tools[0]["name"], "agentic_ns__mcp__shell__run"); + assert_eq!(tools[0]["parameters"], tool_json[0]["tools"][0]["parameters"]); + } +} + +#[tokio::test] +async fn test_previous_response_id_explicit_tool_choice_overrides_stored_choice() { + let fixture = + TestFixture::new_with_responses(vec![text_response("seed answer"), text_response("next answer")]).await; + + let mut first = make_request("seed", true, false, None, None); + first.tool_choice = ToolChoice::Required; + first.tool_choice_explicitly_set = true; + let p1 = unwrap_blocking(execute(first, Arc::clone(&fixture.exec_ctx)).await.expect("first turn")); + + let mut second = make_request("next", true, false, Some(p1.id), None); + second.tool_choice = ToolChoice::None; + second.tool_choice_explicitly_set = true; + let _p2 = unwrap_blocking( + execute(second, Arc::clone(&fixture.exec_ctx)) + .await + .expect("second turn"), + ); + + let requests = fixture.request_bodies().await; + assert_eq!(requests[0]["tool_choice"], "required"); + assert_eq!(requests[1]["tool_choice"], "none"); +} + +#[tokio::test] +async fn test_previous_response_id_rehydrates_function_call_before_tool_output() { + let tool_call_response = MockResponse::Json( + serde_json::json!({ + "id": "resp_tool", + "object": "response", + "created_at": 0, + "model": "test-model", + "status": "completed", + "output": [{ + "id": "fc_1", + "type": "function_call", + "call_id": "call_1", + "name": "run", + "namespace": "mcp__shell", + "arguments": "{\"cmd\":\"pwd\"}", + "status": "completed" + }], + "usage": null, + "incomplete_details": null, + "error": null, + "previous_response_id": null, + "conversation_id": null, + "instructions": null + }) + .to_string(), + ); + let fixture = TestFixture::new_with_responses(vec![tool_call_response, text_response("tool result handled")]).await; + + let first = make_request("run pwd", true, false, None, None); + let p1 = unwrap_blocking(execute(first, Arc::clone(&fixture.exec_ctx)).await.expect("first turn")); + + let mut second = make_request("ignored", true, false, Some(p1.id), None); + second.input = ResponsesInput::Items(vec![InputItem::FunctionCallOutput(FunctionToolResultMessage { + call_id: "call_1".to_string(), + output: "{\"stdout\":\"/workspace\"}".to_string(), + })]); + let _p2 = unwrap_blocking( + execute(second, Arc::clone(&fixture.exec_ctx)) + .await + .expect("second turn"), + ); + + let requests = fixture.request_bodies().await; + let input = requests[1]["input"].as_array().expect("input array"); + assert_eq!(input[1]["type"], "function_call"); + assert_eq!(input[1]["namespace"], "mcp__shell"); + assert_eq!(input[1]["name"], "run"); + assert_eq!(input[2]["type"], "function_call_output"); + assert_eq!(input[2]["call_id"], "call_1"); +} + +#[tokio::test] +async fn test_mcp_namespace_showcase_round_trip_rehydrates_calls_tools_and_outputs() { + let tool_json = mcp_showcase_tools_json(); + let tools: Vec = serde_json::from_value(tool_json.clone()).expect("tool fixture parses"); + let tool_call_response = MockResponse::Json( + serde_json::json!({ + "id": "resp_mcp_showcase", + "object": "response", + "created_at": 0, + "model": "test-model", + "status": "completed", + "output": [ + upstream_mcp_fixture_call("fc_echo", "call_echo", "echo_text", r#"{"text":"namespace showcase","uppercase":true}"#), + upstream_mcp_fixture_call("fc_sum", "call_sum", "add_numbers", r#"{"numbers":[2,3,5]}"#), + upstream_mcp_fixture_call("fc_slug", "call_slug", "make_slug", r#"{"text":"Codex MCP Showcase"}"#), + upstream_mcp_fixture_call("fc_head", "call_head", "repo_file_head", r#"{"path":"README.md","lines":2}"#), + upstream_mcp_fixture_call("fc_search", "call_search", "search_repo", r#"{"query":"codex","path_prefix":"scripts","max_results":3}"#) + ], + "usage": null, + "incomplete_details": null, + "error": null, + "previous_response_id": null, + "conversation_id": null, + "instructions": null + }) + .to_string(), + ); + let fixture = TestFixture::new_with_responses(vec![tool_call_response, text_response("showcase complete")]).await; + + let mut first = make_request("use the agentic_fixture MCP toolbox", true, false, None, None); + first.tools = Some(tools); + let p1 = unwrap_blocking(execute(first, Arc::clone(&fixture.exec_ctx)).await.expect("first turn")); + + let output = serde_json::to_value(&p1.output).expect("output serializes"); + assert_namespaced_calls( + output.as_array().expect("output array"), + &["echo_text", "add_numbers", "make_slug", "repo_file_head", "search_repo"], + ); + + let mut second = make_request("ignored", true, false, Some(p1.id), None); + second.input = ResponsesInput::Items(vec![ + tool_output( + "call_echo", + r#"{"echo":"NAMESPACE SHOWCASE","characters":18,"words":2}"#, + ), + tool_output("call_sum", r#"{"count":3,"sum":10}"#), + tool_output("call_slug", r#"{"slug":"codex-mcp-showcase"}"#), + tool_output( + "call_head", + "README.md first 2 lines:\n1: # agentic-api\n2: Stateful API logic", + ), + tool_output( + "call_search", + r#"{"query":"codex","matches":[{"path":"scripts/codex-run.sh","line":16}]}"#, + ), + ]); + let p2 = unwrap_blocking( + execute(second, Arc::clone(&fixture.exec_ctx)) + .await + .expect("second turn"), + ); + + assert_eq!(output_text(&p2), "showcase complete"); + let requests = fixture.request_bodies().await; + assert_eq!(requests.len(), 2); + assert_flat_mcp_showcase_tools(&requests[0]["tools"]); + assert_flat_mcp_showcase_tools(&requests[1]["tools"]); + + let input = requests[1]["input"].as_array().expect("rehydrated input array"); + assert_namespaced_calls( + input, + &["echo_text", "add_numbers", "make_slug", "repo_file_head", "search_repo"], + ); + assert_tool_outputs( + input, + &["call_echo", "call_sum", "call_slug", "call_head", "call_search"], + ); + assert!( + !contains_key(&requests[1], "_agentic_item_kind"), + "storage marker must not leak into rehydrated upstream request" + ); +} + #[tokio::test] async fn test_store_false_with_previous_response_id_hydrates_but_does_not_persist() { let fixture = @@ -266,3 +500,179 @@ async fn test_conversation_id_and_previous_response_id_are_rejected_together() { assert!(result.is_err(), "expected ambiguous state IDs to be rejected"); assert!(fixture.request_bodies().await.is_empty()); } + +fn mcp_showcase_tools_json() -> Value { + serde_json::json!([ + { + "type": "namespace", + "name": "mcp__agentic_fixture", + "description": "Fixture namespace tool for Codex MCP round-trip tests.", + "tools": [ + { + "type": "function", + "name": "run", + "description": "Echo a command string for namespace round-trip validation.", + "parameters": { + "type": "object", + "properties": {"cmd": {"type": "string"}}, + "required": ["cmd"], + "additionalProperties": false + }, + "strict": true + }, + { + "type": "function", + "name": "echo_text", + "description": "Echo text with basic metadata.", + "parameters": { + "type": "object", + "properties": { + "text": {"type": "string"}, + "uppercase": {"type": "boolean"} + }, + "required": ["text"], + "additionalProperties": false + }, + "strict": true + }, + { + "type": "function", + "name": "add_numbers", + "description": "Add a list of numbers and return the total.", + "parameters": { + "type": "object", + "properties": { + "numbers": { + "type": "array", + "items": {"type": "number"}, + "minItems": 1 + } + }, + "required": ["numbers"], + "additionalProperties": false + }, + "strict": true + }, + { + "type": "function", + "name": "make_slug", + "description": "Turn text into a lowercase URL/file-name friendly slug.", + "parameters": { + "type": "object", + "properties": { + "text": {"type": "string"}, + "separator": {"type": "string"} + }, + "required": ["text"], + "additionalProperties": false + }, + "strict": true + }, + { + "type": "function", + "name": "repo_file_head", + "description": "Read the first lines of a repository file.", + "parameters": { + "type": "object", + "properties": { + "path": {"type": "string"}, + "lines": {"type": "integer", "minimum": 1, "maximum": 80} + }, + "required": ["path"], + "additionalProperties": false + }, + "strict": true + }, + { + "type": "function", + "name": "search_repo", + "description": "Literal text search across repository files.", + "parameters": { + "type": "object", + "properties": { + "query": {"type": "string"}, + "path_prefix": {"type": "string"}, + "max_results": {"type": "integer", "minimum": 1, "maximum": 30} + }, + "required": ["query"], + "additionalProperties": false + }, + "strict": true + } + ] + } + ]) +} + +fn upstream_mcp_fixture_call(id: &str, call_id: &str, name: &str, arguments: &str) -> Value { + serde_json::json!({ + "id": id, + "type": "function_call", + "call_id": call_id, + "name": format!("agentic_ns__mcp__agentic_fixture__{name}"), + "arguments": arguments, + "status": "completed" + }) +} + +fn tool_output(call_id: &str, output: &str) -> InputItem { + InputItem::FunctionCallOutput(FunctionToolResultMessage { + call_id: call_id.to_string(), + output: output.to_string(), + }) +} + +fn assert_namespaced_calls(items: &[Value], expected_names: &[&str]) { + for expected_name in expected_names { + assert!( + items.iter().any(|item| { + item.get("type").and_then(Value::as_str) == Some("function_call") + && item.get("namespace").and_then(Value::as_str) == Some("mcp__agentic_fixture") + && item.get("name").and_then(Value::as_str) == Some(expected_name) + }), + "missing namespaced function call mcp__agentic_fixture.{expected_name}" + ); + } +} + +fn assert_tool_outputs(items: &[Value], expected_call_ids: &[&str]) { + for expected_call_id in expected_call_ids { + assert!( + items.iter().any(|item| { + item.get("type").and_then(Value::as_str) == Some("function_call_output") + && item.get("call_id").and_then(Value::as_str) == Some(expected_call_id) + }), + "missing function_call_output for {expected_call_id}" + ); + } +} + +fn assert_flat_mcp_showcase_tools(tools: &Value) { + let tools = tools.as_array().expect("tools array"); + assert_eq!(tools.len(), 6); + for name in [ + "run", + "echo_text", + "add_numbers", + "make_slug", + "repo_file_head", + "search_repo", + ] { + let flat_name = format!("agentic_ns__mcp__agentic_fixture__{name}"); + assert!( + tools.iter().any(|tool| { + tool.get("type").and_then(Value::as_str) == Some("function") + && tool.get("name").and_then(Value::as_str) == Some(flat_name.as_str()) + }), + "missing flat upstream tool {flat_name}" + ); + } +} + +fn contains_key(value: &Value, key: &str) -> bool { + match value { + Value::Object(object) => object.contains_key(key) || object.values().any(|nested| contains_key(nested, key)), + Value::Array(values) => values.iter().any(|nested| contains_key(nested, key)), + Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => false, + } +} diff --git a/crates/agentic-core/tests/support/mod.rs b/crates/agentic-core/tests/support/mod.rs index 76a4109..ca37b2d 100644 --- a/crates/agentic-core/tests/support/mod.rs +++ b/crates/agentic-core/tests/support/mod.rs @@ -359,6 +359,7 @@ pub fn make_request( conversation_id, tools: None, tool_choice: ToolChoice::Auto, + tool_choice_explicitly_set: false, stream, store, include: None, @@ -404,7 +405,11 @@ pub fn output_text(payload: &ResponsePayload) -> String { .iter() .filter_map(|item| match item { OutputItem::Message(msg) => Some(msg.content.iter().map(|c| c.text.as_str()).collect::()), - OutputItem::FunctionCall(_) | OutputItem::Reasoning(_) | OutputItem::Unknown => None, + OutputItem::FunctionCall(_) + | OutputItem::ToolSearchCall(_) + | OutputItem::CustomToolCall(_) + | OutputItem::Reasoning(_) + | OutputItem::Unknown(_) => None, }) .collect::() } diff --git a/crates/agentic-core/tests/tool_normalization_test.rs b/crates/agentic-core/tests/tool_normalization_test.rs index 6065118..1458a48 100644 --- a/crates/agentic-core/tests/tool_normalization_test.rs +++ b/crates/agentic-core/tests/tool_normalization_test.rs @@ -5,11 +5,32 @@ use serde::Deserialize; -use agentic_core::tool::{ToolRegistry, ToolType}; +use agentic_core::tool::{ToolRegistry, ToolType, flatten_tools_for_upstream, model_visible_namespace_member_name}; use agentic_core::types::request_response::RequestPayload; use agentic_core::types::tools::ResponsesTool; const MULTI_TURN_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/cassettes/tool_calls/multi_turn"); +const CODEX_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/cassettes/codex"); + +const CODEX_CASSETTES: &[&str] = &[ + "codex-direct-vllm-http-flat-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-direct-vllm-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-gateway-http-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-gateway-http-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-gateway-websocket-function-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-gateway-websocket-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-openai-https-function-tool-gpt-4o-streaming.yaml", + "codex-openai-https-namespace-tool-gpt-4o-streaming.yaml", + "codex-openai-websocket-function-tool-gpt-4o-streaming.yaml", + "codex-openai-websocket-namespace-tool-gpt-4o-streaming.yaml", +]; + +const CODEX_NAMESPACE_CASSETTES: &[&str] = &[ + "codex-gateway-http-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-gateway-websocket-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml", + "codex-openai-https-namespace-tool-gpt-4o-streaming.yaml", + "codex-openai-websocket-namespace-tool-gpt-4o-streaming.yaml", +]; #[derive(Deserialize)] struct TurnCassette { @@ -23,18 +44,38 @@ struct Turn { response: serde_yml::Value, } -fn load_cassette(filename: &str) -> TurnCassette { - let path = format!("{MULTI_TURN_DIR}/{filename}"); +fn load_cassette_from(dir: &str, filename: &str) -> TurnCassette { + let path = format!("{dir}/{filename}"); let text = std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {path}: {e}")); serde_yml::from_str(&text).unwrap_or_else(|e| panic!("parse {path}: {e}")) } +fn load_cassette(filename: &str) -> TurnCassette { + load_cassette_from(MULTI_TURN_DIR, filename) +} + +fn load_codex_cassette(filename: &str) -> TurnCassette { + load_cassette_from(CODEX_DIR, filename) +} + fn tools_from_turn(turn: &Turn) -> Option { let body = turn.request.get("body")?; let json: serde_json::Value = serde_json::to_value(body).ok()?; json.get("tools").cloned() } +fn request_body_from_turn(turn: &Turn) -> serde_json::Value { + let body = turn.request.get("body").expect("turn has body"); + serde_json::to_value(body).expect("body to json") +} + +fn parse_tools_from_turn(cassette_file: &str, turn_idx: usize, turn: &Turn) -> Vec { + let tools_val = + tools_from_turn(turn).unwrap_or_else(|| panic!("{cassette_file} turn {turn_idx}: expected tools array")); + serde_json::from_value(tools_val.clone()) + .unwrap_or_else(|e| panic!("{cassette_file} turn {turn_idx}: tools parse failed: {e}\nJSON: {tools_val}")) +} + /// Parse `request.body.tools` from every turn of a cassette into `Vec`. fn assert_tools_parse(cassette_file: &str) { let cassette = load_cassette(cassette_file); @@ -192,3 +233,111 @@ fn roundtrip_5turn() { fn roundtrip_parallel() { assert_full_roundtrip("openai_responses_tool_calls_parallel.yaml"); } + +#[test] +fn codex_request_payloads_parse_all_recorded_shapes() { + for filename in CODEX_CASSETTES { + let cassette = load_codex_cassette(filename); + assert_eq!(cassette.turns.len(), 2, "{filename} should have two turns"); + + for (i, turn) in cassette.turns.iter().enumerate() { + let json = request_body_from_turn(turn); + let payload: RequestPayload = serde_json::from_value(json.clone()) + .unwrap_or_else(|e| panic!("{filename} turn {i}: RequestPayload parse failed: {e}\nJSON: {json}")); + assert!( + payload.tools.as_ref().is_some_and(|tools| !tools.is_empty()), + "{filename} turn {i}: expected tools" + ); + if filename.contains("websocket") { + assert!( + json.get("stream").is_none(), + "{filename} turn {i}: websocket cassette should not contain HTTP-only stream field" + ); + } + } + } +} + +#[test] +fn codex_namespace_cassettes_flatten_to_safe_upstream_function_name() { + let expected_flat_name = model_visible_namespace_member_name("mcp__agentic_fixture", "add_numbers"); + + for filename in CODEX_NAMESPACE_CASSETTES { + let cassette = load_codex_cassette(filename); + for (i, turn) in cassette.turns.iter().enumerate() { + let tools = parse_tools_from_turn(filename, i, turn); + assert!( + tools.iter().any(|tool| { + matches!( + tool, + ResponsesTool::Namespace(namespace) + if namespace.name == "mcp__agentic_fixture" + && namespace.tools.iter().any(|member| { + matches!( + member, + agentic_core::types::CodexNamespaceMember::Function(function) + if function.name.as_str() == "add_numbers" + ) + }) + ) + }), + "{filename} turn {i}: expected raw namespace tool" + ); + + let flattened = flatten_tools_for_upstream(Some(&tools)).expect("flattened tools"); + assert!( + flattened + .iter() + .all(|tool| !matches!(tool, ResponsesTool::Namespace(_))), + "{filename} turn {i}: namespace tools should be flattened before upstream" + ); + assert!( + flattened.iter().any(|tool| { + matches!( + tool, + ResponsesTool::Function(function) if function.name.as_str() == expected_flat_name + ) + }), + "{filename} turn {i}: expected flattened tool name {expected_flat_name}" + ); + let upstream_tools: Vec<_> = flattened.iter().filter_map(ResponsesTool::to_function_tool).collect(); + assert!( + upstream_tools.iter().any(|tool| tool.name == expected_flat_name), + "{filename} turn {i}: expected upstream FunctionTool {expected_flat_name}" + ); + } + } +} + +#[test] +fn codex_direct_vllm_flat_namespace_cassette_is_plain_function_tool() { + let filename = "codex-direct-vllm-http-flat-namespace-tool-Qwen-Qwen3.6-35B-A3B-streaming.yaml"; + let expected_flat_name = model_visible_namespace_member_name("mcp__agentic_fixture", "add_numbers"); + let cassette = load_codex_cassette(filename); + + for (i, turn) in cassette.turns.iter().enumerate() { + let tools = parse_tools_from_turn(filename, i, turn); + assert_eq!( + tools.len(), + 1, + "{filename} turn {i}: direct vLLM flat namespace fixture should declare one tool" + ); + assert!( + matches!( + &tools[0], + ResponsesTool::Function(function) if function.name.as_str() == expected_flat_name + ), + "{filename} turn {i}: expected direct vLLM to see a plain function tool named {expected_flat_name}" + ); + + let flattened = flatten_tools_for_upstream(Some(&tools)).expect("flattened tools"); + assert_eq!(flattened.len(), 1); + assert!( + matches!( + &flattened[0], + ResponsesTool::Function(function) if function.name.as_str() == expected_flat_name + ), + "{filename} turn {i}: flattening should preserve already-flat direct vLLM function" + ); + } +} diff --git a/crates/agentic-server/benches/gateway_bench.rs b/crates/agentic-server/benches/gateway_bench.rs index 53860ab..9fdec0c 100644 --- a/crates/agentic-server/benches/gateway_bench.rs +++ b/crates/agentic-server/benches/gateway_bench.rs @@ -157,7 +157,9 @@ async fn spawn_gateway(llm_url: &str) -> (Arc, String) { openai_api_key: None, llm_ready_timeout_s: 5.0, llm_ready_interval_s: 0.1, + skip_llm_ready_check: false, db_url: Some(format!("sqlite://{}", db_path.display())), + model_aliases: std::collections::HashMap::new(), }; let proxy_state = ProxyState::new(config.clone()).unwrap(); diff --git a/crates/agentic-server/benches/proxy_bench.rs b/crates/agentic-server/benches/proxy_bench.rs index 73317ac..8f449c4 100644 --- a/crates/agentic-server/benches/proxy_bench.rs +++ b/crates/agentic-server/benches/proxy_bench.rs @@ -26,7 +26,9 @@ fn bench_config(llm_url: &str) -> Config { openai_api_key: Some("bench-key".to_owned()), llm_ready_timeout_s: 5.0, llm_ready_interval_s: 0.1, + skip_llm_ready_check: false, db_url: None, + model_aliases: std::collections::HashMap::new(), } } diff --git a/crates/agentic-server/src/handler/http/responses.rs b/crates/agentic-server/src/handler/http/responses.rs index 5d22034..970f8d8 100644 --- a/crates/agentic-server/src/handler/http/responses.rs +++ b/crates/agentic-server/src/handler/http/responses.rs @@ -3,6 +3,7 @@ use axum::http::request::Parts; use axum::response::{IntoResponse, Response}; use bytes::Bytes; use either::Either; +use tracing::debug; use agentic_core::executor::execute; use agentic_core::proxy::{ProxyRequest, proxy_request}; @@ -36,6 +37,15 @@ pub async fn responses(State(state): State, req: Request) -> Response }; let should_persist = payload.store || payload.previous_response_id.is_some() || payload.conversation_id.is_some(); + debug!( + route = if should_persist { "executor" } else { "proxy" }, + store = payload.store, + stream = payload.stream, + has_previous_response_id = payload.previous_response_id.is_some(), + has_conversation_id = payload.conversation_id.is_some(), + tools = payload.tools.as_ref().map_or(0, Vec::len), + "routing HTTP responses request" + ); if should_persist { execute_responses(&state, parts, payload).await diff --git a/crates/agentic-server/src/handler/websocket/responses.rs b/crates/agentic-server/src/handler/websocket/responses.rs index 6a7c11a..e2d7c54 100644 --- a/crates/agentic-server/src/handler/websocket/responses.rs +++ b/crates/agentic-server/src/handler/websocket/responses.rs @@ -9,15 +9,16 @@ use futures::stream::{SplitSink, SplitStream}; use futures::{SinkExt, StreamExt}; use serde_json::Value; use tokio_util::sync::CancellationToken; -use tracing::warn; +use tracing::{debug, warn}; use agentic_core::executor::accumulator::ResponseAccumulator; use agentic_core::executor::{ - ExecutionContext, ExecutorError, RequestContext, call_inference, persist_response, rehydrate_conversation, + ExecutionContext, ExecutorError, RequestContext, call_inference, persist_response, prepare_context_for_upstream, + rehydrate_conversation, upstream_request_json, }; +use agentic_core::tool::{normalize_output_items_with_tools, normalize_response_value_with_tools}; use agentic_core::types::ResponsePayload; use agentic_core::types::request_response::RequestPayload; -use agentic_core::utils::common::serialize_to_string; use super::super::common::{MAX_BODY_SIZE, resolve_exec_ctx_from_headers}; use super::error::WsError; @@ -33,6 +34,7 @@ pub async fn responses_ws(State(state): State, headers: HeaderMap, ws: } async fn responses_ws_loop(socket: WebSocket, state: AppState, headers: HeaderMap) { + debug!("responses websocket session opened"); let shutdown_token = state.shutdown_token.clone(); let (mut sender, mut receiver) = socket.split(); @@ -94,6 +96,7 @@ async fn responses_ws_loop(socket: WebSocket, state: AppState, headers: HeaderMa } } } + debug!("responses websocket session closed"); } /// Process one `response.create` message. @@ -116,13 +119,25 @@ async fn handle_ws_text( } let mut payload = serde_json::from_value::(value).map_err(ExecutorError::from)?; + let requested_stream = payload.stream; + let requested_store = payload.store; payload.stream = true; payload.store = true; + debug!( + requested_stream, + requested_store, + forced_stream = payload.stream, + forced_store = payload.store, + has_previous_response_id = payload.previous_response_id.is_some(), + has_conversation_id = payload.conversation_id.is_some(), + tools = payload.tools.as_ref().map_or(0, Vec::len), + "accepted websocket response.create" + ); let exec_ctx = resolve_exec_ctx_from_headers(state, headers); - let ctx = rehydrate_conversation(payload, &exec_ctx).await?; - let upstream_json = - serialize_to_string(&ctx.enriched_request.to_upstream_request(true)).map_err(ExecutorError::from)?; + let mut ctx = rehydrate_conversation(payload, &exec_ctx).await?; + prepare_context_for_upstream(&mut ctx, &exec_ctx); + let upstream_json = upstream_request_json(&ctx, true)?; stream_ws_response(sender, receiver, exec_ctx, ctx, upstream_json, shutdown_token, queue).await } @@ -143,7 +158,14 @@ async fn stream_ws_response( let should_persist = ctx.original_request.store || ctx.original_request.previous_response_id.is_some() || ctx.conversation_id.is_some(); + debug!( + response_id = %ctx.response_id, + should_persist, + has_conversation_id = ctx.conversation_id.is_some(), + "streaming websocket responses request" + ); let mut lines = Vec::new(); + let mut forwarded_events = 0usize; let mut stream = Box::pin(call_inference( upstream_json, exec_ctx.responses_url(), @@ -168,6 +190,11 @@ async fn stream_ws_response( // Client pipelined the next request while we are still streaming. // Enqueue it and keep draining the current stream. queue.push_back(text.to_string()); + debug!( + response_id = %ctx.response_id, + queued_requests = queue.len(), + "queued pipelined websocket response.create while upstream stream is active" + ); continue 'stream; } Some(Err(e)) => return Err(WsError::Receive(e.to_string())), @@ -194,30 +221,50 @@ async fn stream_ws_response( Err(e) => return Err(WsError::Executor(ExecutorError::from(e))), }; apply_gateway_response_ids(&mut value, &ctx); + normalize_response_value_with_tools(&mut value, ctx.enriched_request.tools.as_deref()); send_ws_json(sender, value).await?; + forwarded_events += 1; if should_persist { lines.push(line); } } + debug!( + response_id = %ctx.response_id, + forwarded_events, + persisted_sse_lines = lines.len(), + "finished websocket upstream response stream" + ); if should_persist && !lines.is_empty() { - let acc = ResponseAccumulator::from_sse_lines(lines, ctx.conversation_id.as_deref()); - let mut payload = acc.finalize( - &ctx.enriched_request.model, - ctx.original_request.previous_response_id.as_deref(), - ctx.original_request.instructions.as_deref(), - ); - apply_gateway_payload_ids(&mut payload, &ctx); - let ch = exec_ctx.conv_handler.clone(); - let rh = exec_ctx.resp_handler.clone(); - if let Err(e) = persist_response(payload, ctx, ch, rh).await { - warn!("persist failed: {e}"); - } + persist_ws_response(&exec_ctx, ctx, lines).await; } Ok(()) } +async fn persist_ws_response(exec_ctx: &ExecutionContext, ctx: RequestContext, lines: Vec) { + let acc = ResponseAccumulator::from_sse_lines(lines, ctx.conversation_id.as_deref()); + let mut payload = acc.finalize( + &ctx.enriched_request.model, + ctx.original_request.previous_response_id.as_deref(), + ctx.original_request.instructions.as_deref(), + ); + normalize_output_items_with_tools(&mut payload.output, ctx.enriched_request.tools.as_deref()); + apply_gateway_payload_ids(&mut payload, &ctx); + let ch = exec_ctx.conv_handler.clone(); + let rh = exec_ctx.resp_handler.clone(); + let response_id = ctx.response_id.clone(); + let output_items = payload.output.len(); + match persist_response(payload, ctx, ch, rh).await { + Ok(()) => debug!( + response_id = %response_id, + output_items, + "persisted websocket responses output" + ), + Err(e) => warn!("persist failed: {e}"), + } +} + fn apply_gateway_response_ids(value: &mut Value, ctx: &RequestContext) { let Some(response) = value.get_mut("response").and_then(Value::as_object_mut) else { return; diff --git a/crates/agentic-server/src/main.rs b/crates/agentic-server/src/main.rs index 5cf7609..05ee77a 100644 --- a/crates/agentic-server/src/main.rs +++ b/crates/agentic-server/src/main.rs @@ -1,6 +1,6 @@ use clap::{Args, Parser, Subcommand}; -use agentic_core::config::{Config, normalize_base_url}; +use agentic_core::config::{Config, normalize_base_url, parse_model_aliases}; use agentic_core::error::Error; mod server; @@ -22,6 +22,10 @@ struct CommonArgs { #[arg(long, default_value_t = 2.0, global = true)] llm_ready_interval_s: f64, + /// Skip the upstream /health readiness probe. Useful for hosted OpenAI-compatible providers. + #[arg(long, env = "SKIP_LLM_READY_CHECK", default_value_t = false, global = true)] + skip_llm_ready_check: bool, + /// `SQLite` or `PostgreSQL` URL for conversation and response storage. /// Defaults to a local `SQLite` file. #[arg( @@ -31,6 +35,10 @@ struct CommonArgs { global = true )] db_url: String, + + /// Model alias mapping, repeatable or comma-separated with `MODEL_ALIASES`. + #[arg(long = "model-alias", env = "MODEL_ALIASES", value_delimiter = ',', global = true)] + model_aliases: Vec, } #[derive(Parser)] @@ -63,14 +71,17 @@ enum Commands { }, } -fn build_config(llm_api_base: String, common: &CommonArgs) -> Config { - Config { +fn build_config(llm_api_base: String, common: &CommonArgs) -> Result { + let model_aliases = parse_model_aliases(&common.model_aliases).map_err(Error::Config)?; + Ok(Config { llm_api_base, openai_api_key: common.openai_api_key.clone(), llm_ready_timeout_s: common.llm_ready_timeout_s, llm_ready_interval_s: common.llm_ready_interval_s, + skip_llm_ready_check: common.skip_llm_ready_check, db_url: Some(common.db_url.clone()), - } + model_aliases, + }) } #[tokio::main] @@ -96,7 +107,7 @@ async fn main() -> Result<(), Error> { .to_owned(), ) })?; - let config = build_config(normalize_base_url(&base), &common); + let config = build_config(normalize_base_url(&base), &common)?; server::run(config, &common.gateway_host, common.gateway_port).await } Some(Commands::Serve { model, port, llm_args }) => { @@ -105,7 +116,7 @@ async fn main() -> Result<(), Error> { "--llm-api-base is only valid in standalone mode; remove it when using `serve`".to_owned(), )); } - let config = build_config(normalize_base_url(&format!("http://127.0.0.1:{port}")), &common); + let config = build_config(normalize_base_url(&format!("http://127.0.0.1:{port}")), &common)?; let mut args = vec!["--model".to_owned(), model]; args.push("--port".to_owned()); args.push(port.to_string()); diff --git a/crates/agentic-server/src/server.rs b/crates/agentic-server/src/server.rs index 34aea6c..d7571e5 100644 --- a/crates/agentic-server/src/server.rs +++ b/crates/agentic-server/src/server.rs @@ -60,8 +60,12 @@ async fn serve_gateway_until_signal(state: AppState, host: &str, port: u16) -> R /// Returns an error if DB initialisation, LLM readiness polling, or the /// server binding fails. pub async fn run(config: Config, host: &str, port: u16) -> Result<(), Error> { - wait_llm_ready(&config).await?; - info!("LLM ready: {}", config.llm_api_base); + if config.skip_llm_ready_check { + info!("skipping LLM readiness check: {}", config.llm_api_base); + } else { + wait_llm_ready(&config).await?; + info!("LLM ready: {}", config.llm_api_base); + } let state = build_state(&config, CancellationToken::new()).await?; serve_gateway_until_signal(state, host, port).await } @@ -80,11 +84,16 @@ pub async fn run_with_llm(config: Config, host: &str, port: u16, llm_args: Vec ready, - status = child.wait() => { - let status = status?; - Err(Error::LlmProcessExited { status: status.to_string() }) + let readiness_result = if config.skip_llm_ready_check { + info!("skipping LLM readiness check: {}", config.llm_api_base); + Ok(()) + } else { + tokio::select! { + ready = wait_llm_ready(&config) => ready, + status = child.wait() => { + let status = status?; + Err(Error::LlmProcessExited { status: status.to_string() }) + } } }; diff --git a/crates/agentic-server/tests/common/mod.rs b/crates/agentic-server/tests/common/mod.rs index eb87a5d..7379452 100644 --- a/crates/agentic-server/tests/common/mod.rs +++ b/crates/agentic-server/tests/common/mod.rs @@ -19,18 +19,22 @@ pub fn test_config(llm_url: &str) -> Config { openai_api_key: Some("test-key".to_owned()), llm_ready_timeout_s: 5.0, llm_ready_interval_s: 0.1, + skip_llm_ready_check: false, db_url: None, + model_aliases: std::collections::HashMap::new(), } } pub fn test_state(config: &Config) -> AppState { - let exec_ctx = Arc::new(ExecutionContext::new( + let mut exec_ctx = ExecutionContext::new( ConversationHandler::new(ConversationStore::disabled()), ResponseHandler::new(ResponseStore::disabled()), Arc::new(reqwest::Client::new()), config.llm_api_base.clone(), config.openai_api_key.clone(), - )); + ); + exec_ctx.model_aliases.clone_from(&config.model_aliases); + let exec_ctx = Arc::new(exec_ctx); let proxy_state = ProxyState::new(config.clone()).expect("proxy state"); AppState { proxy_state, diff --git a/crates/agentic-server/tests/responses_websocket_test.rs b/crates/agentic-server/tests/responses_websocket_test.rs index 5fb99f3..9d5c210 100644 --- a/crates/agentic-server/tests/responses_websocket_test.rs +++ b/crates/agentic-server/tests/responses_websocket_test.rs @@ -22,6 +22,7 @@ use tokio_tungstenite::tungstenite::Message; use tokio_tungstenite::{MaybeTlsStream, WebSocketStream, connect_async}; use tokio_util::sync::CancellationToken; +use agentic_core::config::Config; use agentic_core::executor::{ConversationHandler, ExecutionContext, ResponseHandler}; use agentic_core::proxy::ProxyState; use agentic_core::storage::{ConversationStore, ResponseStore, create_pool_with_schema}; @@ -168,18 +169,19 @@ struct StorageBackedState { _db: TestDb, } -async fn storage_backed_state(llm_url: &str) -> StorageBackedState { +async fn storage_backed_state_with_config(config: Config) -> StorageBackedState { let db = TestDb::new(); let db_url = db.url(); let pool = create_pool_with_schema(Some(&db_url)).await.unwrap(); - let config = test_config(llm_url); - let exec_ctx = Arc::new(ExecutionContext::new( + let mut exec_ctx = ExecutionContext::new( ConversationHandler::new(ConversationStore::new(Arc::clone(&pool))), ResponseHandler::new(ResponseStore::new(pool)), Arc::new(reqwest::Client::new()), config.llm_api_base.clone(), config.openai_api_key.clone(), - )); + ); + exec_ctx.model_aliases.clone_from(&config.model_aliases); + let exec_ctx = Arc::new(exec_ctx); let proxy_state = ProxyState::new(config.clone()).expect("proxy state"); let state = AppState { @@ -191,6 +193,10 @@ async fn storage_backed_state(llm_url: &str) -> StorageBackedState { StorageBackedState { state, _db: db } } +async fn storage_backed_state(llm_url: &str) -> StorageBackedState { + storage_backed_state_with_config(test_config(llm_url)).await +} + fn ws_url(gateway_url: &str) -> String { format!("{}/v1/responses", gateway_url.replacen("http://", "ws://", 1)) } @@ -274,6 +280,46 @@ fn sse_response(response_id: &str, message_id: &str, text: &str) -> String { format!("data: {created}\n\ndata: {added}\n\ndata: {delta}\n\ndata: {completed}\n\ndata: [DONE]\n\n") } +fn sse_function_call_response(response_id: &str, call_name: &str) -> String { + let created = json!({ + "type": "response.created", + "sequence_number": 0, + "response": {"id": response_id, "status": "in_progress"} + }); + let added = json!({ + "type": "response.output_item.added", + "sequence_number": 1, + "output_index": 0, + "item": { + "id": "fc_upstream_1", + "type": "function_call", + "status": "in_progress", + "name": call_name, + "call_id": "call_1", + "arguments": "" + } + }); + let done = json!({ + "type": "response.output_item.done", + "sequence_number": 2, + "output_index": 0, + "item": { + "id": "fc_upstream_1", + "type": "function_call", + "status": "completed", + "name": call_name, + "call_id": "call_1", + "arguments": "{\"numbers\":[8,0]}" + } + }); + let completed = json!({ + "type": "response.completed", + "sequence_number": 3, + "response": {"id": response_id, "status": "completed", "usage": null} + }); + format!("data: {created}\n\ndata: {added}\n\ndata: {done}\n\ndata: {completed}\n\ndata: [DONE]\n\n") +} + #[tokio::test] async fn test_websocket_first_turn_forwards_incremental_response_events() { let mock = MockResponsesServer::start(vec![sse_response("resp_upstream_1", "msg_upstream_1", "HELLO")]).await; @@ -318,6 +364,143 @@ async fn test_websocket_first_turn_forwards_incremental_response_events() { assert!(requests[0].get("type").is_none()); } +#[tokio::test] +async fn test_websocket_rewrites_model_alias_before_upstream_request() { + let mock = MockResponsesServer::start(vec![sse_response("resp_upstream_1", "msg_upstream_1", "HELLO")]).await; + let mut config = test_config(&mock.url); + config + .model_aliases + .insert("codex-compatible".to_string(), "zai-org/GLM-5.2".to_string()); + let fixture = storage_backed_state_with_config(config).await; + let (gateway_url, _gateway) = spawn_gateway(fixture.state.clone()).await; + let mut ws = connect_responses_ws(&gateway_url).await; + + send_json( + &mut ws, + json!({ + "type": "response.create", + "model": "codex-compatible", + "input": [{"type": "message", "role": "user", "content": "hi"}], + "store": true, + "stream": true + }), + ) + .await; + let _events = recv_until_completed(&mut ws).await; + + let requests = mock.request_bodies().await; + assert_eq!(requests.len(), 1); + assert_eq!(requests[0]["model"], "zai-org/GLM-5.2"); + assert_eq!(requests[0]["stream"], true); +} + +#[tokio::test] +async fn test_websocket_restores_namespace_tool_call_events() { + let mock = MockResponsesServer::start(vec![sse_function_call_response( + "resp_upstream_1", + "agentic_ns__mcp__agentic_fixture__add_numbers", + )]) + .await; + let fixture = storage_backed_state(&mock.url).await; + let (gateway_url, _gateway) = spawn_gateway(fixture.state.clone()).await; + let mut ws = connect_responses_ws(&gateway_url).await; + + send_json( + &mut ws, + json!({ + "type": "response.create", + "model": "test-model", + "input": [{"type": "message", "role": "user", "content": "use the tool"}], + "tools": [ + { + "type": "namespace", + "name": "mcp__agentic_fixture", + "tools": [ + { + "type": "function", + "name": "add_numbers", + "parameters": {"type": "object"} + } + ] + } + ], + "store": true, + "stream": true + }), + ) + .await; + + let events = recv_until_completed(&mut ws).await; + let added = events + .iter() + .find(|event| event["type"] == "response.output_item.added") + .unwrap(); + let done = events + .iter() + .find(|event| event["type"] == "response.output_item.done") + .unwrap(); + + assert_eq!(added["item"]["namespace"], "mcp__agentic_fixture"); + assert_eq!(added["item"]["name"], "add_numbers"); + assert_eq!(done["item"]["namespace"], "mcp__agentic_fixture"); + assert_eq!(done["item"]["name"], "add_numbers"); + + let requests = mock.request_bodies().await; + assert_eq!(requests.len(), 1); + assert_eq!(requests[0]["tools"][0]["type"], "function"); + assert_eq!( + requests[0]["tools"][0]["name"], + "agentic_ns__mcp__agentic_fixture__add_numbers" + ); +} + +#[tokio::test] +async fn test_websocket_preserves_plain_function_tool_call_events() { + let mock = MockResponsesServer::start(vec![sse_function_call_response("resp_upstream_1", "get_weather")]).await; + let fixture = storage_backed_state(&mock.url).await; + let (gateway_url, _gateway) = spawn_gateway(fixture.state.clone()).await; + let mut ws = connect_responses_ws(&gateway_url).await; + + send_json( + &mut ws, + json!({ + "type": "response.create", + "model": "test-model", + "input": [{"type": "message", "role": "user", "content": "use the tool"}], + "tools": [ + { + "type": "function", + "name": "get_weather", + "parameters": {"type": "object"} + } + ], + "store": true, + "stream": true + }), + ) + .await; + + let events = recv_until_completed(&mut ws).await; + let added = events + .iter() + .find(|event| event["type"] == "response.output_item.added") + .unwrap(); + let done = events + .iter() + .find(|event| event["type"] == "response.output_item.done") + .unwrap(); + + assert!(added["item"].get("namespace").is_none()); + assert_eq!(added["item"]["name"], "get_weather"); + assert!(done["item"].get("namespace").is_none()); + assert_eq!(done["item"]["name"], "get_weather"); + + let requests = mock.request_bodies().await; + assert_eq!(requests.len(), 1); + assert_eq!(requests[0]["tools"][0]["type"], "function"); + assert_eq!(requests[0]["tools"][0]["name"], "get_weather"); +} + #[tokio::test] async fn test_websocket_continuation_rehydrates_previous_response() { let mock = MockResponsesServer::start(vec![ diff --git a/docs/design/codex-integration.md b/docs/design/codex-integration.md index f73b6d9..baec7fe 100644 --- a/docs/design/codex-integration.md +++ b/docs/design/codex-integration.md @@ -2,7 +2,7 @@ > **References:** [Issue #54](https://github.com/vllm-project/agentic-api/issues/54), > [PR #67](https://github.com/vllm-project/agentic-api/pull/67) -> **Owner:** @haoshan98 for Codex compatibility. @ashwing PR #67 owns the generic tool framework. +> **Owner:** @haoshan98 for Codex compatibility. Latest `main` owns the generic tool framework lineage from PR #67. --- @@ -10,13 +10,18 @@ `agentic-api` should work as an upstream layer for Codex CLI while routing inference to vLLM-supported models. -This PR is an MVP compatibility slice. It lets `agentic-api` accept and preserve Codex-used Responses traffic now, -without waiting for the full generic tool framework from PR #67. +Post-merge status: the generic tool framework from latest `main` is now present. Codex tool wire shapes live in the +shared `ResponsesTool` type, and namespace flatten/restore behavior lives in `tool::normalize`. The typed stateful +executor forwards only vLLM-compatible `function` tools upstream after namespace flattening; raw `store=false` proxying +continues to preserve raw tool declarations while applying the same compatibility rewrite. + +This PR remains an MVP compatibility slice. It lets `agentic-api` accept and preserve Codex-used Responses traffic while +plugging the Codex-specific compatibility rules into the shared tool framework. The important split: -- **This PR:** preserve Codex request/response shapes and continuation state. -- **PR #67:** formalize generic tool normalization, execution, registry, ownership, and loop decisions. +- **Codex compatibility:** preserve Codex request/response shapes and continuation state. +- **Shared framework:** provide generic tool normalization, execution, registry, ownership, and loop decisions. --- @@ -37,9 +42,9 @@ This PR should **not** build a second generic tool framework. --- -## Deferred To PR #67 +## Shared Tool Framework Boundary -PR #67 should own the formal shared tool system: +Latest `main` owns the formal shared tool system: - `ToolHandler` / `Tool` trait shape. - Generic tool normalization before `call_inference()`. @@ -48,8 +53,7 @@ PR #67 should own the formal shared tool system: - Requires-action / client-action loop decision. - Live `execution_loop` orchestration and streaming tool events. -The helper types in this PR are temporary. They express Codex requirements, but the canonical versions should come -from #67. After #67 lands, this slice should plug into or be refactored onto those abstractions. +Codex-specific rules should stay plugged into those abstractions rather than reintroducing a parallel framework. --- diff --git a/docs/design/core-public-api.md b/docs/design/core-public-api.md index fb92954..873fc45 100644 --- a/docs/design/core-public-api.md +++ b/docs/design/core-public-api.md @@ -1,7 +1,8 @@ # Design: `agentic-core` Public API > Status: Active — implementation in progress -> References: [ADR-03](../adr/ADR-03_gateway_integration.md), [Issue #42](https://github.com/vllm-project/agentic-api/issues/42), [Praxis #354](https://github.com/praxis-proxy/praxis/issues/354) +> References: [ADR-03](../adr/ADR-03_gateway_integration.md), [Issue #42](https://github.com/vllm-project/agentic-api/issues/42), +> [Issue #54](https://github.com/vllm-project/agentic-api/issues/54), [Praxis #354](https://github.com/praxis-proxy/praxis/issues/354) > Owner: @ashwing (tool dispatch, loop control, streaming tee) + @maralbahari (base loop, store integration) --- @@ -33,6 +34,7 @@ The base loop handles text messages. This design extends it with: 3. **Streaming tee** — forward SSE to client in real-time while accumulating for tool detection 4. **Extended SSE events** — function_call, reasoning, file_search, web_search event types 5. **Tool executor traits** — MCP, web_search, vector_store as pluggable implementations +6. **Codex CLI compatibility** — recognize Codex client-side tool types and route them without server execution --- @@ -123,7 +125,7 @@ pub async fn execute_loop( 1. Rehydrate (delegates to PR #46's `rehydrate_conversation`) 2. Call inference (delegates to PR #46's `call_inference` — returns stream lazily) 3. Accumulate response (via `ResponseAccumulator::from_stream`) -4. Check output for `OutputItem::FunctionCall` → `dispatch_tools` → loop or done +4. Check output for `OutputItem::FunctionCall` → `dispatch_tools` → loop, client action, or done 5. Persist final response (delegates to PR #46's `persist_response` with explicit handlers) **Phase 2 is non-streaming only.** The tool loop inspects the full accumulated response before deciding. Streaming + tool dispatch (forwarding events to client while detecting tool calls) requires Phase 3's tee pattern. @@ -252,6 +254,249 @@ How the complete pipeline maps to @leseb's proposed filter chain: --- +## Codex Integration + +Allow `agentic-api` to serve as the upstream layer for Codex CLI in the coming PR by @haoshan98, related to [Issue #54](https://github.com/vllm-project/agentic-api/issues/54). +`agentic-api` should accept Codex CLI traffic, route inference to vLLM-supported models, preserve +`previous_response_id` and conversation persistence, and pass client-owned tool calls back to Codex for local +execution. + +The immediate compatibility gap is request parsing and pass-through behavior. `agentic-api` already supports +`type: "function"`, but it does not yet recognize the Responses API tool shapes Codex uses for local/client tools: +`namespace`, `tool_search`, and `custom`. Today those request tools can fail before they reach upstream inference. +This section scopes the Codex integration to accepting those tool types losslessly and returning client-owned tool +calls to Codex CLI for local execution. + +Server-hosted tool types such as `file_search`, `web_search_preview`, and `code_interpreter` remain future +server-side work. They should not be conflated with this Codex compatibility pass. + +### Codex Tool Type Taxonomy + +Codex CLI sends tool declarations that are executed locally by the CLI. For this phase, `agentic-core` only needs +to recognize and preserve these shapes, normalize them for vLLM when necessary, and avoid treating them as +gateway-executed tools. + +| Tool type | Executor | Core behavior | +|-----------|----------|---------------| +| `function` | Codex CLI by default | Already supported on the wire. Codex requests should return calls to Codex unless configuration marks the tool as gateway-owned. | +| `namespace` | Codex CLI | Accept the model-facing container shape and preserve child function metadata. Calls still arrive as `function_call` with an optional namespace. | +| `tool_search` | Codex CLI | Accept deferred-discovery shape, preserve `execution`, and return calls/output handling to Codex when `execution = "client"`. | +| `custom` | Codex CLI | Accept free-form/grammar tool shape and preserve `format` metadata for Codex. | + +The key requirement is compatibility, not server execution. These tools should pass through `agentic-api` +without request validation failures, and any client-owned model-emitted calls should be surfaced to Codex CLI +rather than executed inside the gateway. + +The key distinction is execution owner, not just wire type. `function` is a shared wire type: Codex can own local +functions, while future gateway integrations may also expose server-executed functions. The request normalizer must +classify every model-visible tool before inference and carry that registry through response handling. + +### Public Type Additions + +The current `ResponsesTool = FunctionTool` alias is too narrow for Codex. Replace it with a tagged tool enum that +preserves unknown shapes while giving Codex-used variants first-class names. + +Do not implement this as `#[serde(tag = "type")]` wrapping the existing `FunctionTool` struct directly, because +`FunctionTool` already stores the wire `type` field. Either use variant-specific payload structs that omit the +already-consumed tag, as sketched below, or write manual deserialization that preserves the raw object. + +```rust +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ResponsesTool { + Known(KnownResponsesTool), + Unknown(Value), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum KnownResponsesTool { + #[serde(rename = "function")] + Function(ResponsesFunctionTool), + #[serde(rename = "namespace")] + Namespace(CodexNamespaceTool), + #[serde(rename = "tool_search")] + ToolSearch(CodexToolSearchTool), + #[serde(rename = "custom")] + Custom(CodexCustomTool), +} + +pub struct ResponsesFunctionTool { + pub name: String, + pub description: Option, + pub parameters: Option, + pub strict: Option, + #[serde(default)] + pub defer_loading: bool, +} + +pub struct CodexNamespaceTool { + pub name: String, + pub description: Option, + #[serde(default)] + pub tools: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum CodexNamespaceMember { + #[serde(rename = "function")] + Function(ResponsesFunctionTool), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ToolSearchExecution { + Server, + Client, +} + +pub struct CodexToolSearchTool { + pub description: Option, + pub execution: Option, + pub parameters: Option, +} + +pub struct CodexCustomTool { + pub name: String, + pub description: Option, + pub format: Option, + #[serde(default)] + pub defer_loading: bool, +} +``` + +The storage and upstream request paths should preserve raw unknown tools as `Value`. Unknown tool types must not +be executed by default. + +### Codex Call Shapes + +Codex's local router treats namespace as part of a function tool name, not as a separate payload type: + +| Response item | Local Codex payload | Gateway behavior | +|---------------|---------------------|------------------| +| `function_call` | `ToolPayload::Function { arguments }` | Preserve optional `namespace` and return the call to Codex when the tool is client-owned. | +| `tool_search_call` with `execution = "client"` | `ToolPayload::ToolSearch` | Return to Codex for local deferred discovery. | +| Hosted `tool_search_call` | Provider-owned | Do not execute locally; provider/upstream owns it. | +| `custom_tool_call` | `ToolPayload::Custom { input }` | Preserve free-form `input`, not JSON-schema function arguments. | + +`custom_tool_call` is for free-form/custom Responses tools, including grammar-based patch or code tools. It should +not be normalized as JSON-schema function arguments unless the adapter can reconstruct the original custom call +exactly. + +### Normalization And Registry + +Codex compatibility needs two related operations: + +1. Build an upstream-safe tool list for the selected inference backend. +2. Keep a lossless registry that maps model-emitted calls back to the original client-visible tool declaration. + +If vLLM only accepts flat function declarations, `tool_search` and `custom` become request normalization concerns. +`namespace` is mostly model-facing spec organization: `ToolSpec::Namespace` wraps function tools, and the model +still emits `function_call` with an optional `namespace`. Any backend-specific flattening is an adapter detail, +not the public semantics of the tool. + +```rust +pub struct NormalizedTools { + pub upstream_tools: Vec, + pub registry: ToolRegistry, +} + +pub struct ToolName { + pub namespace: Option, + pub name: String, +} + +pub enum ToolExecutionOwner { + Client, + Gateway, +} + +pub struct ToolRegistryEntry { + pub owner: ToolExecutionOwner, + pub original_type: String, + pub original_name: ToolName, + pub model_name: ToolName, + pub original_tool: Value, +} +``` + +For a namespace tool: + +```json +{ + "type": "namespace", + "name": "mcp__github", + "tools": [ + { "name": "create_issue", "description": "Create issue", "parameters": {} } + ] +} +``` + +the normalizer records a registry entry keyed by the split `ToolName`: + +```text +ToolName { namespace: Some("mcp__github"), name: "create_issue" } + -> owner = Client, original_type = "namespace" +``` + +Because the registry keys by split `ToolName`, two tools named `run` in different namespaces can coexist. When the +upstream response includes a `namespace` field on a `function_call`, preserve it. When an upstream backend only +returns an encoded flat name, recover the namespace and child tool name from the registry before returning the +response to Codex. This likely requires extending `FunctionToolCall` with: + +```rust +pub namespace: Option, +``` + +`tool_search` may need to be adapted for backends that only understand functions, but the registry must preserve +`execution` and map client-executed `tool_search_call` / `tool_search_output` items back to the +Responses-compatible shape. Hosted/non-client tool search remains provider-owned and should not be handled by the +local Codex route. `custom` carries free-form `input` instead of JSON arguments, so the registry must retain +`format` and enough raw metadata to reconstruct the Codex-visible call. + +### Pass-Through Behavior + +Routing rules: + +1. Client-owned calls (`function`, namespaced `function`, client-executed `tool_search`, and `custom`) are returned + to Codex without gateway execution. +2. Gateway-owned calls execute inside `agentic-api` only when explicitly supported by a registered executor. +3. `namespace`, `tool_search`, and `custom` request declarations must not fail deserialization or validation. +4. The registry preserves the original request tool shape so the returned call can be interpreted by Codex CLI. +5. Unknown tool types are not executed by default. Preserve them when possible and reject them only when the + upstream cannot receive a safe normalized declaration. + +For Codex-owned calls, the gateway should not synthesize tool outputs. It persists the assistant call item, returns +the call to Codex, and expects Codex to continue the conversation with the corresponding tool output item after +local execution. + +The loop needs an explicit client-action decision so this path is not confused with either `Done` or +`Continue`: + +```rust +pub enum LoopDecision { + Continue(Vec), + RequiresClientAction(Vec), + Done, + Incomplete(String), +} +``` + +### Auto-Approval Model Alias + +[Issue #54](https://github.com/vllm-project/agentic-api/issues/54) also notes Codex's auto-approval request path. MVP support should add a simple model alias map in +configuration: + +```toml +[model_aliases] +codex-auto-review = "real-upstream-model" +``` + +`ExecutionContext` resolves aliases before `call_inference()`. A Codex-specific `/v1/models` response with model metadata can come later; the alias map is sufficient to unblock CLI compatibility without expanding the public API. + +--- + ## Open Questions 1. **`execute_loop` vs refactoring `execute`:** Should the loop wrapper be a new function or replace PR #46's `execute()`? Pending maralbahari's response on PR #46 review. diff --git a/scripts/codex-mcp-fixture-server.py b/scripts/codex-mcp-fixture-server.py new file mode 100755 index 0000000..6e4e020 --- /dev/null +++ b/scripts/codex-mcp-fixture-server.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +"""Small stdio MCP server used to verify Codex namespace tool round-trips.""" + +from __future__ import annotations + +import json +import os +import re +import sys +from pathlib import Path +from typing import Any + + +SERVER_NAME = "agentic_fixture" +SERVER_VERSION = "0.1.0" +REPO_ROOT = Path(os.environ.get("AGENTIC_FIXTURE_ROOT", Path(__file__).resolve().parents[1])).resolve() +SKIP_DIRS = {".git", "target", "__pycache__", "codex_captures"} +MAX_READ_BYTES = 12_000 + +TOOLS = [ + { + "name": "run", + "description": "Echo a command string for agentic-api Codex namespace round-trip validation.", + "inputSchema": { + "type": "object", + "properties": { + "cmd": {"type": "string"}, + }, + "required": ["cmd"], + "additionalProperties": False, + }, + }, + { + "name": "echo_text", + "description": "Echo text with basic metadata. Useful for proving a simple MCP function call worked.", + "inputSchema": { + "type": "object", + "properties": { + "text": {"type": "string"}, + "uppercase": {"type": "boolean", "default": False}, + }, + "required": ["text"], + "additionalProperties": False, + }, + }, + { + "name": "add_numbers", + "description": "Add a list of numbers and return the total.", + "inputSchema": { + "type": "object", + "properties": { + "numbers": { + "type": "array", + "items": {"type": "number"}, + "minItems": 1, + }, + }, + "required": ["numbers"], + "additionalProperties": False, + }, + }, + { + "name": "make_slug", + "description": "Turn text into a lowercase URL/file-name friendly slug.", + "inputSchema": { + "type": "object", + "properties": { + "text": {"type": "string"}, + "separator": {"type": "string", "default": "-"}, + }, + "required": ["text"], + "additionalProperties": False, + }, + }, + { + "name": "repo_file_head", + "description": "Read the first lines of a repository file, limited to the agentic-api workspace.", + "inputSchema": { + "type": "object", + "properties": { + "path": {"type": "string"}, + "lines": {"type": "integer", "minimum": 1, "maximum": 80, "default": 20}, + }, + "required": ["path"], + "additionalProperties": False, + }, + }, + { + "name": "search_repo", + "description": "Literal text search across repository files, returning a small capped result set.", + "inputSchema": { + "type": "object", + "properties": { + "query": {"type": "string"}, + "path_prefix": {"type": "string", "default": "."}, + "max_results": {"type": "integer", "minimum": 1, "maximum": 30, "default": 10}, + }, + "required": ["query"], + "additionalProperties": False, + }, + }, +] + + +def read_message() -> tuple[dict[str, Any], str] | None: + headers: dict[str, str] = {} + first_line = sys.stdin.buffer.readline() + if first_line == b"": + return None + stripped = first_line.strip() + if stripped.startswith(b"{"): + return json.loads(stripped.decode("utf-8")), "line" + + line = first_line.decode("ascii", "replace").strip() + if line: + name, _, value = line.partition(":") + headers[name.lower()] = value.strip() + + while True: + line = sys.stdin.buffer.readline() + if line == b"": + return None + line = line.decode("ascii", "replace").strip() + if not line: + break + name, _, value = line.partition(":") + headers[name.lower()] = value.strip() + + length = int(headers.get("content-length", "0")) + if length <= 0: + return None + body = sys.stdin.buffer.read(length) + return json.loads(body.decode("utf-8")), "content-length" + + +def write_message(message: dict[str, Any], framing: str) -> None: + body = json.dumps(message, separators=(",", ":")).encode("utf-8") + if framing == "line": + sys.stdout.buffer.write(body + b"\n") + sys.stdout.buffer.flush() + return + sys.stdout.buffer.write(f"Content-Length: {len(body)}\r\n\r\n".encode("ascii")) + sys.stdout.buffer.write(body) + sys.stdout.buffer.flush() + + +def result_response(request_id: Any, result: dict[str, Any]) -> dict[str, Any]: + return {"jsonrpc": "2.0", "id": request_id, "result": result} + + +def error_response(request_id: Any, code: int, message: str) -> dict[str, Any]: + return {"jsonrpc": "2.0", "id": request_id, "error": {"code": code, "message": message}} + + +def text_result(text: str) -> dict[str, Any]: + return {"content": [{"type": "text", "text": text}], "isError": False} + + +def json_text_result(value: Any) -> dict[str, Any]: + return text_result(json.dumps(value, indent=2, sort_keys=True)) + + +def argument_error(request_id: Any, message: str) -> dict[str, Any]: + return error_response(request_id, -32602, message) + + +def string_arg(arguments: dict[str, Any], name: str) -> str | None: + value = arguments.get(name) + return value if isinstance(value, str) else None + + +def int_arg(arguments: dict[str, Any], name: str, default: int, minimum: int, maximum: int) -> int: + value = arguments.get(name, default) + if not isinstance(value, int): + return default + return max(minimum, min(maximum, value)) + + +def resolve_repo_path(relative_path: str) -> Path | None: + candidate = (REPO_ROOT / relative_path).resolve() + try: + candidate.relative_to(REPO_ROOT) + except ValueError: + return None + return candidate + + +def repo_relative(path: Path) -> str: + return str(path.relative_to(REPO_ROOT)) + + +def read_limited_text(path: Path) -> str: + with path.open("rb") as fh: + return fh.read(MAX_READ_BYTES).decode("utf-8", "replace") + + +def handle_run(arguments: dict[str, Any], request_id: Any) -> dict[str, Any]: + cmd = string_arg(arguments, "cmd") + if cmd is None: + return argument_error(request_id, "missing string argument: cmd") + return result_response(request_id, text_result(f"agentic_fixture.run received cmd={cmd}")) + + +def handle_echo_text(arguments: dict[str, Any], request_id: Any) -> dict[str, Any]: + text = string_arg(arguments, "text") + if text is None: + return argument_error(request_id, "missing string argument: text") + echoed = text.upper() if arguments.get("uppercase") is True else text + return result_response( + request_id, + json_text_result({"echo": echoed, "characters": len(text), "words": len(text.split())}), + ) + + +def handle_add_numbers(arguments: dict[str, Any], request_id: Any) -> dict[str, Any]: + numbers = arguments.get("numbers") + if not isinstance(numbers, list) or not numbers: + return argument_error(request_id, "missing non-empty array argument: numbers") + if not all(isinstance(number, (int, float)) and not isinstance(number, bool) for number in numbers): + return argument_error(request_id, "numbers must contain only numeric values") + total = sum(numbers) + return result_response(request_id, json_text_result({"count": len(numbers), "sum": total})) + + +def handle_make_slug(arguments: dict[str, Any], request_id: Any) -> dict[str, Any]: + text = string_arg(arguments, "text") + if text is None: + return argument_error(request_id, "missing string argument: text") + separator = string_arg(arguments, "separator") or "-" + separator = separator[:1] or "-" + slug = re.sub(r"[^a-z0-9]+", separator, text.lower()).strip(separator) + return result_response(request_id, json_text_result({"slug": slug})) + + +def handle_repo_file_head(arguments: dict[str, Any], request_id: Any) -> dict[str, Any]: + relative = string_arg(arguments, "path") + if relative is None: + return argument_error(request_id, "missing string argument: path") + path = resolve_repo_path(relative) + if path is None or not path.is_file(): + return argument_error(request_id, f"file not found inside repo: {relative}") + + line_limit = int_arg(arguments, "lines", 20, 1, 80) + text = read_limited_text(path) + selected = text.splitlines()[:line_limit] + numbered = "\n".join(f"{idx + 1}: {line}" for idx, line in enumerate(selected)) + return result_response( + request_id, + text_result(f"{repo_relative(path)} first {len(selected)} lines:\n{numbered}"), + ) + + +def iter_search_files(base: Path) -> Any: + for path in base.rglob("*"): + if any(part in SKIP_DIRS for part in path.relative_to(REPO_ROOT).parts): + continue + if path.is_file(): + yield path + + +def handle_search_repo(arguments: dict[str, Any], request_id: Any) -> dict[str, Any]: + query = string_arg(arguments, "query") + if not query: + return argument_error(request_id, "missing non-empty string argument: query") + prefix = string_arg(arguments, "path_prefix") or "." + base = resolve_repo_path(prefix) + if base is None or not base.exists(): + return argument_error(request_id, f"path_prefix not found inside repo: {prefix}") + max_results = int_arg(arguments, "max_results", 10, 1, 30) + + matches = [] + files = [base] if base.is_file() else iter_search_files(base) + for path in files: + try: + text = read_limited_text(path) + except OSError: + continue + for line_no, line in enumerate(text.splitlines(), start=1): + if query in line: + matches.append({"path": repo_relative(path), "line": line_no, "text": line.strip()[:240]}) + if len(matches) >= max_results: + return result_response(request_id, json_text_result({"query": query, "matches": matches})) + + return result_response(request_id, json_text_result({"query": query, "matches": matches})) + + +TOOL_HANDLERS = { + "run": handle_run, + "echo_text": handle_echo_text, + "add_numbers": handle_add_numbers, + "make_slug": handle_make_slug, + "repo_file_head": handle_repo_file_head, + "search_repo": handle_search_repo, +} + + +def handle_request(message: dict[str, Any]) -> dict[str, Any] | None: + request_id = message.get("id") + method = message.get("method") + + if request_id is None: + return None + + if method == "initialize": + return result_response( + request_id, + { + "protocolVersion": "2024-11-05", + "capabilities": {"tools": {"listChanged": False}}, + "serverInfo": {"name": SERVER_NAME, "version": SERVER_VERSION}, + }, + ) + + if method == "ping": + return result_response(request_id, {}) + + if method == "tools/list": + return result_response(request_id, {"tools": TOOLS}) + + if method == "tools/call": + params = message.get("params") if isinstance(message.get("params"), dict) else {} + name = params.get("name") + arguments = params.get("arguments") if isinstance(params.get("arguments"), dict) else {} + handler = TOOL_HANDLERS.get(name) + if handler is None: + return error_response(request_id, -32602, f"unknown tool: {name}") + return handler(arguments, request_id) + + if method in {"resources/list", "prompts/list"}: + key = "resources" if method == "resources/list" else "prompts" + return result_response(request_id, {key: []}) + + return error_response(request_id, -32601, f"method not found: {method}") + + +def main() -> int: + while True: + received = read_message() + if received is None: + return 0 + message, framing = received + response = handle_request(message) + if response is not None: + write_message(response, framing) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/codex-start-gateway.sh b/scripts/codex-start-gateway.sh new file mode 100755 index 0000000..83546d2 --- /dev/null +++ b/scripts/codex-start-gateway.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -euo pipefail + +require_env() { + local name="$1" + if [[ -z "${!name:-}" ]]; then + echo "error: set ${name}" >&2 + exit 2 + fi +} + +require_env V_API_BASE +require_env V_MODEL + +if [[ "$V_API_BASE" != *"://"* ]]; then + V_API_BASE="http://${V_API_BASE}" +fi + +GATEWAY_HOST="${GATEWAY_HOST:-127.0.0.1}" +GATEWAY_PORT="${GATEWAY_PORT:-3000}" +MODEL_ALIAS="${MODEL_ALIAS:-codex-compatible}" +DATABASE_URL="${DATABASE_URL:-sqlite://./agentic_api_codex.db}" +SKIP_LLM_READY_CHECK="${SKIP_LLM_READY_CHECK:-true}" + +if [[ -n "${V_API_KEY:-}" ]]; then + export OPENAI_API_KEY="${OPENAI_API_KEY:-$V_API_KEY}" +else + unset OPENAI_API_KEY +fi +export DATABASE_URL + +echo "Starting agentic-api gateway on http://${GATEWAY_HOST}:${GATEWAY_PORT}" +echo "Upstream base: ${V_API_BASE}" +echo "Model alias: ${MODEL_ALIAS}=${V_MODEL}" +echo "Skip readiness check: ${SKIP_LLM_READY_CHECK}" + +ready_args=() +if [[ "$SKIP_LLM_READY_CHECK" == "true" || "$SKIP_LLM_READY_CHECK" == "1" ]]; then + ready_args+=(--skip-llm-ready-check) +fi + +exec cargo run -p agentic-server -- \ + --gateway-host "$GATEWAY_HOST" \ + --gateway-port "$GATEWAY_PORT" \ + --llm-api-base "$V_API_BASE" \ + "${ready_args[@]}" \ + --model-alias "${MODEL_ALIAS}=${V_MODEL}"