use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub use crate::shared::{
CompletionTokensDetails, FinishReason, PromptTokensDetails, ReasoningEffort, Role,
SearchContextSize, ServiceTier, Usage,
};
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ChatCompletionRequest {
pub model: String,
pub messages: Vec<ChatCompletionMessageParam>,
#[serde(skip_serializing_if = "Option::is_none")]
pub frequency_penalty: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logit_bias: Option<HashMap<String, i32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub logprobs: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_logprobs: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_completion_tokens: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub n: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub presence_penalty: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<ResponseFormat>,
#[serde(skip_serializing_if = "Option::is_none")]
pub seed: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub service_tier: Option<ServiceTier>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop: Option<Stop>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream_options: Option<StreamOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_p: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<Tool>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_choice: Option<ToolChoice>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parallel_tool_calls: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub store: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modalities: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reasoning_effort: Option<ReasoningEffort>,
#[serde(skip_serializing_if = "Option::is_none")]
pub verbosity: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audio: Option<ChatCompletionAudioParam>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prediction: Option<PredictionContent>,
#[serde(skip_serializing_if = "Option::is_none")]
pub web_search_options: Option<WebSearchOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_cache_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cache_control: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub provider: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub functions: Option<Vec<FunctionDef>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub function_call: Option<FunctionCallOption>,
}
impl ChatCompletionRequest {
pub fn new(model: impl Into<String>, messages: Vec<ChatCompletionMessageParam>) -> Self {
Self {
model: model.into(),
messages,
frequency_penalty: None,
logit_bias: None,
logprobs: None,
top_logprobs: None,
max_completion_tokens: None,
n: None,
presence_penalty: None,
response_format: None,
seed: None,
service_tier: None,
stop: None,
stream: None,
stream_options: None,
temperature: None,
top_p: None,
tools: None,
tool_choice: None,
parallel_tool_calls: None,
user: None,
store: None,
metadata: None,
modalities: None,
reasoning_effort: None,
verbosity: None,
audio: None,
prediction: None,
web_search_options: None,
prompt_cache_key: None,
cache_control: None,
provider: None,
session_id: None,
max_tokens: None,
functions: None,
function_call: None,
}
}
pub fn model(mut self, model: impl Into<String>) -> Self {
self.model = model.into();
self
}
pub fn messages(mut self, messages: Vec<ChatCompletionMessageParam>) -> Self {
self.messages = messages;
self
}
pub fn temperature(mut self, temperature: f64) -> Self {
self.temperature = Some(temperature);
self
}
pub fn max_completion_tokens(mut self, max: i64) -> Self {
self.max_completion_tokens = Some(max);
self
}
pub fn tools(mut self, tools: Vec<Tool>) -> Self {
self.tools = Some(tools);
self
}
pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
self.tool_choice = Some(choice);
self
}
pub fn response_format(mut self, format: ResponseFormat) -> Self {
self.response_format = Some(format);
self
}
pub fn reasoning_effort(mut self, effort: ReasoningEffort) -> Self {
self.reasoning_effort = Some(effort);
self
}
pub fn prediction(mut self, prediction: PredictionContent) -> Self {
self.prediction = Some(prediction);
self
}
pub fn top_p(mut self, top_p: f64) -> Self {
self.top_p = Some(top_p);
self
}
pub fn seed(mut self, seed: i64) -> Self {
self.seed = Some(seed);
self
}
pub fn stop(mut self, stop: Stop) -> Self {
self.stop = Some(stop);
self
}
pub fn user(mut self, user: impl Into<String>) -> Self {
self.user = Some(user.into());
self
}
pub fn store(mut self, store: bool) -> Self {
self.store = Some(store);
self
}
pub fn n(mut self, n: i32) -> Self {
self.n = Some(n);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
#[serde(untagged)]
#[non_exhaustive]
pub enum Stop {
Single(String),
Multiple(Vec<String>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct StreamOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub include_usage: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ChatCompletionAudioParam {
pub format: String,
pub voice: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct PredictionContent {
#[serde(rename = "type")]
pub type_: String,
pub content: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct WebSearchOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub search_context_size: Option<SearchContextSize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_location: Option<WebSearchUserLocation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct WebSearchUserLocation {
#[serde(rename = "type")]
pub type_: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub approximate: Option<ApproximateLocation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ApproximateLocation {
#[serde(skip_serializing_if = "Option::is_none")]
pub city: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub region: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timezone: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
#[serde(tag = "type")]
#[non_exhaustive]
pub enum ResponseFormat {
#[serde(rename = "text")]
Text,
#[serde(rename = "json_object")]
JsonObject,
#[serde(rename = "json_schema")]
JsonSchema { json_schema: JsonSchema },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct JsonSchema {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schema: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub strict: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
#[serde(tag = "role")]
#[non_exhaustive]
pub enum ChatCompletionMessageParam {
#[serde(rename = "system")]
System {
content: String,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
},
#[serde(rename = "developer")]
Developer {
content: String,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
},
#[serde(rename = "user")]
User {
content: UserContent,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
},
#[serde(rename = "assistant")]
Assistant {
#[serde(skip_serializing_if = "Option::is_none")]
content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
tool_calls: Option<Vec<ToolCall>>,
#[serde(skip_serializing_if = "Option::is_none")]
refusal: Option<String>,
},
#[serde(rename = "tool")]
Tool {
content: String,
tool_call_id: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
#[serde(untagged)]
#[non_exhaustive]
pub enum UserContent {
Text(String),
Parts(Vec<ContentPart>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
#[serde(tag = "type")]
#[non_exhaustive]
pub enum ContentPart {
#[serde(rename = "text")]
Text { text: String },
#[serde(rename = "image_url")]
ImageUrl { image_url: ImageUrl },
#[serde(rename = "input_audio")]
InputAudio { input_audio: InputAudio },
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum ImageDetail {
Auto,
Low,
High,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ImageUrl {
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<ImageDetail>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct InputAudio {
pub data: String,
pub format: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct Tool {
#[serde(rename = "type")]
pub type_: String,
pub function: FunctionDef,
}
fn strip_format_recursive(value: &mut serde_json::Value) {
if let Some(map) = value.as_object_mut() {
map.remove("format");
if map.get("minimum") == Some(&serde_json::json!(0.0)) {
map.remove("minimum");
}
for v in map.values_mut() {
strip_format_recursive(v);
}
} else if let Some(arr) = value.as_array_mut() {
for v in arr {
strip_format_recursive(v);
}
}
}
impl Tool {
pub fn function(
name: impl Into<String>,
description: impl Into<String>,
mut parameters: serde_json::Value,
) -> Self {
strip_format_recursive(&mut parameters);
Self {
type_: "function".to_string(),
function: FunctionDef {
name: name.into(),
description: Some(description.into()),
parameters: Some(parameters),
strict: Some(true),
},
}
}
pub fn web_search() -> Self {
Self {
type_: "web_search".to_string(),
function: FunctionDef {
name: "".to_string(),
description: None,
parameters: None,
strict: None,
},
}
}
pub fn file_search() -> Self {
Self {
type_: "file_search".to_string(),
function: FunctionDef {
name: "".to_string(),
description: None,
parameters: None,
strict: None,
},
}
}
pub fn code_interpreter() -> Self {
Self {
type_: "code_interpreter".to_string(),
function: FunctionDef {
name: "".to_string(),
description: None,
parameters: None,
strict: None,
},
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct FunctionDef {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub strict: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
#[serde(untagged)]
#[non_exhaustive]
pub enum FunctionCallOption {
Mode(String),
Named { name: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
#[serde(untagged)]
#[non_exhaustive]
pub enum ToolChoice {
Mode(String),
Named {
#[serde(rename = "type")]
type_: String,
function: ToolChoiceFunction,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ToolChoiceFunction {
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ToolCall {
pub id: String,
#[serde(rename = "type")]
pub type_: String,
pub function: FunctionCall,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct FunctionCall {
pub name: String,
pub arguments: String,
}
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ChatCompletionResponse {
pub id: String,
pub choices: Vec<ChatCompletionChoice>,
pub created: i64,
pub model: String,
pub object: String,
#[serde(default)]
pub service_tier: Option<ServiceTier>,
#[serde(default)]
pub system_fingerprint: Option<String>,
#[serde(default)]
pub usage: Option<Usage>,
#[serde(default)]
pub session_id: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ChatCompletionChoice {
pub finish_reason: FinishReason,
pub index: i32,
pub message: ChatCompletionMessage,
#[serde(default)]
pub logprobs: Option<ChoiceLogprobs>,
}
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ChatCompletionMessage {
pub role: Role,
#[serde(default)]
pub content: Option<String>,
#[serde(default)]
pub refusal: Option<String>,
#[serde(default)]
pub tool_calls: Option<Vec<ToolCall>>,
#[serde(default)]
pub annotations: Option<Vec<Annotation>>,
}
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ChoiceLogprobs {
#[serde(default)]
pub content: Option<Vec<TokenLogprob>>,
#[serde(default)]
pub refusal: Option<Vec<TokenLogprob>>,
}
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct TokenLogprob {
pub token: String,
pub logprob: f64,
#[serde(default)]
pub bytes: Option<Vec<u8>>,
#[serde(default)]
pub top_logprobs: Option<Vec<TopLogprob>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct TopLogprob {
pub token: String,
pub logprob: f64,
#[serde(default)]
pub bytes: Option<Vec<u8>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct Annotation {
#[serde(rename = "type")]
pub type_: String,
#[serde(default)]
pub url_citation: Option<UrlCitation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct UrlCitation {
pub end_index: i32,
pub start_index: i32,
pub title: String,
pub url: String,
}
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ChatCompletionChunk {
pub id: String,
pub choices: Vec<ChunkChoice>,
pub created: i64,
pub model: String,
pub object: String,
#[serde(default)]
pub service_tier: Option<ServiceTier>,
#[serde(default)]
pub system_fingerprint: Option<String>,
#[serde(default)]
pub usage: Option<Usage>,
#[serde(default)]
pub session_id: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ChunkChoice {
pub delta: ChoiceDelta,
pub finish_reason: Option<FinishReason>,
pub index: i32,
#[serde(default)]
pub logprobs: Option<ChoiceLogprobs>,
}
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct ChoiceDelta {
#[serde(default)]
pub content: Option<String>,
#[serde(default)]
pub role: Option<Role>,
#[serde(default)]
pub refusal: Option<String>,
#[serde(default)]
pub tool_calls: Option<Vec<DeltaToolCall>>,
}
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct DeltaToolCall {
pub index: i32,
#[serde(default)]
pub id: Option<String>,
#[serde(default)]
pub function: Option<DeltaFunctionCall>,
#[serde(default, rename = "type")]
pub type_: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(feature = "structured", derive(schemars::JsonSchema))]
pub struct DeltaFunctionCall {
#[serde(default)]
pub arguments: Option<String>,
#[serde(default)]
pub name: Option<String>,
}