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