a3s_code_core/llm/mod.rs
1//! LLM client abstraction layer
2//!
3//! Provides a unified interface for interacting with LLM providers
4//! (Anthropic Claude, OpenAI, Zhipu AI GLM, and OpenAI-compatible providers).
5
6pub mod anthropic;
7mod error;
8pub mod factory;
9pub mod http;
10pub mod openai;
11pub mod structured;
12mod types;
13pub mod zhipu;
14
15// Re-export public types
16pub use anthropic::AnthropicClient;
17pub(crate) use error::non_retryable_llm_error_message;
18pub use error::NonRetryableLlmError;
19pub use factory::{create_client_with_config, LlmConfig};
20pub use http::{
21 clear_http_metrics_callback, default_http_client, set_http_metrics_callback, HttpClient,
22 HttpMetricsCallback, HttpMetricsRecord, HttpResponse, StreamingHttpResponse,
23};
24pub use openai::OpenAiClient;
25pub use types::*;
26pub use zhipu::ZhipuClient;
27
28use anyhow::Result;
29use async_trait::async_trait;
30use tokio::sync::mpsc;
31use tokio_util::sync::CancellationToken;
32
33/// LLM client trait
34#[async_trait]
35pub trait LlmClient: Send + Sync {
36 /// Derive a provider client bound to one logical agent session.
37 ///
38 /// Stateless providers can keep the default and share the existing client.
39 /// Account-backed providers whose transport uses a live session identity
40 /// should return an independent client so parallel child agents do not
41 /// contend for the parent's active operation.
42 fn fork_for_session(&self, _session_id: &str) -> Option<std::sync::Arc<dyn LlmClient>> {
43 None
44 }
45
46 /// Complete a conversation (non-streaming)
47 async fn complete(
48 &self,
49 messages: &[Message],
50 system: Option<&str>,
51 tools: &[ToolDefinition],
52 ) -> Result<LlmResponse>;
53
54 /// Complete a conversation with streaming
55 /// Returns a receiver for streaming events.
56 /// The cancel_token is checked during the HTTP request; if cancelled, the request is aborted.
57 async fn complete_streaming(
58 &self,
59 messages: &[Message],
60 system: Option<&str>,
61 tools: &[ToolDefinition],
62 cancel_token: CancellationToken,
63 ) -> Result<mpsc::Receiver<StreamEvent>>;
64
65 /// Report the strongest provider-native structured-output enforcement this
66 /// client supports. Used by [`structured`] to decide whether to force a
67 /// tool call, request a native `response_format`, or fall back to
68 /// prompt-and-parse. Defaults to no native support.
69 fn native_structured_support(&self) -> structured::NativeStructuredSupport {
70 structured::NativeStructuredSupport::None
71 }
72
73 /// Complete a conversation while honoring a structured-output directive
74 /// (forced `tool_choice` and/or native `response_format`).
75 ///
76 /// The default implementation ignores the directive and behaves exactly
77 /// like [`LlmClient::complete`], so existing clients keep working unchanged;
78 /// providers that support native structured output override this.
79 async fn complete_structured(
80 &self,
81 messages: &[Message],
82 system: Option<&str>,
83 tools: &[ToolDefinition],
84 _directive: &structured::StructuredDirective,
85 ) -> Result<LlmResponse> {
86 self.complete(messages, system, tools).await
87 }
88
89 /// Streaming counterpart of [`LlmClient::complete_structured`]. Defaults to
90 /// [`LlmClient::complete_streaming`], ignoring the directive.
91 async fn complete_streaming_structured(
92 &self,
93 messages: &[Message],
94 system: Option<&str>,
95 tools: &[ToolDefinition],
96 _directive: &structured::StructuredDirective,
97 cancel_token: CancellationToken,
98 ) -> Result<mpsc::Receiver<StreamEvent>> {
99 self.complete_streaming(messages, system, tools, cancel_token)
100 .await
101 }
102}
103
104// Include test modules — these reference internal types via crate paths
105#[cfg(test)]
106#[path = "tests.rs"]
107mod tests_file;