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 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    /// Stream termination. `finish_reason` is the provider's stop reason:
27    /// - `"stop"`: natural completion
28    /// - `"length"`: hit token limit (tool call args may be truncated)
29    /// - `"tool_calls"`: model requested tool calls
30    /// - `None`: synthetic stop (e.g. `[DONE]` sentinel, stream closed)
31    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/// Reasoning/thinking configuration, unifying reasoning/thinking parameters across vendors.
54#[derive(Debug, Clone, Default)]
55pub struct ReasoningConfig {
56    /// Whether to enable reasoning/thinking process
57    pub enabled: Option<bool>,
58    /// Thinking budget (token count limit)
59    pub budget_tokens: Option<u64>,
60    /// Reasoning intensity/depth (semantics vary by vendor)
61    pub effort: Option<ReasoningEffort>,
62}
63
64/// Reasoning intensity/depth enumeration.
65#[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    /// The model name used by this client (e.g. "claude-sonnet", "gpt-4o").
96    /// Default: "unknown".
97    fn model_name(&self) -> &str {
98        "unknown"
99    }
100}