use crate::error::ApiError;use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;use codex_protocol::config_types::Verbosity as VerbosityConfig;use codex_protocol::models::ResponseItem;use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;use codex_protocol::protocol::ModelVerification;use codex_protocol::protocol::RateLimitSnapshot;use codex_protocol::protocol::TokenUsage;use codex_protocol::protocol::TurnModerationMetadataEvent;use codex_protocol::protocol::W3cTraceContext;use futures::Stream;use serde::Deserialize;use serde::Serialize;use serde_json::Value;use std::collections::HashMap;use std::pin::Pin;use std::task::Context;use std::task::Poll;use tokio::sync::mpsc;pub const WS_REQUEST_HEADER_TRACEPARENT_CLIENT_METADATA_KEY: &str = "ws_request_header_traceparent";pub const WS_REQUEST_HEADER_TRACESTATE_CLIENT_METADATA_KEY: &str = "ws_request_header_tracestate";/// Canonical input payload for the compaction endpoint.#[derive(Debug, Clone, Serialize)]pub struct CompactionInput<'a> { pub model: &'a str, pub input: &'a [ResponseItem], #[serde(skip_serializing_if = "str::is_empty")] pub instructions: &'a str, pub tools: Vec<Value>, pub parallel_tool_calls: bool, #[serde(skip_serializing_if = "Option::is_none")] pub reasoning: Option<Reasoning>, #[serde(skip_serializing_if = "Option::is_none")] pub service_tier: Option<&'a str>, #[serde(skip_serializing_if = "Option::is_none")] pub prompt_cache_key: Option<&'a str>, #[serde(skip_serializing_if = "Option::is_none")] pub text: Option<TextControls>,}/// Canonical input payload for the memory summarize endpoint.#[derive(Debug, Clone, Serialize)]pub struct MemorySummarizeInput { pub model: String, #[serde(rename = "traces")] pub raw_memories: Vec<RawMemory>, #[serde(skip_serializing_if = "Option::is_none")] pub reasoning: Option<Reasoning>,}#[derive(Debug, Clone, Serialize)]pub struct RawMemory { pub id: String, pub metadata: RawMemoryMetadata, pub items: Vec<Value>,}#[derive(Debug, Clone, Serialize)]pub struct RawMemoryMetadata { pub source_path: String,}#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]pub struct MemorySummarizeOutput { #[serde(rename = "trace_summary", alias = "raw_memory")] pub raw_memory: String, pub memory_summary: String,}#[derive(Debug)]pub enum ResponseEvent { Created, OutputItemDone(ResponseItem), OutputItemAdded(ResponseItem), /// Emitted when the server includes `OpenAI-Model` on the stream response. /// This can differ from the requested model when backend safety routing applies. ServerModel(String), /// Emitted when the server recommends additional account verification. ModelVerifications(Vec<ModelVerification>), /// Emitted when the server includes moderation metadata for first-party turn presentation. TurnModerationMetadata(TurnModerationMetadataEvent), /// Emitted when `X-Reasoning-Included: true` is present on the response, /// meaning the server already accounted for past reasoning tokens and the /// client should not re-estimate them. ServerReasoningIncluded(bool), Completed { response_id: String, token_usage: Option<TokenUsage>, /// Did the model affirmatively end its turn? Some providers do not set this, /// so we rely on fallback logic when this is `None`. end_turn: Option<bool>, }, OutputTextDelta(String), ToolCallInputDelta { item_id: String, call_id: Option<String>, delta: String, }, ReasoningSummaryDelta { delta: String, summary_index: i64, }, ReasoningContentDelta { delta: String, content_index: i64, }, ReasoningSummaryPartAdded { summary_index: i64, }, RateLimits(RateLimitSnapshot), ModelsEtag(String),}#[derive(Debug, Serialize, Clone, PartialEq)]#[serde(rename_all = "snake_case")]pub enum ReasoningContext { Auto, CurrentTurn, AllTurns,}#[derive(Debug, Serialize, Clone, PartialEq)]pub struct Reasoning { #[serde(skip_serializing_if = "Option::is_none")] pub effort: Option<ReasoningEffortConfig>, #[serde(skip_serializing_if = "Option::is_none")] pub summary: Option<ReasoningSummaryConfig>, #[serde(skip_serializing_if = "Option::is_none")] pub context: Option<ReasoningContext>,}#[derive(Debug, Serialize, Default, Clone, PartialEq)]#[serde(rename_all = "snake_case")]pub enum TextFormatType { #[default] JsonSchema,}#[derive(Debug, Serialize, Default, Clone, PartialEq)]pub struct TextFormat { /// Format type used by the OpenAI text controls. pub r#type: TextFormatType, /// When true, the server is expected to strictly validate responses. pub strict: bool, /// JSON schema for the desired output. pub schema: Value, /// Friendly name for the format, used in telemetry/debugging. pub name: String,}/// Controls the `text` field for the Responses API, combining verbosity and/// optional JSON schema output formatting.#[derive(Debug, Serialize, Default, Clone, PartialEq)]pub struct TextControls { #[serde(skip_serializing_if = "Option::is_none")] pub verbosity: Option<OpenAiVerbosity>, #[serde(skip_serializing_if = "Option::is_none")] pub format: Option<TextFormat>,}#[derive(Debug, Serialize, Default, Clone, PartialEq)]#[serde(rename_all = "lowercase")]pub enum OpenAiVerbosity { Low, #[default] Medium, High,}impl From<VerbosityConfig> for OpenAiVerbosity { fn from(v: VerbosityConfig) -> Self { match v { VerbosityConfig::Low => OpenAiVerbosity::Low, VerbosityConfig::Medium => OpenAiVerbosity::Medium, VerbosityConfig::High => OpenAiVerbosity::High, } }}#[derive(Debug, Serialize, Clone, PartialEq)]pub struct ResponsesApiRequest { pub model: String, #[serde(skip_serializing_if = "String::is_empty")] pub instructions: String, pub input: Vec<ResponseItem>, pub tools: Vec<serde_json::Value>, pub tool_choice: String, pub parallel_tool_calls: bool, pub reasoning: Option<Reasoning>, pub store: bool, pub stream: bool, pub include: Vec<String>, #[serde(skip_serializing_if = "Option::is_none")] pub service_tier: Option<String>, #[serde(skip_serializing_if = "Option::is_none")] pub prompt_cache_key: Option<String>, #[serde(skip_serializing_if = "Option::is_none")] pub text: Option<TextControls>, #[serde(skip_serializing_if = "Option::is_none")] pub client_metadata: Option<HashMap<String, String>>,}impl From<&ResponsesApiRequest> for ResponseCreateWsRequest { fn from(request: &ResponsesApiRequest) -> Self { Self { model: request.model.clone(), instructions: request.instructions.clone(), previous_response_id: None, input: request.input.clone(), tools: request.tools.clone(), tool_choice: request.tool_choice.clone(), parallel_tool_calls: request.parallel_tool_calls, reasoning: request.reasoning.clone(), store: request.store, stream: request.stream, include: request.include.clone(), service_tier: request.service_tier.clone(), prompt_cache_key: request.prompt_cache_key.clone(), text: request.text.clone(), generate: None, client_metadata: request.client_metadata.clone(), } }}#[derive(Debug, Serialize)]pub struct ResponseCreateWsRequest { pub model: String, #[serde(skip_serializing_if = "String::is_empty")] pub instructions: String, #[serde(skip_serializing_if = "Option::is_none")] pub previous_response_id: Option<String>, pub input: Vec<ResponseItem>, pub tools: Vec<Value>, pub tool_choice: String, pub parallel_tool_calls: bool, pub reasoning: Option<Reasoning>, pub store: bool, pub stream: bool, pub include: Vec<String>, #[serde(skip_serializing_if = "Option::is_none")] pub service_tier: Option<String>, #[serde(skip_serializing_if = "Option::is_none")] pub prompt_cache_key: Option<String>, #[serde(skip_serializing_if = "Option::is_none")] pub text: Option<TextControls>, #[serde(skip_serializing_if = "Option::is_none")] pub generate: Option<bool>, #[serde(skip_serializing_if = "Option::is_none")] pub client_metadata: Option<HashMap<String, String>>,}pub fn response_create_client_metadata( client_metadata: Option<HashMap<String, String>>, trace: Option<&W3cTraceContext>,) -> Option<HashMap<String, String>> { let mut client_metadata = client_metadata.unwrap_or_default(); if let Some(traceparent) = trace.and_then(|trace| trace.traceparent.as_deref()) { client_metadata.insert( WS_REQUEST_HEADER_TRACEPARENT_CLIENT_METADATA_KEY.to_string(), traceparent.to_string(), ); } if let Some(tracestate) = trace.and_then(|trace| trace.tracestate.as_deref()) { client_metadata.insert( WS_REQUEST_HEADER_TRACESTATE_CLIENT_METADATA_KEY.to_string(), tracestate.to_string(), ); } (!client_metadata.is_empty()).then_some(client_metadata)}#[derive(Debug, Serialize)]#[serde(tag = "type")]#[allow(clippy::large_enum_variant)]pub enum ResponsesWsRequest { #[serde(rename = "response.create")] ResponseCreate(ResponseCreateWsRequest),}pub fn create_text_param_for_request( verbosity: Option<VerbosityConfig>, output_schema: &Option<Value>, output_schema_strict: bool,) -> Option<TextControls> { if verbosity.is_none() && output_schema.is_none() { return None; } Some(TextControls { verbosity: verbosity.map(std::convert::Into::into), format: output_schema.as_ref().map(|schema| TextFormat { r#type: TextFormatType::JsonSchema, strict: output_schema_strict, schema: schema.clone(), name: "codex_output_schema".to_string(), }), })}pub struct ResponseStream { pub rx_event: mpsc::Receiver<Result<ResponseEvent, ApiError>>, /// Server-assigned `x-request-id` response header, when present. pub upstream_request_id: Option<String>,}impl Stream for ResponseStream { type Item = Result<ResponseEvent, ApiError>; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { self.rx_event.poll_recv(cx) }}