Codex Handbook
core/src/client_common.rs 131 lines
pub use codex_api::ResponseEvent;use codex_config::types::Personality;use codex_protocol::error::Result;use codex_protocol::models::BaseInstructions;use codex_protocol::models::ContentItem;use codex_protocol::models::FunctionCallOutputContentItem;use codex_protocol::models::ResponseItem;use codex_tools::ToolSpec;use futures::Stream;use serde_json::Value;use std::pin::Pin;use std::task::Context;use std::task::Poll;use tokio::sync::mpsc;use tokio_util::sync::CancellationToken;/// API request payload for a single model turn#[derive(Debug, Clone)]pub struct Prompt {    /// Conversation context input items.    pub input: Vec<ResponseItem>,    /// Tools available to the model, including additional tools sourced from    /// external MCP servers.    pub(crate) tools: Vec<ToolSpec>,    /// Whether parallel tool calls are permitted for this prompt.    pub(crate) parallel_tool_calls: bool,    pub base_instructions: BaseInstructions,    /// Optionally specify the personality of the model.    pub personality: Option<Personality>,    /// Optional the output schema for the model's response.    pub output_schema: Option<Value>,    /// Whether the Responses API should strictly validate `output_schema`.    pub output_schema_strict: bool,}impl Default for Prompt {    fn default() -> Self {        Self {            input: Vec::new(),            tools: Vec::new(),            parallel_tool_calls: false,            base_instructions: BaseInstructions::default(),            personality: None,            output_schema: None,            output_schema_strict: true,        }    }}impl Prompt {    pub(crate) fn get_formatted_input_for_request(        &self,        use_responses_lite: bool,    ) -> Vec<ResponseItem> {        let mut input = self.input.clone();        if use_responses_lite {            strip_image_details(&mut input);        }        input    }}fn strip_image_details(items: &mut [ResponseItem]) {    for item in items {        match item {            ResponseItem::Message { content, .. } => {                for content_item in content {                    if let ContentItem::InputImage { detail, .. } = content_item {                        *detail = None;                    }                }            }            ResponseItem::FunctionCallOutput { output, .. }            | ResponseItem::CustomToolCallOutput { output, .. } => {                if let Some(content) = output.content_items_mut() {                    for content_item in content {                        if let FunctionCallOutputContentItem::InputImage { detail, .. } =                            content_item                        {                            *detail = None;                        }                    }                }            }            ResponseItem::Reasoning { .. }            | ResponseItem::AgentMessage { .. }            | ResponseItem::LocalShellCall { .. }            | ResponseItem::FunctionCall { .. }            | ResponseItem::ToolSearchCall { .. }            | ResponseItem::CustomToolCall { .. }            | ResponseItem::ToolSearchOutput { .. }            | ResponseItem::WebSearchCall { .. }            | ResponseItem::ImageGenerationCall { .. }            | ResponseItem::Compaction { .. }            | ResponseItem::CompactionTrigger { .. }            | ResponseItem::ContextCompaction { .. }            | ResponseItem::Other => {}        }    }}pub struct ResponseStream {    pub(crate) rx_event: mpsc::Receiver<Result<ResponseEvent>>,    /// Signals the mapper task that the consumer stopped polling before the    /// provider stream reached its own terminal event.    pub(crate) consumer_dropped: CancellationToken,}impl Stream for ResponseStream {    type Item = Result<ResponseEvent>;    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {        self.rx_event.poll_recv(cx)    }}impl Drop for ResponseStream {    fn drop(&mut self) {        self.consumer_dropped.cancel();    }}#[cfg(test)]#[path = "client_common_tests.rs"]mod tests;