Skip to main content

ds_api/raw/request/
chat_completion.rs

1use serde::{Deserialize, Serialize};
2
3use super::{
4    message::Message, model::Model, response_format::ResponseFormat, stop::Stop,
5    stream_options::StreamOptions, thinking::Thinking, tool::Tool, tool_choice::ToolChoice,
6};
7
8#[derive(Debug, Default, Serialize, Deserialize)]
9#[serde(default)]
10pub struct ChatCompletionRequest {
11    /// 对话的消息列表。
12    pub messages: Vec<Message>,
13
14    /// 使用的模型的 ID。您可以使用 deepseek-chat 来获得更快的响应速度,或者使用 deepseek-reasoner 来获得更深入的推理能力。
15    pub model: Model,
16
17    /// 控制思考模式与非思考模式的转换
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub thinking: Option<Thinking>,
20
21    /// Possible values: >= -2 and <= 2
22    /// Default value: 0
23    /// 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其在已有文本中的出现频率受到相应的惩罚,降低模型重复相同内容的可能性。
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub frequency_penalty: Option<f32>,
26
27    /// 限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。取值范围与默认值详见文档。
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub max_tokens: Option<u32>,
30
31    /// Possible values: >= -2 and <= 2
32    /// Default value: 0
33    /// 介于 -2.0 和 2.0 之间的数字。如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性。
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub presence_penalty: Option<f32>,
36
37    /// 一个 object,指定模型必须输出的格式。
38    /// 设置为 { "type": "json_object" } 以启用 JSON 模式,该模式保证模型生成的消息是有效的 JSON。
39    /// 注意: 使用 JSON 模式时,你还必须通过系统或用户消息指示模型生成 JSON。否则,模型可能会生成不断的空白字符,直到生成达到令牌限制,从而导致请求长时间运行并显得“卡住”。此外,如果 finish_reason="length",这表示生成超过了 max_tokens 或对话超过了最大上下文长度,消息内容可能会被部分截断。
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub response_format: Option<ResponseFormat>,
42
43    /// 一个 string 或最多包含 16 个 string 的 list,在遇到这些词时,API 将停止生成更多的 token。
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub stop: Option<Stop>,
46
47    /// 如果设置为 True,将会以 SSE(server-sent events)的形式以流式发送消息增量。消息流以 data: \[DONE\] 结尾。
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub stream: Option<bool>,
50
51    /// 流式输出相关选项。只有在 stream 参数为 true 时,才可设置此参数。
52    /// include_usage: boolean
53    /// 如果设置为 true,在流式消息最后的 data: \[DONE\] 之前将会传输一个额外的块。此块上的 usage 字段显示整个请求的 token 使用统计信息,而 choices 字段将始终是一个空数组。所有其他块也将包含一个 usage 字段,但其值为 null。
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub stream_options: Option<StreamOptions>,
56
57    /// Possible values: <= 2
58    /// Default value: 1
59    /// 采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。 我们通常建议可以更改这个值或者更改 top_p,但不建议同时对两者进行修改。
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub temperature: Option<f32>,
62
63    /// Possible values: <= 1
64    /// Default value: 1
65    /// 作为调节采样温度的替代方案,模型会考虑前 top_p 概率的 token 的结果。所以 0.1 就意味着只有包括在最高 10% 概率中的 token 会被考虑。 我们通常建议修改这个值或者更改 temperature,但不建议同时对两者进行修改。
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub top_p: Option<f32>,
68
69    /// 模型可能会调用的 tool 的列表。目前,仅支持 function 作为工具。使用此参数来提供以 JSON 作为输入参数的 function 列表。最多支持 128 个 function。
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub tools: Option<Vec<Tool>>,
72
73    /// 控制模型调用 tool 的行为。
74    /// none 意味着模型不会调用任何 tool,而是生成一条消息。
75    /// auto 意味着模型可以选择生成一条消息或调用一个或多个 tool。
76    /// required 意味着模型必须调用一个或多个 tool。
77    /// 通过 {"type": "function", "function": {"name": "my_function"}} 指定特定 tool,会强制模型调用该 tool。
78    /// 当没有 tool 时,默认值为 none。如果有 tool 存在,默认值为 auto。
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub tool_choice: Option<ToolChoice>,
81
82    /// logprobs boolean NULLABLE
83    /// 是否返回所输出 token 的对数概率。如果为 true,则在 message 的 content 中返回每个输出 token 的对数概率。
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub logprobs: Option<bool>,
86
87    /// Possible values: <= 20
88    /// 一个介于 0 到 20 之间的整数 N,指定每个输出位置返回输出概率 top N 的 token,且返回这些 token 的对数概率。指定此参数时,logprobs 必须为 true。
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub top_logprobs: Option<u32>,
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use crate::raw::request::message::Role;
97
98    #[test]
99    fn test_chat_completion_request_serialization() {
100        let request = ChatCompletionRequest {
101            messages: vec![Message {
102                role: Role::User,
103                content: Some("Hello, world!".to_string()),
104                name: None,
105                tool_call_id: None,
106                tool_calls: None,
107                reasoning_content: None,
108                prefix: None,
109            }],
110            model: Model::DeepseekChat,
111            thinking: None,
112            frequency_penalty: Some(0.5),
113            max_tokens: Some(100),
114            presence_penalty: None,
115            response_format: None,
116            stop: None,
117            stream: Some(false),
118            stream_options: None,
119            temperature: Some(0.7),
120            top_p: None,
121            tools: None,
122            tool_choice: None,
123            logprobs: None,
124            top_logprobs: None,
125        };
126
127        let json = serde_json::to_string(&request).unwrap();
128        let parsed: ChatCompletionRequest = serde_json::from_str(&json).unwrap();
129
130        assert_eq!(parsed.messages.len(), 1);
131        assert_eq!(
132            parsed.messages[0].content.as_ref().unwrap(),
133            "Hello, world!"
134        );
135        assert!(matches!(parsed.model, Model::DeepseekChat));
136        assert_eq!(parsed.frequency_penalty, Some(0.5));
137        assert_eq!(parsed.max_tokens, Some(100));
138        assert_eq!(parsed.stream, Some(false));
139        assert_eq!(parsed.temperature, Some(0.7));
140    }
141
142    #[test]
143    fn test_default_chat_completion_request() {
144        let request = ChatCompletionRequest::default();
145
146        assert!(request.messages.is_empty());
147        assert!(matches!(request.model, Model::DeepseekChat));
148        assert!(request.thinking.is_none());
149        assert!(request.frequency_penalty.is_none());
150        assert!(request.max_tokens.is_none());
151        assert!(request.presence_penalty.is_none());
152        assert!(request.response_format.is_none());
153        assert!(request.stop.is_none());
154        assert!(request.stream.is_none());
155        assert!(request.stream_options.is_none());
156        assert!(request.temperature.is_none());
157        assert!(request.top_p.is_none());
158        assert!(request.tools.is_none());
159        assert!(request.tool_choice.is_none());
160        assert!(request.logprobs.is_none());
161        assert!(request.top_logprobs.is_none());
162    }
163}