// Responses API response types use crate::protocols::openai::responses::request::ResponsesRequest; use crate::protocols::openai::responses::types::*; use serde::{Deserialize, Serialize}; use std::collections::HashMap; fn generate_response_id() -> String { format!("resp_{}", uuid::Uuid::new_v4().simple()) } fn current_timestamp() -> i64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap_or_else(|_| std::time::Duration::from_secs(0)) .as_secs() as i64 } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct ResponsesResponse { /// Response ID #[serde(default = "generate_response_id")] pub id: String, /// Object type #[serde(default = "default_object_type")] pub object: String, /// Creation timestamp #[serde(default = "current_timestamp")] pub created_at: i64, /// Model name pub model: String, /// Output items #[serde(default)] pub output: Vec, /// Response status pub status: ResponseStatus, /// Usage information #[serde(skip_serializing_if = "Option::is_none")] pub usage: Option, /// Whether parallel tool calls are enabled #[serde(default = "default_true")] pub parallel_tool_calls: bool, /// Tool choice setting #[serde(default = "default_tool_choice")] pub tool_choice: String, /// Available tools #[serde(default)] pub tools: Vec, } fn default_object_type() -> String { "response".to_string() } fn default_true() -> bool { true } fn default_tool_choice() -> String { "auto".to_string() } impl ResponsesResponse { /// Create a response from a request #[allow(clippy::too_many_arguments)] pub fn from_request( request: &ResponsesRequest, _sampling_params: &HashMap, model_name: String, created_time: i64, output: Vec, status: ResponseStatus, usage: Option, ) -> Self { Self { id: request.request_id.clone(), object: "response".to_string(), created_at: created_time, model: model_name, output, status, usage, parallel_tool_calls: request.parallel_tool_calls, tool_choice: match request.tool_choice { ToolChoice::Auto => "auto".to_string(), ToolChoice::Required => "required".to_string(), ToolChoice::None => "none".to_string(), }, tools: request.tools.clone(), } } /// Create a new response with default values pub fn new(request_id: String, model: String, status: ResponseStatus) -> Self { Self { id: request_id, object: "response".to_string(), created_at: current_timestamp(), model, output: Vec::new(), status, usage: None, parallel_tool_calls: true, tool_choice: "auto".to_string(), tools: Vec::new(), } } /// Add an output item to the response pub fn add_output(&mut self, item: ResponseOutputItem) { self.output.push(item); } /// Set the usage information pub fn set_usage(&mut self, usage: UsageInfo) { self.usage = Some(usage); } /// Update the status pub fn set_status(&mut self, status: ResponseStatus) { self.status = status; } /// Check if the response is complete pub fn is_complete(&self) -> bool { matches!(self.status, ResponseStatus::Completed) } /// Check if the response is in progress pub fn is_in_progress(&self) -> bool { matches!(self.status, ResponseStatus::InProgress) } /// Check if the response failed pub fn is_failed(&self) -> bool { matches!(self.status, ResponseStatus::Failed) } /// Check if the response was cancelled pub fn is_cancelled(&self) -> bool { matches!(self.status, ResponseStatus::Cancelled) } /// Check if the response is queued pub fn is_queued(&self) -> bool { matches!(self.status, ResponseStatus::Queued) } /// Convert usage to OpenAI Responses API format pub fn usage_in_response_format( &self, ) -> Option { self.usage.as_ref().map(|usage| usage.to_response_usage()) } /// Get the response as a JSON value with usage in response format pub fn to_response_format(&self) -> serde_json::Value { let mut response = serde_json::to_value(self).unwrap_or(serde_json::Value::Null); // Convert usage to response format if present if let Some(usage) = &self.usage { if let Ok(usage_value) = serde_json::to_value(usage.to_response_usage()) { response["usage"] = usage_value; } } response } } // ============= Helper Functions ============= impl ResponseOutputItem { /// Create a new message output item pub fn new_message( id: String, role: String, content: Vec, status: String, ) -> Self { Self::Message { id, role, content, status, } } /// Create a new reasoning output item pub fn new_reasoning( id: String, summary: Vec, content: Vec, status: Option, ) -> Self { Self::Reasoning { id, summary, content, status, } } /// Create a new function tool call output item pub fn new_function_tool_call( id: String, name: String, arguments: String, output: Option, status: String, ) -> Self { Self::FunctionToolCall { id, name, arguments, output, status, } } } impl ResponseContentPart { /// Create a new text content part pub fn new_text( text: String, annotations: Vec, logprobs: Option, ) -> Self { Self::OutputText { text, annotations, logprobs, } } } impl ResponseReasoningContent { /// Create a new reasoning text content pub fn new_reasoning_text(text: String) -> Self { Self::ReasoningText { text } } } impl UsageInfo { /// Create a new usage info with token counts pub fn new(prompt_tokens: u32, completion_tokens: u32, reasoning_tokens: Option) -> Self { Self { prompt_tokens, completion_tokens, total_tokens: prompt_tokens + completion_tokens, reasoning_tokens, prompt_tokens_details: None, } } /// Create usage info with cached token details pub fn new_with_cached( prompt_tokens: u32, completion_tokens: u32, reasoning_tokens: Option, cached_tokens: u32, ) -> Self { Self { prompt_tokens, completion_tokens, total_tokens: prompt_tokens + completion_tokens, reasoning_tokens, prompt_tokens_details: Some(PromptTokenUsageInfo { cached_tokens }), } } }