1use async_trait::async_trait;
2use futures_core::Stream;
3use serde_json::Value;
4use std::pin::Pin;
5
6use crate::types::{AgentResult, ChatMessage, ResponseFormat};
7
8mod anthropic;
9mod openai;
10mod registry;
11
12pub use anthropic::AnthropicClient;
13pub use openai::{LlmClientConfig, OpenAiClient};
14pub use registry::{LlmClientBuilder, LlmProvider};
15
16#[derive(Clone, Debug)]
17pub enum StreamChunk {
18 Text(String),
19 Thought(String),
20 ToolCall(Value),
21 Usage(UsageInfo),
22 Stop,
23}
24
25#[derive(Clone, Debug, Default)]
26pub struct UsageInfo {
27 pub prompt_tokens: Option<u32>,
28 pub completion_tokens: Option<u32>,
29 pub total_tokens: Option<u32>,
30}
31
32#[derive(Clone, Debug, Default)]
33pub struct LlmCapabilities {
34 pub supports_streaming: bool,
35 pub supports_tools: bool,
36 pub supports_vision: bool,
37 pub supports_thinking: bool,
38 pub max_context_tokens: Option<u32>,
39 pub max_output_tokens: Option<u32>,
40}
41
42#[derive(Debug, Clone, Default)]
44pub struct ReasoningConfig {
45 pub enabled: Option<bool>,
47 pub budget_tokens: Option<u64>,
49 pub effort: Option<ReasoningEffort>,
51}
52
53#[derive(Debug, Clone, Default)]
55pub enum ReasoningEffort {
56 #[default]
57 None,
58 Low,
59 Medium,
60 High,
61 XHigh,
62}
63
64#[async_trait]
65pub trait LlmClient: Send + Sync {
66 async fn chat(
67 &self,
68 messages: &[ChatMessage],
69 tools: &[Value],
70 reasoning: Option<&ReasoningConfig>,
71 response_format: Option<&ResponseFormat>,
72 ) -> AgentResult<Value>;
73
74 async fn chat_stream(
75 &self,
76 messages: &[ChatMessage],
77 tools: &[Value],
78 reasoning: Option<&ReasoningConfig>,
79 response_format: Option<&ResponseFormat>,
80 ) -> AgentResult<Pin<Box<dyn Stream<Item = AgentResult<StreamChunk>> + Send>>>;
81
82 fn capabilities(&self) -> LlmCapabilities;
83
84 fn model_name(&self) -> &str {
87 "unknown"
88 }
89}