use async_trait::async_trait;
use serde_json::Value;
use super::ToolCall;
#[derive(Debug, Clone, PartialEq, Default)]
#[allow(dead_code)]
pub enum ResponseMode {
#[default]
Text,
JsonObject,
JsonSchema {
name: String,
schema: Value,
strict: bool,
},
}
#[derive(Debug, Clone, PartialEq, Default)]
#[allow(dead_code)]
pub enum ToolChoiceMode {
#[default]
Auto,
None,
Required,
Specific(String),
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ChatOptions {
pub response_mode: ResponseMode,
pub tool_choice: ToolChoiceMode,
pub max_tokens_override: Option<u32>,
pub reasoning_effort_override: Option<String>,
pub id_slot: Option<u32>,
}
#[async_trait]
pub trait ModelProvider: Send + Sync {
async fn chat(
&self,
model: &str,
messages: &[Value],
tools: &[Value],
) -> anyhow::Result<ProviderResponse>;
async fn chat_with_options(
&self,
model: &str,
messages: &[Value],
tools: &[Value],
_options: &ChatOptions,
) -> anyhow::Result<ProviderResponse> {
self.chat(model, messages, tools).await
}
async fn list_models(&self) -> anyhow::Result<Vec<String>>;
}
#[derive(Debug, Clone, Default)]
pub struct TokenUsage {
pub input_tokens: u32,
pub output_tokens: u32,
pub cached_input_tokens: Option<u32>,
pub cache_creation_input_tokens: Option<u32>,
pub model: String,
}
impl TokenUsage {
pub fn fresh_input_tokens(&self) -> Option<u32> {
self.cached_input_tokens
.map(|cached| self.input_tokens.saturating_sub(cached))
}
}
#[derive(Debug, Clone)]
pub struct ProviderResponse {
pub content: Option<String>,
pub tool_calls: Vec<ToolCall>,
pub usage: Option<TokenUsage>,
pub thinking: Option<String>,
pub response_note: Option<String>,
}
#[derive(Debug, Clone)]
pub struct TokenUsageRecord {
pub model: String,
pub input_tokens: i64,
pub output_tokens: i64,
#[allow(dead_code)] pub cached_input_tokens: Option<i64>,
#[allow(dead_code)] pub cache_creation_input_tokens: Option<i64>,
#[allow(dead_code)] pub call_id: Option<String>,
#[allow(dead_code)] pub created_at: String,
}