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::RateLimitSnapshot; use codex_protocol::protocol::TokenUsage; use futures::Stream; use serde::Serialize; use serde_json::Value; use std::pin::Pin; use std::task::Context; use std::task::Poll; use tokio::sync::mpsc; /// Canonical prompt input for Chat and Responses endpoints. #[derive(Debug, Clone)] pub struct Prompt { /// Fully-resolved system instructions for this turn. pub instructions: String, /// Conversation history and user/tool messages. pub input: Vec, /// JSON-encoded tool definitions compatible with the target API. // TODO(jif) have a proper type here pub tools: Vec, /// Whether parallel tool calls are permitted. pub parallel_tool_calls: bool, /// Optional output schema used to build the `text.format` controls. pub output_schema: Option, } /// Canonical input payload for the compaction endpoint. #[derive(Debug, Clone, Serialize)] pub struct CompactionInput<'a> { pub model: &'a str, pub input: &'a [ResponseItem], pub instructions: &'a str, } #[derive(Debug)] pub enum ResponseEvent { Created, OutputItemDone(ResponseItem), OutputItemAdded(ResponseItem), Completed { response_id: String, token_usage: Option, }, OutputTextDelta(String), ReasoningSummaryDelta { delta: String, summary_index: i64, }, ReasoningContentDelta { delta: String, content_index: i64, }, ReasoningSummaryPartAdded { summary_index: i64, }, RateLimits(RateLimitSnapshot), } #[derive(Debug, Serialize, Clone)] pub struct Reasoning { #[serde(skip_serializing_if = "Option::is_none")] pub effort: Option, #[serde(skip_serializing_if = "Option::is_none")] pub summary: Option, } #[derive(Debug, Serialize, Default, Clone)] #[serde(rename_all = "snake_case")] pub enum TextFormatType { #[default] JsonSchema, } #[derive(Debug, Serialize, Default, Clone)] 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)] pub struct TextControls { #[serde(skip_serializing_if = "Option::is_none")] pub verbosity: Option, #[serde(skip_serializing_if = "Option::is_none")] pub format: Option, } #[derive(Debug, Serialize, Default, Clone)] #[serde(rename_all = "lowercase")] pub enum OpenAiVerbosity { Low, #[default] Medium, High, } impl From for OpenAiVerbosity { fn from(v: VerbosityConfig) -> Self { match v { VerbosityConfig::Low => OpenAiVerbosity::Low, VerbosityConfig::Medium => OpenAiVerbosity::Medium, VerbosityConfig::High => OpenAiVerbosity::High, } } } #[derive(Debug, Serialize)] pub struct ResponsesApiRequest<'a> { pub model: &'a str, pub instructions: &'a str, pub input: &'a [ResponseItem], pub tools: &'a [serde_json::Value], pub tool_choice: &'static str, pub parallel_tool_calls: bool, pub reasoning: Option, pub store: bool, pub stream: bool, pub include: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub prompt_cache_key: Option, #[serde(skip_serializing_if = "Option::is_none")] pub text: Option, } pub fn create_text_param_for_request( verbosity: Option, output_schema: &Option, ) -> Option { 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: true, schema: schema.clone(), name: "codex_output_schema".to_string(), }), }) } pub struct ResponseStream { pub rx_event: mpsc::Receiver>, } impl Stream for ResponseStream { type Item = Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.rx_event.poll_recv(cx) } }