use serde::{Deserialize, Serialize};
use serde_json::Value;
use thiserror::Error;
use uuid::Uuid;
use crate::{ToolCall, ToolDefinition, ToolResult};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ProviderCapabilities {
pub streaming: bool,
pub tools: bool,
pub structured_output: bool,
pub multimodal_input: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub context_tokens: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ConversationItem {
UserText {
text: String,
},
AssistantText {
text: String,
},
ToolCall {
call: ToolCall,
},
ToolResult {
result: ToolResult,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProviderRequest {
pub session_id: Uuid,
pub model: String,
pub instructions: String,
pub items: Vec<ConversationItem>,
#[serde(default)]
pub tools: Vec<ToolDefinition>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output_schema: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub previous_response_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProviderTurn {
pub provider: String,
pub model: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_id: Option<String>,
#[serde(default)]
pub output_text: String,
#[serde(default)]
pub tool_calls: Vec<ToolCall>,
pub stop_reason: StopReason,
#[serde(default)]
pub usage: TokenUsage,
#[serde(default)]
pub opaque: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum ProviderStreamEvent {
TextDelta {
delta: String,
#[serde(default)]
opaque: Value,
},
ToolCallStarted {
id: String,
name: String,
#[serde(default)]
opaque: Value,
},
ToolCallArgumentsDelta {
id: String,
delta: String,
#[serde(default)]
opaque: Value,
},
ToolCallCompleted {
call: ToolCall,
#[serde(default)]
opaque: Value,
},
Usage {
usage: TokenUsage,
#[serde(default)]
opaque: Value,
},
Completed {
turn: ProviderTurn,
},
Opaque {
event_type: String,
payload: Value,
},
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum StopReason {
EndTurn,
ToolUse,
MaxTokens,
Refusal,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct TokenUsage {
pub input_tokens: u64,
pub output_tokens: u64,
pub cached_tokens: u64,
pub reasoning_tokens: u64,
}
#[derive(Debug, Error)]
pub enum ProviderError {
#[error("provider configuration error: {0}")]
Configuration(String),
#[error("provider does not support required capability: {0}")]
Unsupported(String),
#[error("provider transport failed: {0}")]
Transport(String),
#[error("provider API error: {0}")]
Api(String),
#[error("provider response was invalid: {0}")]
InvalidResponse(String),
#[error("provider turn cancelled")]
Cancelled,
}