use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use tokio::sync::mpsc;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LlmTool {
pub name: String,
pub description: String,
pub parameters: JsonValue,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCall {
pub id: String,
pub name: String,
pub arguments: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolResult {
pub call_id: String,
pub content: String,
pub is_error: bool,
}
#[derive(Debug, Clone)]
pub enum LlmStreamEvent {
TextDelta(String),
ToolUse(ToolCall),
Usage(LlmUsage),
Done(String),
Error(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LlmMessage {
pub role: String,
pub content: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<ToolCall>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_call_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LlmRequest {
pub model: String,
pub system_prompt: Option<String>,
pub messages: Vec<LlmMessage>,
pub temperature: Option<f64>,
pub max_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<LlmTool>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LlmUsage {
pub prompt_tokens: u32,
pub completion_tokens: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LlmResponse {
pub content: String,
pub usage: LlmUsage,
pub finish_reason: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_calls: Option<Vec<ToolCall>>,
}
#[async_trait]
pub trait LlmProvider: Send + Sync {
async fn complete(&self, request: LlmRequest) -> Result<LlmResponse>;
}
#[async_trait]
pub trait StreamingLlmProvider: LlmProvider {
async fn stream(&self, request: LlmRequest, tx: mpsc::Sender<LlmStreamEvent>) -> Result<()>;
}