Skip to content

Commit 8d7d5c3

Browse files
committed
feat(core): deepseek 模型请求响应体的结构体映射
1 parent 260f17b commit 8d7d5c3

17 files changed

Lines changed: 586 additions & 3 deletions

File tree

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# 说明:.env 已配置 Git 忽略,仅本地生效;.env.example 为公开模板,不会被忽略
2+
# 使用方式:复制本文件并重命名为 .env,在 .env 中的 API_KEY 键填入真实 API Key
3+
# 重要警告:严禁在 .env.example 中写入真实密钥并提交至远程仓库,防止 Token 泄露
4+
# 补充:仓库虽然开启了密钥泄露通知,但仅作兜底防护,请勿依赖该机制
5+
6+
API_KEY=sh-...

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ anyhow = "1.0.102"
1616
tokio = { version = "1.52.3", features = ["full"] }
1717
colored = "3.1.1"
1818
indicatif = "0.18.4"
19-
reqwest = { version = "0.13.3", features = ["stream"] }
19+
reqwest = { version = "0.13.3", features = ["stream", "json"] }
2020
tokio-stream = "0.1.18"
2121
futures = "0.3.32"
2222
futures-util = "0.3.32"
@@ -38,4 +38,6 @@ zip = "8.6.0"
3838
flate2 = "1.1.9"
3939
tar = "0.4.46"
4040
thiserror = "2.0.18"
41-
hex = "0.4.3"
41+
hex = "0.4.3"
42+
dotenv = "0.15.0"
43+
bytes = "1.11.1"

src/core/chat/client.rs

Whitespace-only changes.

src/core/chat/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mod client;
2+
mod provider;

src/core/chat/provider/anthropic/mod.rs

Whitespace-only changes.

src/core/chat/provider/common/mod.rs

