use serde::{Deserialize, Serialize};
use serde_json::Value;
pub use crate::compact::{CompactContent, CompactContentPart, CompactOutputItem};
use crate::openresponses_types::{self as types};
#[derive(Debug, Clone, Serialize)]
pub(crate) struct ResponsesRequest {
pub(crate) model: String,
pub(crate) input: Vec<ResponsesInputItem>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) instructions: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) previous_response_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) temperature: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) max_output_tokens: Option<u32>,
pub(crate) stream: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) tools: Option<Vec<ResponsesTool>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) reasoning: Option<ResponsesReasoning>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) metadata: Option<std::collections::HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) prompt_cache_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) parallel_tool_calls: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) service_tier: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) text: Option<ResponsesText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) include: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize)]
pub(crate) struct ResponsesText {
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) verbosity: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub(crate) struct ResponsesReasoning {
pub(crate) effort: String,
pub(crate) summary: String,
}
#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
pub(crate) enum ResponsesInputItem {
ConfigurationUpdate {
r#type: String,
reasoning: crate::compact::ConfigurationReasoning,
},
ProviderItem(Value),
Message {
r#type: String,
role: String,
content: ResponsesContent,
#[serde(skip_serializing_if = "Option::is_none")]
phase: Option<String>,
},
FunctionCall {
r#type: String,
call_id: String,
name: String,
arguments: String,
},
FunctionCallOutput {
r#type: String,
call_id: String,
output: String,
},
Reasoning {
r#type: String,
id: String,
encrypted_content: String,
summary: Vec<types::ContentPart>,
},
Compaction {
r#type: String,
encrypted_content: String,
},
}
impl From<&CompactOutputItem> for ResponsesInputItem {
fn from(item: &CompactOutputItem) -> Self {
match item {
CompactOutputItem::ProviderItem(item) => Self::ProviderItem(item.clone()),
CompactOutputItem::Message { role, content } => Self::Message {
r#type: "message".to_string(),
role: role.clone(),
content: match content {
CompactContent::Text(text) => ResponsesContent::Text(text.clone()),
CompactContent::Parts(parts) => ResponsesContent::Parts(
parts
.iter()
.map(|part| match part {
CompactContentPart::InputText { text } => {
ResponsesContentPart::InputText {
r#type: "input_text".to_string(),
text: text.clone(),
}
}
CompactContentPart::InputImage { image_url } => {
ResponsesContentPart::InputImage {
r#type: "input_image".to_string(),
image_url: image_url.clone(),
}
}
CompactContentPart::InputFile {
file_data,
filename,
} => ResponsesContentPart::InputFile {
r#type: "input_file".to_string(),
input_file: ResponsesInputFile {
file_data: Some(file_data.clone()),
file_url: None,
filename: filename.clone(),
},
},
})
.collect(),
),
},
phase: None,
},
CompactOutputItem::Compaction { encrypted_content } => Self::Compaction {
r#type: "compaction".to_string(),
encrypted_content: encrypted_content.clone(),
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum ResponsesContent {
Text(String),
Parts(Vec<ResponsesContentPart>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
#[allow(clippy::enum_variant_names)]
pub(crate) enum ResponsesContentPart {
InputText {
r#type: String,
text: String,
},
InputImage {
r#type: String,
image_url: String,
},
InputAudio {
r#type: String,
input_audio: ResponsesInputAudio,
},
InputFile {
r#type: String,
input_file: ResponsesInputFile,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ResponsesInputAudio {
pub(crate) data: String,
pub(crate) format: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct ResponsesInputFile {
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) file_data: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) file_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) filename: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
pub(crate) enum ResponsesTool {
Function {
r#type: String,
name: String,
description: String,
parameters: Value,
#[serde(skip_serializing_if = "Option::is_none")]
strict: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
defer_loading: Option<bool>,
},
Namespace {
r#type: String,
name: String,
description: String,
tools: Vec<ResponsesTool>,
},
ToolSearch { r#type: String },
}