llm_connector/core/
builder.rs

1//! Provider 构建器 - 统一的构建接口
2//!
3//! 这个模块提供了一个优雅的 Builder 模式 API,用于构建各种 Provider。
4
5use crate::core::{GenericProvider, HttpClient, Protocol};
6use crate::error::LlmConnectorError;
7use std::collections::HashMap;
8
9/// Provider 构建器
10///
11/// 提供链式调用的 API 来构建 Provider,统一处理所有配置项。
12///
13/// # 示例
14/// ```rust,no_run
15/// use llm_connector::core::{ProviderBuilder, Protocol};
16/// use llm_connector::protocols::OpenAIProtocol;
17///
18/// let provider = ProviderBuilder::new(
19///     OpenAIProtocol::new("sk-..."),
20///     "https://api.openai.com"
21/// )
22/// .timeout(60)
23/// .proxy("http://proxy:8080")
24/// .header("X-Custom-Header", "value")
25/// .build()
26/// .unwrap();
27/// ```
28pub struct ProviderBuilder<P: Protocol> {
29    protocol: P,
30    base_url: String,
31    timeout_secs: Option<u64>,
32    proxy: Option<String>,
33    extra_headers: HashMap<String, String>,
34}
35
36impl<P: Protocol> ProviderBuilder<P> {
37    /// 创建新的 Provider 构建器
38    ///
39    /// # 参数
40    /// - `protocol`: 协议实例
41    /// - `base_url`: 基础 URL
42    ///
43    /// # 示例
44    /// ```rust,no_run
45    /// use llm_connector::core::ProviderBuilder;
46    /// use llm_connector::protocols::OpenAIProtocol;
47    ///
48    /// let builder = ProviderBuilder::new(
49    ///     OpenAIProtocol::new("sk-..."),
50    ///     "https://api.openai.com"
51    /// );
52    /// ```
53    pub fn new(protocol: P, base_url: &str) -> Self {
54        Self {
55            protocol,
56            base_url: base_url.to_string(),
57            timeout_secs: None,
58            proxy: None,
59            extra_headers: HashMap::new(),
60        }
61    }
62
63    /// 设置超时时间(秒)
64    ///
65    /// # 示例
66    /// ```rust,no_run
67    /// # use llm_connector::core::ProviderBuilder;
68    /// # use llm_connector::protocols::OpenAIProtocol;
69    /// let builder = ProviderBuilder::new(
70    ///     OpenAIProtocol::new("sk-..."),
71    ///     "https://api.openai.com"
72    /// )
73    /// .timeout(60);  // 60秒超时
74    /// ```
75    pub fn timeout(mut self, secs: u64) -> Self {
76        self.timeout_secs = Some(secs);
77        self
78    }
79
80    /// 设置代理
81    ///
82    /// # 示例
83    /// ```rust,no_run
84    /// # use llm_connector::core::ProviderBuilder;
85    /// # use llm_connector::protocols::OpenAIProtocol;
86    /// let builder = ProviderBuilder::new(
87    ///     OpenAIProtocol::new("sk-..."),
88    ///     "https://api.openai.com"
89    /// )
90    /// .proxy("http://proxy:8080");
91    /// ```
92    pub fn proxy(mut self, proxy: &str) -> Self {
93        self.proxy = Some(proxy.to_string());
94        self
95    }
96
97    /// 添加额外的 HTTP 头部
98    ///
99    /// 注意:这些头部会与协议的认证头部合并。
100    ///
101    /// # 示例
102    /// ```rust,no_run
103    /// # use llm_connector::core::ProviderBuilder;
104    /// # use llm_connector::protocols::OpenAIProtocol;
105    /// let builder = ProviderBuilder::new(
106    ///     OpenAIProtocol::new("sk-..."),
107    ///     "https://api.openai.com"
108    /// )
109    /// .header("X-Custom-Header", "value")
110    /// .header("X-Another-Header", "value2");
111    /// ```
112    pub fn header(mut self, key: &str, value: &str) -> Self {
113        self.extra_headers.insert(key.to_string(), value.to_string());
114        self
115    }
116
117    /// 构建 Provider
118    ///
119    /// # 返回
120    /// 配置好的 GenericProvider 实例
121    ///
122    /// # 错误
123    /// 如果 HTTP 客户端创建失败,返回错误
124    ///
125    /// # 示例
126    /// ```rust,no_run
127    /// # use llm_connector::core::ProviderBuilder;
128    /// # use llm_connector::protocols::OpenAIProtocol;
129    /// let provider = ProviderBuilder::new(
130    ///     OpenAIProtocol::new("sk-..."),
131    ///     "https://api.openai.com"
132    /// )
133    /// .timeout(60)
134    /// .build()
135    /// .unwrap();
136    /// ```
137    pub fn build(self) -> Result<GenericProvider<P>, LlmConnectorError> {
138        // 创建 HTTP 客户端
139        let client = HttpClient::with_config(
140            &self.base_url,
141            self.timeout_secs,
142            self.proxy.as_deref(),
143        )?;
144
145        // 合并认证头和额外头部
146        let mut headers: HashMap<String, String> =
147            self.protocol.auth_headers().into_iter().collect();
148        headers.extend(self.extra_headers);
149        let client = client.with_headers(headers);
150
151        // 创建通用提供商
152        Ok(GenericProvider::new(self.protocol, client))
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use crate::protocols::OpenAIProtocol;
160
161    #[test]
162    fn test_builder_basic() {
163        let provider = ProviderBuilder::new(
164            OpenAIProtocol::new("sk-test"),
165            "https://api.openai.com"
166        )
167        .build();
168
169        assert!(provider.is_ok());
170    }
171
172    #[test]
173    fn test_builder_with_timeout() {
174        let provider = ProviderBuilder::new(
175            OpenAIProtocol::new("sk-test"),
176            "https://api.openai.com"
177        )
178        .timeout(60)
179        .build();
180
181        assert!(provider.is_ok());
182    }
183
184    #[test]
185    fn test_builder_with_proxy() {
186        let provider = ProviderBuilder::new(
187            OpenAIProtocol::new("sk-test"),
188            "https://api.openai.com"
189        )
190        .proxy("http://proxy:8080")
191        .build();
192
193        assert!(provider.is_ok());
194    }
195
196    #[test]
197    fn test_builder_with_headers() {
198        let provider = ProviderBuilder::new(
199            OpenAIProtocol::new("sk-test"),
200            "https://api.openai.com"
201        )
202        .header("X-Custom-Header", "value")
203        .header("X-Another-Header", "value2")
204        .build();
205
206        assert!(provider.is_ok());
207    }
208
209    #[test]
210    fn test_builder_chain() {
211        let provider = ProviderBuilder::new(
212            OpenAIProtocol::new("sk-test"),
213            "https://api.openai.com"
214        )
215        .timeout(60)
216        .proxy("http://proxy:8080")
217        .header("X-Custom-Header", "value")
218        .build();
219
220        assert!(provider.is_ok());
221    }
222}
223