优化: AI Chat全栈多批审查修复与架构清理(risk_level清理/路由解耦/工具渲染/测试补测/死代码)
This commit is contained in:
@@ -43,12 +43,16 @@ pub enum Capability {
|
||||
|
||||
/// 维度 3 — 价格分级(成本)
|
||||
///
|
||||
/// 设计文档 §2.2:Free/Low/Medium/High。序列化为 snake_case。
|
||||
/// 路由器按此做成本上限过滤(§6.1 `cost_tier <= max_cost`)。
|
||||
/// 序列化为 snake_case。原设计文档 §2.2 列 Free/Low/Medium/High,但 Free 变体形同虚设:
|
||||
/// 预设表(presets/models.json)0 条 free + 启发式从不赋 Free(只 Low/Medium/High),
|
||||
/// B-260618-05 删除 Free 死档变体。
|
||||
///
|
||||
/// 路由已解耦(2026-06-18 B-260618-03):provider /v1/models API 不返回 cost_tier,
|
||||
/// 此维度 100% 靠预设表写死 + 模型名启发式猜,数据无客观依据不可信,不再参与硬路由
|
||||
/// (原 §6.1 `cost_tier <= max_cost` 过滤已删除)。枚举保留供未来出现真实判别源时再接回。
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum CostTier {
|
||||
Free,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
@@ -57,7 +61,11 @@ pub enum CostTier {
|
||||
/// 维度 4 — 聪明程度(智力分级)
|
||||
///
|
||||
/// 设计文档 §2.2:Lite/Standard/Plus/Ultra。序列化为 snake_case。
|
||||
/// 派生 Ord:Lite < Standard < Plus < Ultra,供路由器 `intelligence >= min_intelligence` 比较(§6.1)。
|
||||
/// 派生 Ord:Lite < Standard < Plus < Ultra。
|
||||
///
|
||||
/// 路由已解耦(2026-06-18 B-260618-03):provider /v1/models API 不返回 intelligence,
|
||||
/// 此维度 100% 靠预设表写死 + 模型名启发式猜,数据无客观依据不可信,不再参与硬路由
|
||||
/// (原 §6.1 `intelligence >= min_intelligence` 过滤已删除)。枚举保留供未来出现真实判别源时再接回。
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum IntelligenceTier {
|
||||
@@ -285,7 +293,6 @@ mod tests {
|
||||
#[test]
|
||||
fn cost_tier_serde_and_ordering() {
|
||||
let cases = [
|
||||
(CostTier::Free, "\"free\""),
|
||||
(CostTier::Low, "\"low\""),
|
||||
(CostTier::Medium, "\"medium\""),
|
||||
(CostTier::High, "\"high\""),
|
||||
@@ -294,8 +301,8 @@ mod tests {
|
||||
assert_eq!(serde_json::to_string(&variant).unwrap(), expected);
|
||||
assert_eq!(serde_json::from_str::<CostTier>(expected).unwrap(), variant);
|
||||
}
|
||||
// Ord:Free < Low < Medium < High(路由器 max_cost 比较)
|
||||
assert!(CostTier::Free < CostTier::Low);
|
||||
// Ord:Low < Medium < High(枚举序,路由已解耦不再用于过滤,保留供未来判别源)。
|
||||
// Free 变体已删除(B-260618-05:预设/启发式从不赋 Free,死档)。
|
||||
assert!(CostTier::Low < CostTier::Medium);
|
||||
assert!(CostTier::Medium < CostTier::High);
|
||||
}
|
||||
@@ -312,7 +319,7 @@ mod tests {
|
||||
assert_eq!(serde_json::to_string(&variant).unwrap(), expected);
|
||||
assert_eq!(serde_json::from_str::<IntelligenceTier>(expected).unwrap(), variant);
|
||||
}
|
||||
// Ord:Lite < Standard < Plus < Ultra(路由器 min_intelligence 比较)
|
||||
// Ord:Lite < Standard < Plus < Ultra(枚举序,路由已解耦不再用于过滤,保留供未来判别源)
|
||||
assert!(IntelligenceTier::Lite < IntelligenceTier::Standard);
|
||||
assert!(IntelligenceTier::Standard < IntelligenceTier::Plus);
|
||||
assert!(IntelligenceTier::Plus < IntelligenceTier::Ultra);
|
||||
|
||||
@@ -121,29 +121,41 @@ pub struct ChatMessage {
|
||||
/// DeepSeek thinking 模式的推理内容(多轮需回传)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub reasoning_content: Option<String>,
|
||||
/// 消息创建时间(Unix 毫秒)。仅前端时间戳展示用,序列化进 messages JSON 持久化;
|
||||
/// provider 请求映射不读此字段(构造器打戳→映射忽略,不进 LLM 请求),老数据反序列化为 None。
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub timestamp: Option<i64>,
|
||||
}
|
||||
|
||||
/// 当前 Unix 毫秒(ChatMessage 打戳用;df-ai-core 不依赖 df-types,内联避免新增依赖)。
|
||||
fn now_millis_i64() -> i64 {
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_millis() as i64)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
impl ChatMessage {
|
||||
pub fn system(content: impl Into<String>) -> Self {
|
||||
Self { role: MessageRole::System, content: content.into(), parts: None, tool_call_id: None, tool_calls: None, model: None, status: None, reasoning_content: None }
|
||||
Self { role: MessageRole::System, content: content.into(), parts: None, tool_call_id: None, tool_calls: None, model: None, status: None, reasoning_content: None, timestamp: Some(now_millis_i64()) }
|
||||
}
|
||||
pub fn user(content: impl Into<String>) -> Self {
|
||||
Self { role: MessageRole::User, content: content.into(), parts: None, tool_call_id: None, tool_calls: None, model: None, status: None, reasoning_content: None }
|
||||
Self { role: MessageRole::User, content: content.into(), parts: None, tool_call_id: None, tool_calls: None, model: None, status: None, reasoning_content: None, timestamp: Some(now_millis_i64()) }
|
||||
}
|
||||
pub fn assistant(content: impl Into<String>) -> Self {
|
||||
Self { role: MessageRole::Assistant, content: content.into(), parts: None, tool_call_id: None, tool_calls: None, model: None, status: None, reasoning_content: None }
|
||||
Self { role: MessageRole::Assistant, content: content.into(), parts: None, tool_call_id: None, tool_calls: None, model: None, status: None, reasoning_content: None, timestamp: Some(now_millis_i64()) }
|
||||
}
|
||||
pub fn assistant_with_tools(content: impl Into<String>, tool_calls: Vec<ToolCall>) -> Self {
|
||||
Self { role: MessageRole::Assistant, content: content.into(), parts: None, tool_call_id: None, tool_calls: Some(tool_calls), model: None, status: None, reasoning_content: None }
|
||||
Self { role: MessageRole::Assistant, content: content.into(), parts: None, tool_call_id: None, tool_calls: Some(tool_calls), model: None, status: None, reasoning_content: None, timestamp: Some(now_millis_i64()) }
|
||||
}
|
||||
pub fn tool_result(call_id: impl Into<String>, content: impl Into<String>) -> Self {
|
||||
Self { role: MessageRole::Tool, content: content.into(), parts: None, tool_call_id: Some(call_id.into()), tool_calls: None, model: None, status: None, reasoning_content: None }
|
||||
Self { role: MessageRole::Tool, content: content.into(), parts: None, tool_call_id: Some(call_id.into()), tool_calls: None, model: None, status: None, reasoning_content: None, timestamp: Some(now_millis_i64()) }
|
||||
}
|
||||
|
||||
/// 多模态 user 消息:content 文本 + parts(含 Image 片)。
|
||||
/// content 作为人类可读文本(也作非 vision 端点降级载荷);parts 透传给 vision 端点。
|
||||
pub fn user_parts(content: impl Into<String>, parts: Vec<ContentPart>) -> Self {
|
||||
Self { role: MessageRole::User, content: content.into(), parts: Some(parts), tool_call_id: None, tool_calls: None, model: None, status: None, reasoning_content: None }
|
||||
Self { role: MessageRole::User, content: content.into(), parts: Some(parts), tool_call_id: None, tool_calls: None, model: None, status: None, reasoning_content: None, timestamp: Some(now_millis_i64()) }
|
||||
}
|
||||
|
||||
/// 是否含图片片(供 provider 判定走多模态分支)。
|
||||
@@ -462,6 +474,7 @@ mod tests {
|
||||
model: None,
|
||||
status: None,
|
||||
reasoning_content: None,
|
||||
timestamp: None,
|
||||
};
|
||||
assert_eq!(m.content, "字面量构造");
|
||||
assert!(m.parts.is_none());
|
||||
@@ -512,6 +525,7 @@ mod tests {
|
||||
model: None,
|
||||
status: None,
|
||||
reasoning_content: Some("thinking process".to_string()),
|
||||
timestamp: None,
|
||||
};
|
||||
let json = serde_json::to_string(&m).unwrap();
|
||||
assert!(json.contains("reasoning_content"));
|
||||
|
||||
Reference in New Issue
Block a user