use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Usage {
#[serde(default)]
pub prompt_tokens: u32,
#[serde(default)]
pub completion_tokens: u32,
#[serde(default)]
pub total_tokens: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cost_usd: Option<f64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Role {
System,
User,
Assistant,
Tool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FinishReason {
Stop,
Length,
ToolCalls,
ContentFilter,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BatchStatus {
Validating,
InProgress,
Finalizing,
Completed,
Failed,
Expired,
Canceling,
Canceled,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ImageUrl {
pub url: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentPart {
Text { text: String },
ImageUrl { image_url: ImageUrl },
}
impl ContentPart {
pub fn text(text: impl Into<String>) -> Self {
ContentPart::Text { text: text.into() }
}
pub fn image_url(url: impl Into<String>) -> Self {
ContentPart::ImageUrl {
image_url: ImageUrl {
url: url.into(),
detail: None,
},
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Content {
Text(String),
Parts(Vec<ContentPart>),
}
impl From<String> for Content {
fn from(s: String) -> Self {
Content::Text(s)
}
}
impl From<&str> for Content {
fn from(s: &str) -> Self {
Content::Text(s.to_string())
}
}
impl From<Vec<ContentPart>> for Content {
fn from(p: Vec<ContentPart>) -> Self {
Content::Parts(p)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FunctionCall {
pub name: String,
pub arguments: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolCall {
pub id: String,
#[serde(rename = "type")]
pub kind: String,
pub function: FunctionCall,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FunctionCallDelta {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub arguments: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolCallDelta {
pub index: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none", rename = "type")]
pub kind: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub function: Option<FunctionCallDelta>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChatMessage {
pub role: Role,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content: Option<Content>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tool_calls: Vec<ToolCall>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_call_id: Option<String>,
}
impl ChatMessage {
pub fn system(text: impl Into<String>) -> Self {
Self::with_text(Role::System, text)
}
pub fn user(text: impl Into<String>) -> Self {
Self::with_text(Role::User, text)
}
pub fn assistant(text: impl Into<String>) -> Self {
Self::with_text(Role::Assistant, text)
}
fn with_text(role: Role, text: impl Into<String>) -> Self {
ChatMessage {
role,
content: Some(Content::Text(text.into())),
name: None,
tool_calls: Vec::new(),
tool_call_id: None,
}
}
pub fn text(&self) -> Option<&str> {
match &self.content {
Some(Content::Text(s)) => Some(s.as_str()),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FunctionDef {
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parameters: Option<serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ToolDef {
#[serde(rename = "type")]
pub kind: String,
pub function: FunctionDef,
}
impl ToolDef {
pub fn function(function: FunctionDef) -> Self {
ToolDef {
kind: "function".to_string(),
function,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FunctionName {
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NamedToolChoice {
#[serde(rename = "type")]
pub kind: String,
pub function: FunctionName,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ToolChoice {
Mode(String),
Named(NamedToolChoice),
}
impl ToolChoice {
pub fn mode(mode: impl Into<String>) -> Self {
ToolChoice::Mode(mode.into())
}
pub fn named(name: impl Into<String>) -> Self {
ToolChoice::Named(NamedToolChoice {
kind: "function".to_string(),
function: FunctionName { name: name.into() },
})
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ResponseFormat {
#[serde(rename = "type")]
pub kind: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub json_schema: Option<serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Stop {
One(String),
Many(Vec<String>),
}
impl Stop {
pub fn from_vec(mut v: Vec<String>) -> Option<Self> {
match v.len() {
0 => None,
1 => Some(Stop::One(v.pop().unwrap())),
_ => Some(Stop::Many(v)),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct ChatRequest {
pub model: String,
pub messages: Vec<ChatMessage>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top_p: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_completion_tokens: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stop: Option<Stop>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub n: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub seed: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub frequency_penalty: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub presence_penalty: Option<f32>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tools: Vec<ToolDef>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tool_choice: Option<ToolChoice>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub response_format: Option<ResponseFormat>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reasoning_effort: Option<String>,
#[serde(default, flatten, skip_serializing_if = "Option::is_none")]
pub extra: Option<serde_json::Map<String, serde_json::Value>>,
}
impl ChatRequest {
pub fn new(model: impl Into<String>, messages: Vec<ChatMessage>) -> Self {
ChatRequest {
model: model.into(),
messages,
..Default::default()
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Choice {
#[serde(default)]
pub index: u32,
pub message: ChatMessage,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub finish_reason: Option<FinishReason>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChatResponse {
pub id: String,
pub object: String,
pub created: i64,
pub model: String,
pub choices: Vec<Choice>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
}
impl ChatResponse {
pub fn first_text(&self) -> Option<&str> {
self.choices.first().and_then(|c| c.message.text())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Delta {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub role: Option<Role>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tool_calls: Vec<ToolCallDelta>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChunkChoice {
#[serde(default)]
pub index: u32,
pub delta: Delta,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub finish_reason: Option<FinishReason>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChatCompletionChunk {
pub id: String,
pub object: String,
pub created: i64,
pub model: String,
pub choices: Vec<ChunkChoice>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
}
impl ChatCompletionChunk {
pub fn first_delta_text(&self) -> Option<&str> {
self.choices
.first()
.and_then(|c| c.delta.content.as_deref())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum EmbeddingInput {
One(String),
Many(Vec<String>),
}
impl From<String> for EmbeddingInput {
fn from(s: String) -> Self {
EmbeddingInput::One(s)
}
}
impl From<&str> for EmbeddingInput {
fn from(s: &str) -> Self {
EmbeddingInput::One(s.to_string())
}
}
impl From<Vec<String>> for EmbeddingInput {
fn from(v: Vec<String>) -> Self {
EmbeddingInput::Many(v)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EmbeddingRequest {
pub model: String,
pub input: EmbeddingInput,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dimensions: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub encoding_format: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra: Option<serde_json::Value>,
}
impl EmbeddingRequest {
pub fn new(model: impl Into<String>, input: impl Into<EmbeddingInput>) -> Self {
EmbeddingRequest {
model: model.into(),
input: input.into(),
dimensions: None,
encoding_format: None,
extra: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Embedding {
pub object: String,
#[serde(default)]
pub index: u32,
pub embedding: Vec<f32>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EmbeddingResponse {
pub object: String,
pub data: Vec<Embedding>,
pub model: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpeechRequest {
pub model: String,
pub input: String,
pub voice: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub response_format: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub speed: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra: Option<serde_json::Value>,
}
impl SpeechRequest {
pub fn new(
model: impl Into<String>,
input: impl Into<String>,
voice: impl Into<String>,
) -> Self {
SpeechRequest {
model: model.into(),
input: input.into(),
voice: voice.into(),
response_format: None,
speed: None,
extra: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Voice {
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub languages: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct VoicesResponse {
pub model: String,
pub voices: Vec<Voice>,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct TranscriptionRequest {
pub model: String,
pub language: Option<String>,
pub prompt: Option<String>,
pub response_format: Option<String>,
pub temperature: Option<f32>,
}
impl TranscriptionRequest {
pub fn new(model: impl Into<String>) -> Self {
TranscriptionRequest {
model: model.into(),
..Default::default()
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TranscriptionResponse {
pub text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub task: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub duration: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Transcription {
Json(TranscriptionResponse),
Text(String),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Architecture {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub input_modalities: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub output_modalities: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub modality: Option<String>,
#[serde(default)]
pub tokenizer: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub instruct_type: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Pricing {
pub prompt: String,
pub completion: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TopProvider {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_length: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_completion_tokens: Option<u32>,
#[serde(default)]
pub is_moderated: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_thinking_tokens: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ModelEndpoint {
pub provider: String,
pub model: String,
#[serde(default)]
pub down: bool,
pub source: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ModelEntry {
pub id: String,
#[serde(default)]
pub canonical_slug: String,
#[serde(default)]
pub name: String,
#[serde(default)]
pub created: i64,
#[serde(default)]
pub description: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_length: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub architecture: Option<Architecture>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pricing: Option<Pricing>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub top_provider: Option<TopProvider>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub supported_parameters: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub unsupported_parameters: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default_parameters: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub endpoints: Vec<ModelEndpoint>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ListModelsResponse {
#[serde(default)]
pub data: Vec<ModelEntry>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ModelType {
All,
Llm,
Tts,
Stt,
Embedding,
}
impl ModelType {
pub(crate) fn as_str(self) -> &'static str {
match self {
ModelType::All => "all",
ModelType::Llm => "llm",
ModelType::Tts => "tts",
ModelType::Stt => "stt",
ModelType::Embedding => "embedding",
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BatchRequestItem {
pub custom_id: String,
pub body: ChatRequest,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BatchCreateRequest {
pub requests: Vec<BatchRequestItem>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct BatchCounts {
#[serde(default)]
pub total: u64,
#[serde(default)]
pub processing: u64,
#[serde(default)]
pub succeeded: u64,
#[serde(default)]
pub errored: u64,
#[serde(default)]
pub canceled: u64,
#[serde(default)]
pub expired: u64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BatchHandle {
pub id: String,
pub status: BatchStatus,
#[serde(default)]
pub counts: BatchCounts,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_at: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expires_at: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ended_at: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub endpoint: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BatchResponse {
pub status_code: u32,
pub body: ChatResponse,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BatchError {
pub code: String,
pub message: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BatchResultLine {
pub custom_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub response: Option<BatchResponse>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<BatchError>,
}