| pub use codex_api::ResponseEvent; |
| use codex_protocol::error::Result; |
| use codex_protocol::models::BaseInstructions; |
| use codex_protocol::models::ContentItem; |
| use codex_protocol::models::DEFAULT_IMAGE_DETAIL; |
| use codex_protocol::models::FunctionCallOutputContentItem; |
| use codex_protocol::models::ImageDetail; |
| use codex_protocol::models::ResponseItem; |
| use codex_protocol::openai_models::ModelInfo; |
| use codex_tools::ToolSpec; |
| use futures::Stream; |
| use serde_json::Value; |
| use std::pin::Pin; |
| use std::sync::Arc; |
| use std::task::Context; |
| use std::task::Poll; |
| use tokio::sync::mpsc; |
| use tokio_util::sync::CancellationToken; |
|
|
| |
| #[derive(Debug, Clone)] |
| pub struct Prompt { |
| |
| pub input: Vec<ResponseItem>, |
|
|
| |
| |
| pub(crate) tools: Arc<[ToolSpec]>, |
|
|
| |
| pub(crate) parallel_tool_calls: bool, |
|
|
| pub base_instructions: BaseInstructions, |
|
|
| |
| pub output_schema: Option<Value>, |
|
|
| |
| pub output_schema_strict: bool, |
|
|
| pub(crate) cyber_access_program: Option<codex_protocol::turn_input::CyberAccessProgram>, |
| } |
|
|
| impl Default for Prompt { |
| fn default() -> Self { |
| Self { |
| input: Vec::new(), |
| tools: Arc::default(), |
| parallel_tool_calls: false, |
| base_instructions: BaseInstructions::default(), |
| output_schema: None, |
| output_schema_strict: true, |
| cyber_access_program: None, |
| } |
| } |
| } |
|
|
| impl Prompt { |
| pub(crate) fn get_formatted_input_for_request( |
| &self, |
| model_info: &ModelInfo, |
| ) -> Vec<ResponseItem> { |
| let mut input = self.input.clone(); |
| normalize_image_details(&mut input, model_info); |
| input |
| } |
| } |
|
|
| fn normalize_image_details(items: &mut [ResponseItem], model_info: &ModelInfo) { |
| for item in items { |
| match item { |
| ResponseItem::Message { content, .. } => { |
| for content_item in content { |
| if let ContentItem::InputImage { detail, .. } = content_item { |
| normalize_image_detail(detail, model_info); |
| } |
| } |
| } |
| 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 |
| { |
| normalize_image_detail(detail, model_info); |
| } |
| } |
| } |
| } |
| ResponseItem::AdditionalTools { .. } |
| | ResponseItem::Reasoning { .. } |
| | ResponseItem::AgentMessage { .. } |
| | ResponseItem::LocalShellCall { .. } |
| | ResponseItem::FunctionCall { .. } |
| | ResponseItem::ToolSearchCall { .. } |
| | ResponseItem::CustomToolCall { .. } |
| | ResponseItem::ToolSearchOutput { .. } |
| | ResponseItem::WebSearchCall { .. } |
| | ResponseItem::ImageGenerationCall { .. } |
| | ResponseItem::Compaction { .. } |
| | ResponseItem::ConfigurationUpdate { .. } |
| | ResponseItem::CompactionTrigger { .. } |
| | ResponseItem::ContextCompaction { .. } |
| | ResponseItem::Other => {} |
| } |
| } |
| } |
|
|
| fn normalize_image_detail(detail: &mut Option<ImageDetail>, model_info: &ModelInfo) { |
| if model_info.use_responses_lite { |
| *detail = None; |
| } else if *detail == Some(ImageDetail::Original) && !model_info.supports_image_detail_original { |
| *detail = Some(DEFAULT_IMAGE_DETAIL); |
| } |
| } |
|
|
| pub struct ResponseStream { |
| pub(crate) rx_event: mpsc::Receiver<Result<ResponseEvent>>, |
| |
| |
| 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; |
|
|