Skip to main content

agent_base/llm/
mod.rs

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;
11mod stream_client;
12
13pub use anthropic::AnthropicClient;
14pub use openai::{LlmClientConfig, OpenAiClient};
15pub use registry::{LlmClientBuilder, LlmProvider};
16pub use stream_client::{LlmClientAdapter, StreamClient, adapt};
17
18#[derive(Clone, Debug)]
19pub enum StreamChunk {
20    Text(String),
21    Thought(String),
22    ToolCall(Value),
23    Usage(UsageInfo),
24    /// Stream termination. `finish_reason` is the provider's stop reason:
25    /// - `"stop"`: natural completion
26    /// - `"length"`: hit token limit (tool call args may be truncated)
27    /// - `"tool_calls"`: model requested tool calls
28    /// - `None`: synthetic stop (e.g. `[DONE]` sentinel, stream closed)
29    Stop {
30        finish_reason: Option<String>,
31    },
32}
33
34#[derive(Clone, Debug, Default)]
35pub struct UsageInfo {
36    pub prompt_tokens: Option<u32>,
37    pub completion_tokens: Option<u32>,
38    pub total_tokens: Option<u32>,
39}
40
41#[derive(Clone, Debug, Default)]
42pub struct LlmCapabilities {
43    pub supports_streaming: bool,
44    pub supports_tools: bool,
45    pub supports_vision: bool,
46    pub supports_thinking: bool,
47    pub max_context_tokens: Option<u32>,
48    pub max_output_tokens: Option<u32>,
49}
50
51/// Reasoning/thinking configuration, unifying reasoning/thinking parameters across vendors.
52#[derive(Debug, Clone, Default)]
53pub struct ReasoningConfig {
54    /// Whether to enable reasoning/thinking process
55    pub enabled: Option<bool>,
56    /// Thinking budget (token count limit)
57    pub budget_tokens: Option<u64>,
58    /// Reasoning intensity/depth (semantics vary by vendor)
59    pub effort: Option<ReasoningEffort>,
60}
61
62/// Reasoning intensity/depth enumeration.
63#[derive(Debug, Clone, Default)]
64pub enum ReasoningEffort {
65    #[default]
66    None,
67    Low,
68    Medium,
69    High,
70    XHigh,
71}
72
73#[async_trait]
74pub trait LlmClient: Send + Sync {
75    async fn chat(
76        &self,
77        messages: &[ChatMessage],
78        tools: &[Value],
79        reasoning: Option<&ReasoningConfig>,
80        response_format: Option<&ResponseFormat>,
81    ) -> AgentResult<Value>;
82
83    async fn chat_stream(
84        &self,
85        messages: &[ChatMessage],
86        tools: &[Value],
87        reasoning: Option<&ReasoningConfig>,
88        response_format: Option<&ResponseFormat>,
89    ) -> AgentResult<Pin<Box<dyn Stream<Item = AgentResult<StreamChunk>> + Send>>>;
90
91    fn capabilities(&self) -> LlmCapabilities;
92
93    /// The model name used by this client (e.g. "claude-sonnet", "gpt-4o").
94    /// Default: "unknown".
95    fn model_name(&self) -> &str {
96        "unknown"
97    }
98}