Skip to main content

ds_api/
normal_chatter.rs

1//! 支持自定义历史记录管理的聊天客户端模块
2//!
3//! 提供灵活的聊天客户端实现,允许用户自定义对话历史记录的存储和管理方式。
4//!
5//! # 主要特性
6//!
7//! - **自定义历史记录**: 通过实现 [`History`] trait 来自定义历史记录存储
8//! - **聊天功能**: 支持基本的聊天交互
9//! - **JSON 响应**: 支持 JSON 格式的响应
10//! - **异步处理**: 基于 `tokio` 的异步实现
11//!
12//! # 示例
13//!
14//! ## 基本使用
15//!
16//! ```rust,no_run
17//! use ds_api::{NormalChatter, History, Message, Role};
18//!
19//! #[tokio::main]
20//! async fn main() -> ds_api::error::Result<()> {
21//!     let token = "your_deepseek_api_token".to_string();
22//!     let mut chatter = NormalChatter::new(token);
23//!     let mut history: Vec<Message> = vec![];
24//!
25//!     let response = chatter.chat("Hello, how are you?", &mut history).await?;
26//!     println!("Assistant: {}", response);
27//!
28//!     Ok(())
29//! }
30//! ```
31//!
32//! ## 自定义历史记录实现
33//!
34//! ```rust,no_run
35//! use ds_api::{NormalChatter, History, Message, Role};
36//!
37//! struct LimitedHistory {
38//!     messages: Vec<Message>,
39//!     max_messages: usize,
40//! }
41//!
42//! impl History for LimitedHistory {
43//!     fn add_message(&mut self, message: Message) {
44//!         self.messages.push(message);
45//!         if self.messages.len() > self.max_messages {
46//!             self.messages.remove(0); // 移除最旧的消息
47//!         }
48//!     }
49//!
50//!     fn get_history(&self) -> Vec<Message> {
51//!         self.messages.clone()
52//!     }
53//! }
54//!
55//! #[tokio::main]
56//! async fn main() -> ds_api::error::Result<()> {
57//!     let token = "your_deepseek_api_token".to_string();
58//!     let mut chatter = NormalChatter::new(token);
59//!     let mut history = LimitedHistory {
60//!         messages: vec![],
61//!         max_messages: 10,
62//!     };
63//!
64//!     let response = chatter.chat("What is Rust?", &mut history).await?;
65//!     println!("Assistant: {}", response);
66//!
67//!     Ok(())
68//! }
69//! ```
70//!
71//! ## 使用 JSON 响应
72//!
73//! ```rust,no_run
74//! use ds_api::{NormalChatter, History, Message, Role};
75//! use serde_json::Value;
76//!
77//! #[tokio::main]
78//! async fn main() -> ds_api::error::Result<()> {
79//!     let token = "your_deepseek_api_token".to_string();
80//!     let mut chatter = NormalChatter::new(token);
81//!     let mut history: Vec<Message> = vec![
82//!         Message::new(Role::System, "You are a helpful assistant that responds in JSON format.")
83//!     ];
84//!
85//!     let json_response = chatter.chat_json("Give me information about Paris in JSON format", &mut history).await?;
86//!     println!("JSON response: {}", serde_json::to_string_pretty(&json_response)?);
87//!
88//!     Ok(())
89//! }
90//! ```
91//!
92//! # 注意事项
93//!
94//! - 当前实现不支持流式响应
95//! - 需要手动管理上下文长度,避免超过模型限制
96//! - 历史记录中的第一条消息通常是系统提示词
97//!
98
99/// 历史记录管理 trait
100///
101/// 定义对话历史记录的存储和管理接口,允许用户自定义历史记录的存储方式。
102///
103/// # 实现要求
104///
105/// 实现者需要提供:
106/// - 消息添加功能
107/// - 历史记录获取功能
108///
109/// # 示例
110///
111/// 使用 `Vec<Message>` 作为历史记录存储:
112///
113/// ```rust
114/// use ds_api::{History, Message, Role};
115///
116/// let mut history: Vec<Message> = vec![];
117/// history.add_message(Message::new(Role::User, "Hello"));
118///
119/// let messages = history.get_history();
120/// assert_eq!(messages.len(), 1);
121/// ```
122pub trait History {
123    /// 添加一条消息到历史记录中
124    ///
125    /// # 参数
126    ///
127    /// * `message` - 要添加的消息
128    fn add_message(&mut self, message: Message);
129
130    /// 获取完整的历史记录
131    ///
132    /// 返回历史记录中所有消息的副本。由于需要发送给 API,
133    /// 这里直接返回 `Vec<Message>` 而不是迭代器,这样不会带来性能损失。
134    ///
135    /// # 返回
136    ///
137    /// 历史记录中所有消息的向量
138    fn get_history(&self) -> Vec<Message>;
139}
140
141impl History for Vec<Message> {
142    fn add_message(&mut self, message: Message) {
143        self.push(message);
144    }
145
146    fn get_history(&self) -> Vec<Message> {
147        self.clone()
148    }
149}
150
151use crate::error::Result;
152
153use crate::request::*;
154use crate::response::Response;
155use reqwest::Client;
156use serde_json::Value;
157
158/// 支持自定义历史记录管理的聊天客户端
159///
160/// 这个结构体提供了与 DeepSeek API 交互的基本功能,同时允许用户
161/// 通过实现 [`History`] trait 来自定义历史记录的存储和管理方式。
162///
163/// # 字段
164///
165/// - `token`: DeepSeek API 访问令牌
166/// - `client`: HTTP 客户端,用于发送请求
167///
168/// # 注意事项
169///
170/// - 历史记录中的第一条消息通常是系统提示词(System Prompt)
171/// - 后续消息是用户(User)或助手(Assistant)的对话内容
172/// - 需要手动管理上下文长度,避免超过模型限制
173pub struct NormalChatter {
174    /// DeepSeek API 访问令牌
175    pub token: String,
176    /// HTTP 客户端实例
177    pub client: Client,
178}
179
180impl NormalChatter {
181    /// 创建一个新的 `NormalChatter` 实例
182    ///
183    /// # 参数
184    ///
185    /// * `token` - DeepSeek API 访问令牌
186    ///
187    /// # 示例
188    ///
189    /// ```rust
190    /// use ds_api::NormalChatter;
191    ///
192    /// let token = "your_deepseek_api_token".to_string();
193    /// let chatter = NormalChatter::new(token);
194    /// ```
195    pub fn new(token: String) -> Self {
196        Self {
197            token,
198            client: Client::new(),
199        }
200    }
201
202    /// 发送聊天消息并获取文本响应
203    ///
204    /// 这个方法会将用户消息添加到历史记录中,发送请求到 DeepSeek API,
205    /// 然后将助手的响应也添加到历史记录中,最后返回响应文本。
206    ///
207    /// # 参数
208    ///
209    /// * `user_message` - 用户消息内容
210    /// * `history` - 实现了 [`History`] trait 的历史记录管理器
211    ///
212    /// # 返回
213    ///
214    /// 返回助手的响应文本,如果发生错误则返回错误信息。
215    ///
216    /// # 示例
217    ///
218    /// ```rust,no_run
219    /// use ds_api::{NormalChatter, History, Message, Role};
220    ///
221    /// #[tokio::main]
222    /// async fn main() -> ds_api::error::Result<()> {
223    ///     let token = "your_token".to_string();
224    ///     let mut chatter = NormalChatter::new(token);
225    ///     let mut history: Vec<Message> = vec![];
226    ///
227    ///     let response = chatter.chat("Hello, world!", &mut history).await?;
228    ///     println!("Assistant: {}", response);
229    ///
230    ///     Ok(())
231    /// }
232    /// ```
233    pub async fn chat<T: AsRef<str>>(
234        &mut self,
235        user_message: T,
236        history: &mut impl History,
237    ) -> Result<String> {
238        let user_message = Message::new(Role::User, user_message.as_ref());
239        history.add_message(user_message);
240
241        let response = Request::basic_query(history.get_history())
242            .execute_nostreaming(&self.token)
243            .await?;
244
245        let assistant_message = response
246            .choices
247            .get(0)
248            .map(|c| c.message.clone())
249            .ok_or_else(|| {
250                crate::error::ApiError::Other("missing choice or content in response".to_string())
251            })?;
252        history.add_message(assistant_message);
253
254        Ok(response.content()?.to_string())
255    }
256
257    /// 发送聊天消息并获取 JSON 格式的响应
258    ///
259    /// 这个方法与 [`NormalChatter::chat`] 类似,但会启用 JSON 响应模式,并返回解析后的 JSON 值。
260    ///
261    /// # 参数
262    ///
263    /// * `user_message` - 用户消息内容
264    /// * `history` - 实现了 [`History`] trait 的历史记录管理器
265    ///
266    /// # 返回
267    ///
268    /// 返回解析后的 JSON 值,如果发生错误则返回错误信息。
269    ///
270    /// # 注意事项
271    ///
272    /// 使用此方法前,确保在系统提示词中指示模型返回 JSON 格式的响应。
273    ///
274    /// # 示例
275    ///
276    /// ```rust,no_run
277    /// use ds_api::{NormalChatter, History, Message, Role};
278    /// use serde_json::Value;
279    ///
280    /// #[tokio::main]
281    /// async fn main() -> ds_api::error::Result<()> {
282    ///     let token = "your_token".to_string();
283    ///     let mut chatter = NormalChatter::new(token);
284    ///     let mut history: Vec<Message> = vec![
285    ///         Message::new(Role::System, "You are a helpful assistant that responds in JSON format.")
286    ///     ];
287    ///
288    ///     let json_response = chatter.chat_json("Give me information about Paris", &mut history).await?;
289    ///     println!("JSON response: {}", serde_json::to_string_pretty(&json_response)?);
290    ///
291    ///     Ok(())
292    /// }
293    /// ```
294    pub async fn chat_json<T: AsRef<str>>(
295        &mut self,
296        user_message: T,
297        history: &mut impl History,
298    ) -> Result<Value> {
299        let user_message = Message::new(Role::User, user_message.as_ref());
300        history.add_message(user_message);
301
302        let response = Request::basic_query(history.get_history())
303            .json()
304            .execute_nostreaming(&self.token)
305            .await?;
306
307        let assistant_message = response
308            .choices
309            .get(0)
310            .map(|c| c.message.clone())
311            .ok_or_else(|| {
312                crate::error::ApiError::Other("missing choice or content in response".to_string())
313            })?;
314        history.add_message(assistant_message);
315
316        let value = serde_json::from_str(response.content()?)?;
317
318        Ok(value)
319    }
320}