use std::collections::BTreeMap;
use serde::de;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
pub type ExtraFields = BTreeMap<String, Value>;
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ChatCompletionRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub messages: Vec<ChatMessage>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tools: Vec<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_choice: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub temperature: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top_p: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_completion_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ChatMessage {
pub role: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content: Option<OpenAiContent>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_call_id: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tool_calls: Vec<ChatToolCall>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ChatMessageDelta {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub role: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content: Option<OpenAiContent>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tool_calls: Vec<ChatToolCall>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub refusal: Option<String>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum OpenAiContent {
Text(String),
Parts(Vec<OpenAiContentPart>),
Null,
Raw(Value),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct OpenAiContentPart {
#[serde(rename = "type")]
pub kind: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(
default,
deserialize_with = "deserialize_openai_image_url",
skip_serializing_if = "Option::is_none"
)]
pub image_url: Option<OpenAiImageUrl>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input_text: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output_text: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input_image: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub refusal: Option<String>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct OpenAiImageUrl {
pub url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
fn deserialize_openai_image_url<'de, D>(deserializer: D) -> Result<Option<OpenAiImageUrl>, D::Error>
where
D: Deserializer<'de>,
{
let Some(value) = Option::<Value>::deserialize(deserializer)? else {
return Ok(None);
};
match value {
Value::Null => Ok(None),
Value::String(url) => Ok(Some(OpenAiImageUrl {
url,
detail: None,
extra: ExtraFields::new(),
})),
Value::Object(mut object) => {
let url = object
.remove("url")
.or_else(|| object.remove("image_url"))
.and_then(|value| value.as_str().map(ToString::to_string))
.ok_or_else(|| de::Error::custom("image_url object must include url"))?;
let detail = object
.remove("detail")
.and_then(|value| value.as_str().map(ToString::to_string));
Ok(Some(OpenAiImageUrl {
url,
detail,
extra: object.into_iter().collect(),
}))
}
_ => Err(de::Error::custom("image_url must be a string or object")),
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ChatToolCall {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub index: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, rename = "type", skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub function: Option<ChatFunctionCall>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ChatFunctionCall {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub arguments: Option<String>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ChatCompletionResponse {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub choices: Vec<ChatChoice>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<OpenAiUsage>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ChatCompletionChunk {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub choices: Vec<ChatChoice>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<OpenAiUsage>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ChatChoice {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub index: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<ChatMessage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub delta: Option<ChatMessageDelta>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub finish_reason: Option<String>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ResponsesRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input: Option<ResponsesInput>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub instructions: Option<OpenAiContent>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tools: Vec<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_choice: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub temperature: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top_p: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_output_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reasoning: Option<OpenAiReasoning>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ResponsesInput {
Text(String),
Items(Vec<ResponsesItem>),
Null,
Raw(Value),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ResponsesItem {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, rename = "type", skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub role: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content: Option<OpenAiContent>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub call_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub arguments: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output: Option<Value>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
pub type ResponsesOutputItem = ResponsesItem;
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ResponsesResponse {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub output: Vec<ResponsesOutputItem>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<OpenAiUsage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<Value>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct ResponsesStreamEvent {
#[serde(default, rename = "type", skip_serializing_if = "Option::is_none")]
pub kind: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sequence_number: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub item_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output_index: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content_index: Option<usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub delta: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub response: Option<ResponsesResponse>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub item: Option<ResponsesOutputItem>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<Value>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct OpenAiReasoning {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub effort: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub summary: Option<Value>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct OpenAiUsage {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub prompt_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub completion_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "ExtraFields::is_empty", flatten)]
pub extra: ExtraFields,
}