优化: AI Chat全栈多批审查修复与架构清理(risk_level清理/路由解耦/工具渲染/测试补测/死代码)

This commit is contained in:
2026-06-18 22:57:19 +08:00
parent 0ca5d9805f
commit a2871a66e0
87 changed files with 5720 additions and 3012 deletions

View File

@@ -83,23 +83,25 @@ pub fn probe(model_id: &str) -> ModelConfig {
// 启发式推断(模型名命名模式 → 4 维度)
// ────────────────────────────────────────────────────────────
/// 模型名启发式推断。命名是行业惯例(flash/mini/v/embed/code),可信度 Medium。
/// 模型名启发式推断。仅推断功能性维度(modalities/capabilities),
/// cost_tier / intelligence 一律返中性默认(Medium / Standard)。
///
/// 取舍:B-260618-04 — 模型名关键词猜档位(flash→Lite、4o→High、pro→Plus)无依据,
/// 厂商定价/智力与命名无关,瞎填会污染路由器过滤(intelligence >= min / cost_tier <= max)。
/// 改中性默认,真实档位由用户手填或更高阶探测源(如厂商 API/定价表)提供。
///
/// 规则(任务规格,设计文档 §4.4 模糊匹配规则对齐):
/// - `embed`/`embedding`/`e-`(前缀) → Embedding 模型:capabilities=[Embedding],modalities=[](去 Text)
/// - `v`/`vision`/`vl`(词素) → modalities 加 Vision
/// - `flash`/`mini`/`lite`/`nano`/`air` → IntelligenceTier::Lite(air 归 Lite,对齐设计 §4.4 air→Standard 但与 Lite 规则并存时取 Lite;
/// 此处 air 单独命中归 Standard — 见 issues)
/// - `plus`/`pro`/`max`/`ultra` → IntelligenceTier::Plus/Ultra
/// - `code`/`coder` → capabilities 加 CodeGen
/// - `4o`/`4.5`/`5`(高价旗舰标识) → CostTier::High
/// - 默认 → Standard / Medium / [Text] / [ToolUse]
/// - cost_tier / intelligence → 中性默认 Medium / Standard(不靠名字猜)
fn heuristic_infer(model_id: &str) -> ModelConfig {
let name = model_id.to_lowercase();
let mut modalities: Vec<Modality> = Vec::new();
let mut capabilities: Vec<Capability> = Vec::new();
let mut cost_tier = CostTier::Medium;
let mut intelligence = IntelligenceTier::Standard;
// 中性默认:不靠模型名猜档位(B-260618-04)
let cost_tier = CostTier::Medium;
let intelligence = IntelligenceTier::Standard;
// —— Embedding 优先判定(改变模态集合,且通常独占) ——
// 规则:含 embed / embedding / 以 "e-" / "embedding-" 起首
@@ -118,8 +120,8 @@ fn heuristic_infer(model_id: &str) -> ModelConfig {
label: None,
modalities,
capabilities,
cost_tier: CostTier::Low, // embedding 普遍低价
intelligence: IntelligenceTier::Lite, // embedding 无智力概念,归 Lite
cost_tier,
intelligence,
weight: 50,
context_window: 8192,
probe_source: None,
@@ -144,24 +146,6 @@ fn heuristic_infer(model_id: &str) -> ModelConfig {
// —— 对话模型默认 ToolUse(主流支持 function calling) ——
capabilities.push(Capability::ToolUse);
// —— 智力分级 ——
if has_lite_token(&name) {
intelligence = IntelligenceTier::Lite;
} else if has_ultra_token(&name) {
intelligence = IntelligenceTier::Ultra;
} else if has_plus_token(&name) {
intelligence = IntelligenceTier::Plus;
} else if has_standard_token(&name) {
intelligence = IntelligenceTier::Standard;
}
// —— 价格分级 ——
if has_high_cost_token(&name) {
cost_tier = CostTier::High;
} else if has_lite_token(&name) {
cost_tier = CostTier::Low;
}
ModelConfig {
model_id: model_id.to_string(),
enabled: true,
@@ -184,41 +168,6 @@ fn has_vision_token(name: &str) -> bool {
name.contains("vision") || name.contains("vl") || name.ends_with('v') || name.contains("-v")
}
/// Lite 智力词素:flash / mini / lite / nano / air(air 对齐设计 §4.4 模糊规则 Lite)。
/// 注:设计 §4.4 文本规则列了 air,但 §4.4 精确例 glm-4-air→Standard。
/// 本启发式取模糊规则(air→Lite)以保持一致 — 见 issues。
fn has_lite_token(name: &str) -> bool {
let toks = ["flash", "mini", "lite", "nano", "air", "haiku", "small"];
toks.iter().any(|t| name.contains(t))
}
/// Plus 智力词素:plus / pro / max / sonnet。
fn has_plus_token(name: &str) -> bool {
let toks = ["plus", "pro", "max", "sonnet"];
toks.iter().any(|t| name.contains(t))
}
/// Ultra 智力词素:ultra / opus / largest。
fn has_ultra_token(name: &str) -> bool {
let toks = ["ultra", "opus", "largest"];
toks.iter().any(|t| name.contains(t))
}
/// Standard 智力词素:standard / chat(对话主力模型默认归 Standard)。
fn has_standard_token(name: &str) -> bool {
let toks = ["standard", "chat", "haiku"];
toks.iter().any(|t| name.contains(t))
}
/// 高价旗舰词素:4o / 4.5 / 5 / opus / o1 / o3(OpenAI/Anthropic 旗舰命名)。
fn has_high_cost_token(name: &str) -> bool {
let toks = ["4o", "4.5", "opus", "o1", "o3"];
// "5" 单字符误伤大(如 "5b"),仅匹配词边界 -5 / 5- 或以 5 结尾
toks.iter().any(|t| name.contains(t))
|| name.ends_with("-5")
|| name.contains("-5-")
}
// ────────────────────────────────────────────────────────────
// 测试
// ────────────────────────────────────────────────────────────
@@ -257,7 +206,9 @@ mod tests {
assert_eq!(m.model_id, "glm-4");
assert_eq!(m.probe_source, Some(ProbeSource::PresetTable));
assert_eq!(m.modalities, vec![Modality::Text]);
assert_eq!(m.intelligence, IntelligenceTier::Plus);
// B-260618-04:预设表不再写死 cost_tier/intelligence,由 serde default 兜底中性值
assert_eq!(m.intelligence, IntelligenceTier::Standard);
assert_eq!(m.cost_tier, CostTier::Medium);
}
#[test]
@@ -326,20 +277,22 @@ mod tests {
assert!(m.modalities.contains(&Modality::Vision));
}
// ── 启发式:Lite 智力 ──
// ── 启发式:档位中性(flash/mini 不再猜 Lite) ──
#[test]
fn heuristic_lite_from_flash() {
fn heuristic_flash_keeps_neutral_tier() {
let m = probe("unknown-flash");
assert_eq!(m.probe_source, Some(ProbeSource::Heuristic));
assert_eq!(m.intelligence, IntelligenceTier::Lite);
assert_eq!(m.cost_tier, CostTier::Low);
// B-260618-04:cost/intel 一律中性,不靠名字猜
assert_eq!(m.intelligence, IntelligenceTier::Standard);
assert_eq!(m.cost_tier, CostTier::Medium);
}
#[test]
fn heuristic_lite_from_mini() {
fn heuristic_mini_keeps_neutral_tier() {
let m = probe("test-mini-model");
assert_eq!(m.intelligence, IntelligenceTier::Lite);
assert_eq!(m.intelligence, IntelligenceTier::Standard);
assert_eq!(m.cost_tier, CostTier::Medium);
}
// ── 启发式:CodeGen ──
@@ -377,24 +330,25 @@ mod tests {
assert!(m.modalities.is_empty());
}
// ── 启发式:Plus/Ultra 智力 + High 价格 ──
// ── 启发式:档位中性(pro/opus/4o 不再猜 Plus/Ultra/High) ──
#[test]
fn heuristic_plus_from_pro() {
fn heuristic_pro_keeps_neutral_tier() {
let m = probe("acme-pro");
assert_eq!(m.intelligence, IntelligenceTier::Plus);
assert_eq!(m.intelligence, IntelligenceTier::Standard);
assert_eq!(m.cost_tier, CostTier::Medium);
}
#[test]
fn heuristic_ultra_from_opus() {
fn heuristic_opus_keeps_neutral_tier() {
let m = probe("acme-opus");
assert_eq!(m.intelligence, IntelligenceTier::Ultra);
assert_eq!(m.intelligence, IntelligenceTier::Standard);
}
#[test]
fn heuristic_high_cost_from_4o() {
fn heuristic_4o_keeps_neutral_cost() {
let m = probe("acme-4o");
assert_eq!(m.cost_tier, CostTier::High);
assert_eq!(m.cost_tier, CostTier::Medium);
}
// ── 启发式:默认(Standard/Medium/Text/ToolUse) ──
@@ -413,10 +367,10 @@ mod tests {
#[test]
fn probe_preset_beats_heuristic() {
// "glm-4-flash" 在预设表 = Lite;若走启发式也会是 Lite,但 source 应为 PresetTable
// "glm-4-flash" 精确命中预设表;B-260618-04 后预设/启发式档位都中性,
// 此处仅校验 source 标注为 PresetTable
let m = probe("glm-4-flash");
assert_eq!(m.probe_source, Some(ProbeSource::PresetTable));
assert_eq!(m.intelligence, IntelligenceTier::Lite);
}
#[test]