use async_trait::async_trait;
use futures::stream::BoxStream;
use serde::{Deserialize, Serialize};
use crate::{Message, Result, SafetyClass, ToolCall, ToolResult};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatRequest {
pub model: String,
pub messages: Vec<Message>,
#[serde(default)]
pub tools: Vec<serde_json::Value>,
#[serde(default)]
pub temperature: Option<f32>,
#[serde(default)]
pub max_tokens: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatChunk {
pub delta_text: String,
pub tool_call: Option<ToolCall>,
pub finished: bool,
}
pub type ChatStream = BoxStream<'static, Result<ChatChunk>>;
#[async_trait]
pub trait LlmProvider: Send + Sync {
fn name(&self) -> &str;
async fn chat_stream(&self, req: ChatRequest) -> Result<ChatStream>;
}
#[async_trait]
pub trait MemoryStore: Send + Sync {
async fn diary_append(&self, project: &str, content: &str) -> Result<()>;
async fn hybrid_search(&self, query: &str, k: usize) -> Result<Vec<(String, f32)>>;
}
#[async_trait]
pub trait ToolHandler: Send + Sync {
fn name(&self) -> &str;
fn schema(&self) -> serde_json::Value;
fn classify(&self) -> SafetyClass;
async fn invoke(&self, call: ToolCall) -> Result<ToolResult>;
}
#[async_trait]
pub trait SkillSource: Send + Sync {
async fn list(&self) -> Result<Vec<String>>;
async fn match_active(&self, query: &str, project: Option<&str>) -> Result<Vec<String>>;
}