204 lines
6 KiB
Rust
204 lines
6 KiB
Rust
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 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<ResponseItem>,
|
|
/// JSON-encoded tool definitions compatible with the target API.
|
|
// TODO(jif) have a proper type here
|
|
pub tools: Vec<Value>,
|
|
/// 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<Value>,
|
|
}
|
|
|
|
/// 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),
|
|
/// 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>,
|
|
},
|
|
OutputTextDelta(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)]
|
|
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>,
|
|
}
|
|
|
|
#[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<OpenAiVerbosity>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub format: Option<TextFormat>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Default, Clone)]
|
|
#[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)]
|
|
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<Reasoning>,
|
|
pub store: bool,
|
|
pub stream: bool,
|
|
pub include: Vec<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>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct ResponseCreateWsRequest {
|
|
pub model: String,
|
|
pub instructions: 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 prompt_cache_key: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub text: Option<TextControls>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct ResponseAppendWsRequest {
|
|
pub input: Vec<ResponseItem>,
|
|
}
|
|
#[derive(Debug, Serialize)]
|
|
#[serde(tag = "type")]
|
|
#[allow(clippy::large_enum_variant)]
|
|
pub enum ResponsesWsRequest {
|
|
#[serde(rename = "response.create")]
|
|
ResponseCreate(ResponseCreateWsRequest),
|
|
#[serde(rename = "response.append")]
|
|
ResponseAppend(ResponseAppendWsRequest),
|
|
}
|
|
|
|
pub fn create_text_param_for_request(
|
|
verbosity: Option<VerbosityConfig>,
|
|
output_schema: &Option<Value>,
|
|
) -> 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: true,
|
|
schema: schema.clone(),
|
|
name: "codex_output_schema".to_string(),
|
|
}),
|
|
})
|
|
}
|
|
|
|
pub struct ResponseStream {
|
|
pub rx_event: mpsc::Receiver<Result<ResponseEvent, ApiError>>,
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|