fix: retire freefirst fast alias
This commit is contained in:
@@ -326,8 +326,9 @@ impl EffectiveAiRuntimePolicy {
|
||||
requested_provider: Option<&str>,
|
||||
requested_model_id: Option<&str>,
|
||||
) -> Result<AiRuntimeResolvedModel, String> {
|
||||
let default_ref = normalize_model_ref(None, &self.default_model)
|
||||
.unwrap_or_else(|| normalize_model_ref(None, DEFAULT_PI_MODEL).expect("default model"));
|
||||
let default_ref = canonical_ai_model_ref(None, &self.default_model).unwrap_or_else(|| {
|
||||
canonical_ai_model_ref(None, DEFAULT_PI_MODEL).expect("default model")
|
||||
});
|
||||
let default_provider = default_ref
|
||||
.split_once('/')
|
||||
.map(|(provider, _)| provider.to_string())
|
||||
@@ -340,7 +341,7 @@ impl EffectiveAiRuntimePolicy {
|
||||
.filter(|value| !value.is_empty());
|
||||
let model_ref = if let Some(model_id) = requested_model_id {
|
||||
let normalized =
|
||||
normalize_model_ref(requested_provider.or(Some(&default_provider)), model_id)
|
||||
canonical_ai_model_ref(requested_provider.or(Some(&default_provider)), model_id)
|
||||
.ok_or_else(|| "请求模型为空".to_string())?;
|
||||
if let (Some(provider), Some((model_provider, _))) =
|
||||
(requested_provider, normalized.split_once('/'))
|
||||
@@ -358,7 +359,7 @@ impl EffectiveAiRuntimePolicy {
|
||||
let allowed = self
|
||||
.allowed_models
|
||||
.iter()
|
||||
.filter_map(|value| normalize_model_ref(Some(&default_provider), value))
|
||||
.filter_map(|value| canonical_ai_model_ref(Some(&default_provider), value))
|
||||
.any(|value| value == model_ref);
|
||||
if !allowed {
|
||||
return Err(format!("模型 {model_ref} 不在当前 AI 设置允许范围内"));
|
||||
@@ -1180,6 +1181,7 @@ pub async fn admin_put_user_settings(
|
||||
|
||||
const DEFAULT_PI_MODEL: &str = "omniroute/gpt-5.4-mini";
|
||||
const FREEFIRST_PI_MODEL: &str = "omniroute/freefirst";
|
||||
const RETIRED_FREEFIRST_FAST_MODEL: &str = "omniroute/freefirst-fast";
|
||||
const LIGHTRAG_PROVIDER: &str = "lightrag";
|
||||
const LIGHTRAG_PROVIDER_DESCRIPTION: &str = "MNote 知识库默认提供者(LightRAG)";
|
||||
const SOURCE_OF_TRUTH: &str = "directory_grants";
|
||||
@@ -1820,17 +1822,20 @@ fn merge_effective_user_policy(global_policy: &Value, user_policy: &Value) -> Va
|
||||
}
|
||||
|
||||
fn effective_default_model(model_policy: &Value) -> String {
|
||||
model_policy
|
||||
let configured = model_policy
|
||||
.get("defaultModel")
|
||||
.or_else(|| model_policy.get("default_model"))
|
||||
.and_then(Value::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or(DEFAULT_PI_MODEL)
|
||||
.to_string()
|
||||
.to_string();
|
||||
canonical_ai_model_ref(None, &configured).unwrap_or(configured)
|
||||
}
|
||||
|
||||
fn effective_models(model_policy: &Value, default_model: &str) -> Vec<ModelEntry> {
|
||||
let default_model =
|
||||
canonical_ai_model_ref(None, default_model).unwrap_or_else(|| DEFAULT_PI_MODEL.to_string());
|
||||
let mut model_ids = model_policy
|
||||
.get("allowedModels")
|
||||
.or_else(|| model_policy.get("allowed_models"))
|
||||
@@ -1838,14 +1843,18 @@ fn effective_models(model_policy: &Value, default_model: &str) -> Vec<ModelEntry
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(Value::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
.filter_map(|value| canonical_ai_model_ref(None, value))
|
||||
.collect::<Vec<_>>();
|
||||
if !model_ids.iter().any(|value| value == default_model) {
|
||||
model_ids.insert(0, default_model.to_string());
|
||||
if !model_ids.iter().any(|value| value == &default_model) {
|
||||
model_ids.insert(0, default_model.clone());
|
||||
}
|
||||
model_ids
|
||||
let mut deduped = Vec::with_capacity(model_ids.len());
|
||||
for model_id in model_ids {
|
||||
if !deduped.iter().any(|value| value == &model_id) {
|
||||
deduped.push(model_id);
|
||||
}
|
||||
}
|
||||
deduped
|
||||
.into_iter()
|
||||
.map(|id| {
|
||||
let name = id.clone();
|
||||
@@ -1880,6 +1889,14 @@ fn normalize_model_ref(default_provider: Option<&str>, value: &str) -> Option<St
|
||||
Some(format!("{provider}/{trimmed}"))
|
||||
}
|
||||
|
||||
fn canonical_ai_model_ref(default_provider: Option<&str>, value: &str) -> Option<String> {
|
||||
let normalized = normalize_model_ref(default_provider, value)?;
|
||||
if normalized == RETIRED_FREEFIRST_FAST_MODEL {
|
||||
return Some(FREEFIRST_PI_MODEL.to_string());
|
||||
}
|
||||
Some(normalized)
|
||||
}
|
||||
|
||||
fn pi_mcp_extension_bridge_enabled() -> bool {
|
||||
std::env::var("MNOTE_PAGE_AI_PI_MCP_EXTENSION")
|
||||
.ok()
|
||||
@@ -2902,6 +2919,22 @@ mod tests {
|
||||
assert!(models[0].is_default);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effective_models_canonicalize_retired_freefirst_fast_alias() {
|
||||
let policy = json!({
|
||||
"defaultModel": "omniroute/freefirst-fast",
|
||||
"allowedModels": ["omniroute/freefirst-fast", "omniroute/freefirst", "omniroute/gpt-5.4-mini"]
|
||||
});
|
||||
assert_eq!(effective_default_model(&policy), "omniroute/freefirst");
|
||||
let models = effective_models(&policy, "omniroute/freefirst-fast");
|
||||
let ids = models
|
||||
.iter()
|
||||
.map(|model| model.id.as_str())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(ids, vec!["omniroute/freefirst", "omniroute/gpt-5.4-mini"]);
|
||||
assert!(models[0].is_default);
|
||||
}
|
||||
|
||||
// ─── Admin body validation ────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -105,7 +105,7 @@ async function main() {
|
||||
await page.locator("#ai-admin-models.is-active").waitFor();
|
||||
const modelPanel = page.locator("#ai-admin-models");
|
||||
await modelPanel.locator('[data-field="allowedModels"]').fill(
|
||||
"omniroute/freefirst, omniroute/freefirst-fast",
|
||||
"omniroute/freefirst, omniroute/gpt-5.4-mini",
|
||||
);
|
||||
await modelPanel.locator('[data-field="secretRef"]').fill("env://OMNIROUTE_API_KEY");
|
||||
await modelPanel.getByRole("button", { name: "保存配置" }).click();
|
||||
@@ -250,7 +250,7 @@ async function main() {
|
||||
});
|
||||
assert.equal(effective.defaultModel, "omniroute/freefirst");
|
||||
assert(
|
||||
(effective.models || []).some((model) => model.id === "omniroute/freefirst-fast"),
|
||||
(effective.models || []).some((model) => model.id === "omniroute/gpt-5.4-mini"),
|
||||
"effective models 应包含管理员允许的模型",
|
||||
);
|
||||
assert(
|
||||
|
||||
Reference in New Issue
Block a user