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 openai_responses;
11mod registry;
12mod stream_client;
13
14pub use anthropic::AnthropicClient;
15pub use openai::{LlmClientConfig, OpenAiClient};
16pub use openai_responses::OpenAiResponsesClient;
17pub use registry::{LlmClientBuilder, LlmProvider};
18pub use stream_client::{LlmClientAdapter, StreamClient, adapt};
19
20#[derive(Clone, Debug)]
21pub enum StreamChunk {
22 Text(String),
23 Thought(String),
24 ToolCall(Value),
25 Usage(UsageInfo),
26 Stop {
32 finish_reason: Option<String>,
33 },
34}
35
36#[derive(Clone, Debug, Default)]
37pub struct UsageInfo {
38 pub prompt_tokens: Option<u32>,
39 pub completion_tokens: Option<u32>,
40 pub total_tokens: Option<u32>,
41}
42
43#[derive(Clone, Debug, Default)]
44pub struct LlmCapabilities {
45 pub supports_streaming: bool,
46 pub supports_tools: bool,
47 pub supports_vision: bool,
48 pub supports_thinking: bool,
49 pub max_context_tokens: Option<u32>,
50 pub max_output_tokens: Option<u32>,
51}
52
53#[derive(Debug, Clone, Default)]
55pub struct ReasoningConfig {
56 pub enabled: Option<bool>,
58 pub budget_tokens: Option<u64>,
60 pub effort: Option<ReasoningEffort>,
62}
63
64#[derive(Debug, Clone, Default)]
66pub enum ReasoningEffort {
67 #[default]
68 None,
69 Low,
70 Medium,
71 High,
72 XHigh,
73}
74
75#[async_trait]
76pub trait LlmClient: Send + Sync {
77 async fn chat(
78 &self,
79 messages: &[ChatMessage],
80 tools: &[Value],
81 reasoning: Option<&ReasoningConfig>,
82 response_format: Option<&ResponseFormat>,
83 ) -> AgentResult<Value>;
84
85 async fn chat_stream(
86 &self,
87 messages: &[ChatMessage],
88 tools: &[Value],
89 reasoning: Option<&ReasoningConfig>,
90 response_format: Option<&ResponseFormat>,
91 ) -> AgentResult<Pin<Box<dyn Stream<Item = AgentResult<StreamChunk>> + Send>>>;
92
93 fn capabilities(&self) -> LlmCapabilities;
94
95 fn model_name(&self) -> &str {
98 "unknown"
99 }
100}