use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MessagesRequest {
pub model: String,
#[serde(default)]
pub messages: Vec<Message>,
#[serde(default)]
pub system: Option<SystemPrompt>,
#[serde(default)]
pub max_tokens: Option<u32>,
#[serde(default)]
pub stream: Option<bool>,
#[serde(default)]
pub temperature: Option<f64>,
#[serde(default)]
pub top_p: Option<f64>,
#[serde(default)]
pub tools: Option<Vec<ToolDefinition>>,
#[serde(default)]
pub tool_choice: Option<Value>,
#[serde(default)]
pub stop_sequences: Option<Vec<String>>,
#[serde(default)]
pub thinking: Option<Value>,
#[serde(default)]
pub output_config: Option<Value>,
#[serde(default)]
pub service_tier: Option<String>,
#[serde(default)]
pub speed: Option<String>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
const CODEX_PRIORITY_SERVICE_TIER: &str = "priority";
impl MessagesRequest {
#[must_use]
pub fn wants_stream(&self) -> bool {
self.stream.unwrap_or(false)
}
#[must_use]
pub(crate) fn upstream_service_tier(&self) -> Option<String> {
if self
.speed
.as_deref()
.map(str::trim)
.is_some_and(|speed| speed.eq_ignore_ascii_case("fast"))
{
return Some(CODEX_PRIORITY_SERVICE_TIER.to_owned());
}
let tier = self.service_tier.as_deref()?.trim();
if tier.eq_ignore_ascii_case("auto")
|| tier.eq_ignore_ascii_case("priority")
|| tier.eq_ignore_ascii_case("fast")
{
Some(CODEX_PRIORITY_SERVICE_TIER.to_owned())
} else {
None
}
}
#[must_use]
pub(crate) fn output_effort(&self) -> Option<String> {
self.output_config
.as_ref()
.and_then(|config| config.get("effort"))
.and_then(Value::as_str)
.and_then(normalize_anthropic_effort)
}
}
pub fn normalize_anthropic_effort(value: &str) -> Option<String> {
let normalized = value.trim().to_ascii_lowercase().replace(['-', ' '], "_");
match normalized.as_str() {
"low" | "medium" | "high" | "xhigh" => Some(normalized),
"extra_high" | "extra" | "max" => Some("xhigh".to_owned()),
"minimal" | "min" => Some("low".to_owned()),
_ => None,
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Message {
pub role: String,
pub content: MessageContent,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum MessageContent {
Text(String),
Blocks(Vec<ContentBlock>),
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum SystemPrompt {
Text(String),
Blocks(Vec<SystemBlock>),
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct SystemBlock {
#[serde(rename = "type")]
pub kind: String,
#[serde(default)]
pub text: Option<String>,
#[serde(default)]
pub cache_control: Option<Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ContentBlock {
#[serde(rename = "type")]
pub kind: String,
#[serde(default)]
pub text: Option<String>,
#[serde(default)]
pub cache_control: Option<Value>,
#[serde(default)]
pub source: Option<ImageSource>,
#[serde(default)]
pub document_content: Option<Vec<Self>>,
#[serde(default)]
pub id: Option<String>,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub input: Option<Value>,
#[serde(default)]
pub tool_use_id: Option<String>,
#[serde(default)]
pub content: Option<ToolResultContent>,
#[serde(default)]
pub is_error: Option<bool>,
#[serde(default)]
pub thinking: Option<String>,
#[serde(default)]
pub signature: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct ImageSource {
#[serde(rename = "type")]
pub kind: String,
#[serde(default)]
pub media_type: Option<String>,
#[serde(default)]
pub data: Option<String>,
#[serde(default)]
pub text: Option<String>,
#[serde(default)]
pub url: Option<String>,
#[serde(default)]
pub file_id: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
pub enum ToolResultContent {
Text(String),
Blocks(Vec<ContentBlock>),
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ToolDefinition {
pub name: String,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub input_schema: Option<Value>,
#[serde(default)]
pub cache_control: Option<Value>,
}
#[derive(Debug, Clone, Serialize)]
pub struct MessageResponse {
pub id: String,
#[serde(rename = "type")]
pub kind: &'static str,
pub role: &'static str,
pub model: String,
pub content: Vec<ResponseContentBlock>,
pub stop_reason: &'static str,
pub stop_sequence: Option<String>,
pub usage: ResponseUsage,
}
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type")]
pub enum ResponseContentBlock {
#[serde(rename = "text")]
Text {
text: String,
},
#[serde(rename = "tool_use")]
ToolUse {
id: String,
name: String,
input: Value,
},
#[serde(rename = "image")]
Image {
source: ImageSource,
},
#[serde(rename = "thinking")]
Thinking {
thinking: String,
#[serde(skip_serializing_if = "Option::is_none")]
signature: Option<String>,
},
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct ResponseUsage {
pub input_tokens: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub cache_creation_input_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cache_read_input_tokens: Option<u32>,
pub output_tokens: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub server_tool_use: Option<Value>,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct CountTokensResponse {
pub input_tokens: u32,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct ModelsResponse {
pub data: Vec<ModelInfo>,
pub first_id: Option<String>,
pub has_more: bool,
pub last_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct ModelInfo {
pub created_at: String,
pub display_name: String,
pub id: String,
#[serde(rename = "type")]
pub kind: &'static str,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MessageBatchCreateRequest {
pub requests: Vec<MessageBatchRequest>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct MessageBatchRequest {
pub custom_id: String,
pub params: MessagesRequest,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct MessageBatch {
pub archived_at: Option<String>,
pub cancel_initiated_at: Option<String>,
pub created_at: String,
pub ended_at: Option<String>,
pub expires_at: String,
pub id: String,
pub processing_status: &'static str,
pub request_counts: MessageBatchRequestCounts,
pub results_url: Option<String>,
#[serde(rename = "type")]
pub kind: &'static str,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct MessageBatchListResponse {
pub data: Vec<MessageBatch>,
pub first_id: Option<String>,
pub has_more: bool,
pub last_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct MessageBatchRequestCounts {
pub canceled: u32,
pub errored: u32,
pub expired: u32,
pub processing: u32,
pub succeeded: u32,
}
#[derive(Debug, Clone, Serialize)]
pub struct MessageBatchResult {
pub custom_id: String,
pub result: MessageBatchResultType,
}
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type")]
pub enum MessageBatchResultType {
#[serde(rename = "succeeded")]
Succeeded {
message: MessageResponse,
},
#[serde(rename = "errored")]
Errored {
error: Value,
},
#[serde(rename = "canceled")]
Canceled,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
pub struct MessageBatchDeleted {
pub id: String,
#[serde(rename = "type")]
pub kind: &'static str,
}
#[derive(Debug, Clone, Serialize)]
pub struct MessageStartEvent {
#[serde(rename = "type")]
pub kind: &'static str,
pub message: StreamingMessage,
}
#[derive(Debug, Clone, Serialize)]
pub struct StreamingMessage {
pub id: String,
#[serde(rename = "type")]
pub kind: &'static str,
pub role: &'static str,
pub content: Vec<Value>,
pub model: String,
pub stop_reason: Option<String>,
pub stop_sequence: Option<String>,
pub usage: ResponseUsage,
}