Skip to main content

lellm_core/
response.rs

1//! 响应类型。
2
3use serde::{Deserialize, Serialize};
4
5use super::{ContentBlock, ToolCall};
6
7/// 统一的聊天响应。
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ChatResponse {
10    /// 响应内容块列表,与 `Message::Assistant` 的 content 类型对齐。
11    pub content: Vec<ContentBlock>,
12    /// 冗余缓存,从 content 中提取的 tool_calls,方便访问。
13    pub tool_calls: Vec<ToolCall>,
14    pub usage: TokenUsage,
15    pub raw: serde_json::Value,
16}
17
18impl ChatResponse {
19    /// 构造函数 — 自动从 content 中提取 tool_calls
20    pub fn new(content: Vec<ContentBlock>, usage: TokenUsage, raw: serde_json::Value) -> Self {
21        let tool_calls = content
22            .iter()
23            .filter_map(|b| match b {
24                ContentBlock::ToolCall(tc) => Some(tc.clone()),
25                _ => None,
26            })
27            .collect();
28        Self {
29            content,
30            tool_calls,
31            usage,
32            raw,
33        }
34    }
35}
36
37/// Token 消耗统计。
38#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
39pub struct TokenUsage {
40    pub prompt_tokens: u32,
41    pub completion_tokens: u32,
42    pub total_tokens: u32,
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use crate::{ContentBlock, ToolCall};
49
50    #[test]
51    fn test_chat_response_new_extracts_tool_calls() {
52        let tc = ToolCall {
53            id: "1".into(),
54            name: "test".into(),
55            arguments: serde_json::json!({}),
56        };
57        let content = vec![
58            ContentBlock::text("hello".to_string()),
59            ContentBlock::ToolCall(tc.clone()),
60        ];
61        let resp = ChatResponse::new(content, TokenUsage::default(), serde_json::json!(null));
62        assert_eq!(resp.tool_calls.len(), 1);
63        assert_eq!(resp.tool_calls[0].id, "1");
64    }
65
66    #[test]
67    fn test_chat_response_no_tool_calls() {
68        let content = vec![ContentBlock::text("hello".to_string())];
69        let resp = ChatResponse::new(content, TokenUsage::default(), serde_json::json!(null));
70        assert!(resp.tool_calls.is_empty());
71    }
72}