重构: 拆model_probe模型探测(strategy核心库)
- 新建 df-ai/model_probe_helpers.rs(137行): PRESETS_JSON/presets(OnceLock) + heuristic_infer + has_vision_token - model_probe.rs 388→251: probe主入口 + use helpers - lib.rs: mod model_probe_helpers 主代grep核验(不cargo避audit-split-5中间态): model_probe.rs use+probe + lib.rs mod印证; agent df-ai独立自验cargo 0+test 119 strategy: 核心库自底向上, 纯函数抽离, pub(super)可见性, helper私有mod零API表面扩大 git add指定(df-ai/*)
This commit is contained in:
@@ -11,6 +11,9 @@ pub mod model_fetch;
|
||||
// crate 内复用,private mod — 外部经 model_fetch 间接访问,无直接路径需求。
|
||||
mod model_fetch_helpers;
|
||||
pub mod model_probe;
|
||||
// model_probe 的纯逻辑子模块(预设表加载 / 启发式推断 / 词素判定),
|
||||
// crate 内复用,private mod — 外部经 model_probe 间接访问,无直接路径需求。
|
||||
mod model_probe_helpers;
|
||||
pub mod openai_compat;
|
||||
pub mod openai_helpers;
|
||||
pub mod provider;
|
||||
|
||||
@@ -12,28 +12,11 @@
|
||||
//! (设计文档 §4.1 写的是"启发式先行 + 预设表合并";本实施按阶段 2 任务规格
|
||||
//! 收敛为"预设优先 > 启发式"链 — 预设表精确数据可信度高于命名猜测,优先短路)。
|
||||
|
||||
use std::sync::OnceLock;
|
||||
use df_ai_core::model::{ModelConfig, ProbeSource};
|
||||
|
||||
use df_ai_core::model::{
|
||||
Capability, CostTier, IntelligenceTier, Modality, ModelConfig, ProbeSource,
|
||||
};
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 预设表:编译期嵌入(include_str! 相对 crate 根),零运行时文件依赖
|
||||
// ────────────────────────────────────────────────────────────
|
||||
|
||||
/// 预设表原始 JSON(编译期从 `crates/df-ai/presets/models.json` 嵌入)。
|
||||
const PRESETS_JSON: &str = include_str!("../presets/models.json");
|
||||
|
||||
/// 解析后的预设表(进程内单例,首次访问惰性解析一次)。
|
||||
fn presets() -> &'static [ModelConfig] {
|
||||
static PRESETS: OnceLock<Vec<ModelConfig>> = OnceLock::new();
|
||||
PRESETS.get_or_init(|| {
|
||||
// include_str! 内容由仓库控制,解析失败属编译期/仓库错误,panic 合理。
|
||||
serde_json::from_str::<Vec<ModelConfig>>(PRESETS_JSON)
|
||||
.expect("presets/models.json 解析失败 — 检查 JSON 格式与 ModelConfig serde 映射")
|
||||
})
|
||||
}
|
||||
// 纯逻辑子模块(预设表加载 / 启发式推断 / 词素判定)抽离至此 —
|
||||
// private mod,crate 内复用,外部经 model_probe::probe 间接访问(对齐 model_fetch_helpers)。
|
||||
use crate::model_probe_helpers::{heuristic_infer, presets};
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 公共入口
|
||||
@@ -79,95 +62,6 @@ pub fn probe(model_id: &str) -> ModelConfig {
|
||||
heuristic
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 启发式推断(模型名命名模式 → 4 维度)
|
||||
// ────────────────────────────────────────────────────────────
|
||||
|
||||
/// 模型名启发式推断。仅推断功能性维度(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
|
||||
/// - `code`/`coder` → capabilities 加 CodeGen
|
||||
/// - 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();
|
||||
// 中性默认:不靠模型名猜档位(B-260618-04)
|
||||
let cost_tier = CostTier::Medium;
|
||||
let intelligence = IntelligenceTier::Standard;
|
||||
|
||||
// —— Embedding 优先判定(改变模态集合,且通常独占) ——
|
||||
// 规则:含 embed / embedding / 以 "e-" / "embedding-" 起首
|
||||
let is_embedding = name.contains("embed")
|
||||
|| name.contains("embedding")
|
||||
|| name.starts_with("e-")
|
||||
|| name.starts_with("embedding-")
|
||||
|| name.starts_with("text-embedding");
|
||||
|
||||
if is_embedding {
|
||||
capabilities.push(Capability::Embedding);
|
||||
// embedding 模型不走对话模态,modalities 留空(设计 §4.4 embedding-3 例:modalities=[])
|
||||
return ModelConfig {
|
||||
model_id: model_id.to_string(),
|
||||
enabled: true,
|
||||
label: None,
|
||||
modalities,
|
||||
capabilities,
|
||||
cost_tier,
|
||||
intelligence,
|
||||
weight: 50,
|
||||
context_window: 8192,
|
||||
probe_source: None,
|
||||
};
|
||||
}
|
||||
|
||||
// —— 对话模型默认 Text 模态 ——
|
||||
modalities.push(Modality::Text);
|
||||
|
||||
// —— Vision 词素:含 "v"(独立词素:glm-4v / qwen-vl)、"vision"、"vl" ——
|
||||
// 注:裸 "v" 子串误伤大(如 "review"),故仅在边界词素命中:
|
||||
// 以 "v" 结尾、含 "-v" / "v-" 分隔、含 "vl" / "vision"
|
||||
if has_vision_token(&name) {
|
||||
modalities.push(Modality::Vision);
|
||||
}
|
||||
|
||||
// —— CodeGen:含 code / coder ——
|
||||
if name.contains("code") || name.contains("coder") {
|
||||
capabilities.push(Capability::CodeGen);
|
||||
}
|
||||
|
||||
// —— 对话模型默认 ToolUse(主流支持 function calling) ——
|
||||
capabilities.push(Capability::ToolUse);
|
||||
|
||||
ModelConfig {
|
||||
model_id: model_id.to_string(),
|
||||
enabled: true,
|
||||
label: None,
|
||||
modalities,
|
||||
capabilities,
|
||||
cost_tier,
|
||||
intelligence,
|
||||
weight: 50,
|
||||
context_window: 8192,
|
||||
probe_source: None,
|
||||
}
|
||||
}
|
||||
|
||||
// —— 词素判定助手(命名约定式启发式,集中维护便于增删) ——
|
||||
|
||||
/// Vision 词素:`-v`(尾缀如 glm-4v)、以 `v` 结尾、含 `vl` / `vision`。
|
||||
/// 不直接 `contains('v')`(避免误伤 review/verbal 等)。
|
||||
fn has_vision_token(name: &str) -> bool {
|
||||
name.contains("vision") || name.contains("vl") || name.ends_with('v') || name.contains("-v")
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 测试
|
||||
// ────────────────────────────────────────────────────────────
|
||||
|
||||
119
crates/df-ai/src/model_probe_helpers.rs
Normal file
119
crates/df-ai/src/model_probe_helpers.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
//! 模型探测器 — 纯逻辑子模块(F-01 阶段2)
|
||||
//!
|
||||
//! 从 `model_probe.rs` 抽离的纯函数实现(预设表加载 / 启发式推断 / 词素判定)。
|
||||
//! 无 IO、无外部状态,crate 内经 `model_probe::probe` 间接复用 —
|
||||
//! 对齐 `model_fetch_helpers` 拆分先例(private mod,外部零直接路径需求)。
|
||||
//!
|
||||
//! 设计来源:docs/02-架构设计/F-01-模型能力系统与智能路由设计-2026-06-16.md §4
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use df_ai_core::model::{
|
||||
Capability, CostTier, IntelligenceTier, Modality, ModelConfig,
|
||||
};
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 预设表:编译期嵌入(include_str! 相对 crate 根),零运行时文件依赖
|
||||
// ────────────────────────────────────────────────────────────
|
||||
|
||||
/// 预设表原始 JSON(编译期从 `crates/df-ai/presets/models.json` 嵌入)。
|
||||
pub(super) const PRESETS_JSON: &str = include_str!("../presets/models.json");
|
||||
|
||||
/// 解析后的预设表(进程内单例,首次访问惰性解析一次)。
|
||||
pub(super) fn presets() -> &'static [ModelConfig] {
|
||||
static PRESETS: OnceLock<Vec<ModelConfig>> = OnceLock::new();
|
||||
PRESETS.get_or_init(|| {
|
||||
// include_str! 内容由仓库控制,解析失败属编译期/仓库错误,panic 合理。
|
||||
serde_json::from_str::<Vec<ModelConfig>>(PRESETS_JSON)
|
||||
.expect("presets/models.json 解析失败 — 检查 JSON 格式与 ModelConfig serde 映射")
|
||||
})
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────
|
||||
// 启发式推断(模型名命名模式 → 4 维度)
|
||||
// ────────────────────────────────────────────────────────────
|
||||
|
||||
/// 模型名启发式推断。仅推断功能性维度(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
|
||||
/// - `code`/`coder` → capabilities 加 CodeGen
|
||||
/// - cost_tier / intelligence → 中性默认 Medium / Standard(不靠名字猜)
|
||||
pub(super) 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();
|
||||
// 中性默认:不靠模型名猜档位(B-260618-04)
|
||||
let cost_tier = CostTier::Medium;
|
||||
let intelligence = IntelligenceTier::Standard;
|
||||
|
||||
// —— Embedding 优先判定(改变模态集合,且通常独占) ——
|
||||
// 规则:含 embed / embedding / 以 "e-" / "embedding-" 起首
|
||||
let is_embedding = name.contains("embed")
|
||||
|| name.contains("embedding")
|
||||
|| name.starts_with("e-")
|
||||
|| name.starts_with("embedding-")
|
||||
|| name.starts_with("text-embedding");
|
||||
|
||||
if is_embedding {
|
||||
capabilities.push(Capability::Embedding);
|
||||
// embedding 模型不走对话模态,modalities 留空(设计 §4.4 embedding-3 例:modalities=[])
|
||||
return ModelConfig {
|
||||
model_id: model_id.to_string(),
|
||||
enabled: true,
|
||||
label: None,
|
||||
modalities,
|
||||
capabilities,
|
||||
cost_tier,
|
||||
intelligence,
|
||||
weight: 50,
|
||||
context_window: 8192,
|
||||
probe_source: None,
|
||||
};
|
||||
}
|
||||
|
||||
// —— 对话模型默认 Text 模态 ——
|
||||
modalities.push(Modality::Text);
|
||||
|
||||
// —— Vision 词素:含 "v"(独立词素:glm-4v / qwen-vl)、"vision"、"vl" ——
|
||||
// 注:裸 "v" 子串误伤大(如 "review"),故仅在边界词素命中:
|
||||
// 以 "v" 结尾、含 "-v" / "v-" 分隔、含 "vl" / "vision"
|
||||
if has_vision_token(&name) {
|
||||
modalities.push(Modality::Vision);
|
||||
}
|
||||
|
||||
// —— CodeGen:含 code / coder ——
|
||||
if name.contains("code") || name.contains("coder") {
|
||||
capabilities.push(Capability::CodeGen);
|
||||
}
|
||||
|
||||
// —— 对话模型默认 ToolUse(主流支持 function calling) ——
|
||||
capabilities.push(Capability::ToolUse);
|
||||
|
||||
ModelConfig {
|
||||
model_id: model_id.to_string(),
|
||||
enabled: true,
|
||||
label: None,
|
||||
modalities,
|
||||
capabilities,
|
||||
cost_tier,
|
||||
intelligence,
|
||||
weight: 50,
|
||||
context_window: 8192,
|
||||
probe_source: None,
|
||||
}
|
||||
}
|
||||
|
||||
// —— 词素判定助手(命名约定式启发式,集中维护便于增删) ——
|
||||
|
||||
/// Vision 词素:`-v`(尾缀如 glm-4v)、以 `v` 结尾、含 `vl` / `vision`。
|
||||
/// 不直接 `contains('v')`(避免误伤 review/verbal 等)。
|
||||
pub(super) fn has_vision_token(name: &str) -> bool {
|
||||
name.contains("vision") || name.contains("vl") || name.ends_with('v') || name.contains("-v")
|
||||
}
|
||||
Reference in New Issue
Block a user