use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use crate::format::FormatId;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Role {
System,
Developer,
User,
Assistant,
Tool,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct InstructionBlock {
pub role: Role,
pub content: Vec<ContentBlock>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Message {
pub role: Role,
pub content: Vec<ContentBlock>,
}
impl Message {
pub fn text(role: Role, text: impl Into<String>) -> Self {
Self {
role,
content: vec![ContentBlock::Text { text: text.into() }],
}
}
pub fn text_content(&self, separator: &str) -> Option<String> {
let parts = self
.content
.iter()
.filter_map(|block| match block {
ContentBlock::Text { text } => Some(text.as_str()),
ContentBlock::Refusal { text } => Some(text.as_str()),
_ => None,
})
.collect::<Vec<_>>();
if parts.is_empty() {
None
} else {
Some(parts.join(separator))
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ContentBlock {
Text {
text: String,
},
Reasoning {
text: String,
signature: Option<String>,
},
Image {
source: ImageSource,
},
Audio {
source: MediaSource,
},
Video {
source: MediaSource,
},
File {
source: FileSource,
},
ToolCall(ToolCall),
ToolResult(ToolResult),
Refusal {
text: String,
},
Unknown {
provider: FormatId,
raw: Value,
},
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ImageSource {
Url {
url: String,
detail: Option<String>,
},
Base64 {
media_type: Option<String>,
data: String,
},
Raw(Value),
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum FileSource {
FileId(String),
FileData {
data: String,
filename: Option<String>,
},
Raw(Value),
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum MediaSource {
Url {
url: String,
media_type: Option<String>,
},
Base64 {
media_type: Option<String>,
data: String,
},
Raw(Value),
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ToolCall {
pub id: String,
pub name: String,
pub arguments: Value,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ToolResult {
pub tool_call_id: String,
pub content: Vec<ContentBlock>,
pub is_error: Option<bool>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ToolDefinition {
pub name: String,
pub description: Option<String>,
pub parameters: Value,
pub strict: Option<bool>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ToolChoice {
Auto,
Required,
None,
Tool { name: String },
Raw(Value),
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct SamplingParams {
pub temperature: Option<f64>,
pub top_p: Option<f64>,
pub top_k: Option<i64>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct OutputParams {
pub max_output_tokens: Option<u64>,
pub response_format: Option<Value>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct ReasoningParams {
pub effort: Option<String>,
pub raw: Option<Value>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct ProviderExtensions {
pub fields: Map<String, Value>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct PreservationMetadata {
pub requests: BTreeMap<FormatId, Value>,
pub responses: BTreeMap<FormatId, Value>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct LlmRequest {
pub model: Option<String>,
pub instructions: Vec<InstructionBlock>,
pub messages: Vec<Message>,
pub tools: Vec<ToolDefinition>,
pub tool_choice: Option<ToolChoice>,
pub sampling: SamplingParams,
pub output: OutputParams,
pub reasoning: ReasoningParams,
pub stream: bool,
pub extensions: ProviderExtensions,
pub preservation: PreservationMetadata,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Usage {
pub input_tokens: Option<u64>,
pub output_tokens: Option<u64>,
pub total_tokens: Option<u64>,
pub reasoning_tokens: Option<u64>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum LlmStreamEvent {
MessageStart {
id: Option<String>,
model: Option<String>,
},
TextDelta {
index: usize,
text: String,
},
ReasoningDelta {
index: usize,
text: String,
},
ToolCallDelta {
index: usize,
id: Option<String>,
name: Option<String>,
arguments_delta: Option<String>,
},
Usage(Usage),
MessageStop {
reason: Option<String>,
},
Error {
message: String,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum StopReason {
EndTurn,
MaxTokens,
ToolUse,
ContentFilter,
Error,
Unknown,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ResponseOutput {
pub role: Role,
pub content: Vec<ContentBlock>,
pub stop_reason: Option<StopReason>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct LlmResponse {
pub id: Option<String>,
pub model: Option<String>,
pub outputs: Vec<ResponseOutput>,
pub usage: Usage,
pub extensions: ProviderExtensions,
pub preservation: PreservationMetadata,
}
impl LlmResponse {
pub fn first_output(&self) -> Option<&ResponseOutput> {
self.outputs.first()
}
}