Whitespace-only changes.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
use serde::{Deserialize, Serialize};
2+
use serde_json::Value;
3+
use super::{enums::*, message::ChatMessage, tool::*};
4+
5+
#[derive(Debug, Serialize, Deserialize, Default)]
6+
pub struct StreamOptions {
7+
#[serde(skip_serializing_if = "Option::is_none")]
8+
pub include_usage: Option<bool>,
9+
}
10+
11+
#[derive(Debug, Serialize, Deserialize, Default)]
12+
pub struct ResponseFormat {
13+
pub r#type: ResponseFormatType,
14+
}
15+
16+
/// 聊天补全请求
17+
#[derive(Debug, Serialize, Deserialize, Default)]
18+
pub struct CreateChatCompletionRequest {
19+
pub model: String,
20+
pub messages: Vec<ChatMessage>,
21+
#[serde(skip_serializing_if = "Option::is_none")]
22+
pub frequency_penalty: Option<f32>,
23+
#[serde(skip_serializing_if = "Option::is_none")]
24+
pub presence_penalty: Option<f32>,
25+
#[serde(skip_serializing_if = "Option::is_none")]
26+
pub temperature: Option<f32>,
27+
#[serde(skip_serializing_if = "Option::is_none")]
28+
pub top_p: Option<f32>,
29+
#[serde(skip_serializing_if = "Option::is_none")]
30+
pub stop: Option<Value>,
31+
#[serde(skip_serializing_if = "Option::is_none")]
32+
pub max_tokens: Option<u32>,
33+
#[serde(skip_serializing_if = "Option::is_none")]
34+
pub stream: Option<bool>,
35+
#[serde(skip_serializing_if = "Option::is_none")]
36+
pub stream_options: Option<StreamOptions>,
37+
#[serde(skip_serializing_if = "Option::is_none")]
38+
pub tools: Option<Vec<Tool>>,
39+
#[serde(skip_serializing_if = "Option::is_none")]
40+
pub tool_choice: Option<ToolChoice>,
41+
#[serde(skip_serializing_if = "Option::is_none")]
42+
pub response_format: Option<ResponseFormat>,
43+
#[serde(skip_serializing_if = "Option::is_none")]
44+
pub logprobs: Option<bool>,
45+
#[serde(skip_serializing_if = "Option::is_none")]
46+
pub top_logprobs: Option<u8>,
47+
#[serde(skip_serializing_if = "Option::is_none")]
48+
pub user: Option<String>,
49+
}
50+
51+
/// 聊天补全响应
52+
#[derive(Debug, Serialize, Deserialize)]
53+
pub struct ChatCompletionResponse {
54+
pub id: String,
55+
pub object: String,
56+
pub created: i64,
57+
pub model: String,
58+
#[serde(skip_serializing_if = "Option::is_none")]
59+
pub system_fingerprint: Option<String>,
60+
pub choices: Vec<Choice>,
61+
pub usage: Usage,
62+
#[serde(skip_serializing_if = "Option::is_none")]
63+
pub prompt_filter_results: Option<Vec<Value>>,
64+
}
65+
66+
#[derive(Debug, Serialize, Deserialize)]
67+
pub struct Choice {
68+
pub index: i32,
69+
pub message: ChatMessage,
70+
#[serde(skip_serializing_if = "Option::is_none")]
71+
pub finish_reason: Option<FinishReason>,
72+
#[serde(skip_serializing_if = "Option::is_none")]
73+
pub logprobs: Option<Logprobs>,
74+
}
75+
76+
#[derive(Debug, Serialize, Deserialize)]
77+
pub struct Logprobs {
78+
pub content: Option<Vec<LogprobContent>>,
79+
}
80+
81+
#[derive(Debug, Serialize, Deserialize)]
82+
pub struct LogprobContent {
83+
pub token: String,
84+
pub logprob: f32,
85+
#[serde(skip_serializing_if = "Option::is_none")]
86+
pub bytes: Option<Vec<u8>>,
87+
pub top_logprobs: Vec<TopLogprob>,
88+
}
89+
90+
#[derive(Debug, Serialize, Deserialize)]
91+
pub struct TopLogprob {
92+
pub token: String,
93+
pub logprob: f32,
94+
#[serde(skip_serializing_if = "Option::is_none")]
95+
pub bytes: Option<Vec<u8>>,
96+
}
97+
98+
#[derive(Debug, Serialize, Deserialize)]
99+
pub struct Usage {
100+
pub prompt_tokens: u32,
101+
pub completion_tokens: u32,
102+
pub total_tokens: u32,
103+
#[serde(skip_serializing_if = "Option::is_none")]
104+
pub prompt_tokens_details: Option<PromptTokensDetails>,
105+
#[serde(skip_serializing_if = "Option::is_none")]
106+
pub completion_tokens_details: Option<CompletionTokensDetails>,
107+
}
108+
109+
#[derive(Debug, Serialize, Deserialize)]
110+
pub struct PromptTokensDetails {
111+
pub cached_tokens: u32,
112+
}
113+
114+
#[derive(Debug, Serialize, Deserialize)]
115+
pub struct CompletionTokensDetails {
116+
pub reasoning_tokens: u32,
117+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
4+
#[serde(rename_all = "snake_case")]
5+
pub enum Role {
6+
System,
7+
User,
8+
Assistant,
9+
Tool,
10+
}
11+
12+
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
13+
#[serde(rename_all = "snake_case")]
14+
pub enum ToolType {
15+
Function,
16+
}
17+
18+
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
19+
#[serde(rename_all = "snake_case")]
20+
pub enum ResponseFormatType {
21+
Text,
22+
JsonObject,
23+
}
24+
25+
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
26+
#[serde(rename_all = "snake_case")]
27+
pub enum FinishReason {
28+
Stop,
29+
Length,
30+
ToolCalls,
31+
ContentFilter,
32+
}
33+
34+
impl Default for ResponseFormatType {
35+
fn default() -> Self {
36+
Self::Text
37+
}
38+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use serde::{Deserialize, Serialize};
2+
use super::enums::Role;
3+
4+
#[derive(Debug, Serialize, Deserialize)]
5+
#[serde(untagged)]
6+
pub enum MessageContent {
7+
Text(String),
8+
Parts(Vec<ContentPart>),
9+
}
10+
11+
#[derive(Debug, Serialize, Deserialize)]
12+
pub struct ContentPart {
13+
pub r#type: String,
14+
#[serde(skip_serializing_if = "Option::is_none")]
15+
pub text: Option<String>,
16+
#[serde(skip_serializing_if = "Option::is_none")]
17+
pub image_url: Option<ImageUrl>,
18+
}
19+
20+
#[derive(Debug, Serialize, Deserialize)]
21+
pub struct ImageUrl {
22+
pub url: String,
23+
}
24+
25+
#[derive(Debug, Serialize, Deserialize)]
26+
pub struct ChatMessage {
27+
pub role: Role,
28+
pub content: MessageContent,
29+
#[serde(skip_serializing_if = "Option::is_none")]
30+
pub name: Option<String>,
31+
#[serde(skip_serializing_if = "Option::is_none")]
32+
pub tool_call_id: Option<String>,
33+
#[serde(skip_serializing_if = "Option::is_none")]
34+
pub tool_calls: Option<Vec<super::tool::ToolCall>>,
35+
}
36+
37+
impl Default for ChatMessage {
38+
fn default() -> Self {
39+
Self {
40+
role: Role::User,
41+
content: MessageContent::Text(String::new()),
42+
name: None,
43+
tool_call_id: None,
44+
tool_calls: None,
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)