fix(mapping): 对齐全局模型映射的正则匹配行为与范围 (#296)

- scheduler_core: `matches_model_mapping` 改为大小写不敏感且整串匹配,并补充单测
- global model routing 预览:
  - Key 过滤增加 `allowed_models + model_mappings` 校验
  - `all_keys_whitelist` 改为收集全站活跃 Provider 的活跃 Key 白名单
- provider mapping-preview:
  - 优先使用 admin 全量 GlobalModel(含非激活)参与映射
  - admin 数据为空时回退 public 模型,保持兼容
- public models 匹配逻辑统一复用 scheduler_core 实现,避免行为分叉
- 更新网关测试,覆盖未关联 Provider 的 Key 也进入 whitelist 的场景
This commit is contained in:
AAEE86
2026-04-14 21:58:52 +08:00
committed by GitHub
parent 47bf1d04a1
commit fb31928e44
5 changed files with 210 additions and 36 deletions

View File

@@ -619,8 +619,16 @@ async fn gateway_handles_admin_global_model_routing_locally_with_trusted_admin_p
None,
None,
);
let unlinked_provider = sample_provider("provider-unlinked", "shadow", 30).with_billing_fields(
Some("quota".to_string()),
Some(30.0),
Some(6.0),
None,
None,
None,
);
let provider_catalog_repository = Arc::new(InMemoryProviderCatalogReadRepository::seed(
vec![openai_provider, alt_provider],
vec![openai_provider, alt_provider, unlinked_provider],
vec![
sample_endpoint(
"endpoint-openai-chat",
@@ -634,6 +642,12 @@ async fn gateway_handles_admin_global_model_routing_locally_with_trusted_admin_p
"openai:chat",
"https://api.alt.example",
),
sample_endpoint(
"endpoint-unlinked-chat",
"provider-unlinked",
"openai:chat",
"https://api.shadow.example",
),
],
{
let mut primary_key = sample_key(
@@ -668,7 +682,17 @@ async fn gateway_handles_admin_global_model_routing_locally_with_trusted_admin_p
mapped_key.allowed_models = Some(json!(["gpt-5-upstream"]));
mapped_key.rpm_limit = Some(120);
vec![primary_key, mapped_key]
let mut unlinked_key = sample_key(
"key-unlinked-routing",
"provider-unlinked",
"openai:chat",
"sk-unlinked-routing-9999",
);
unlinked_key.name = "unlinked".to_string();
unlinked_key.internal_priority = 40;
unlinked_key.allowed_models = Some(json!(["gpt-5-upstream-shadow"]));
vec![primary_key, mapped_key, unlinked_key]
},
));
let global_model_repository = Arc::new(
@@ -774,13 +798,16 @@ async fn gateway_handles_admin_global_model_routing_locally_with_trusted_admin_p
let whitelist = payload["all_keys_whitelist"]
.as_array()
.expect("whitelist array");
assert_eq!(whitelist.len(), 2);
assert_eq!(whitelist.len(), 3);
assert!(whitelist
.iter()
.any(|item| item["key_id"] == "key-openai-routing"));
assert!(whitelist
.iter()
.any(|item| item["key_id"] == "key-alt-routing"));
assert!(whitelist
.iter()
.any(|item| item["key_id"] == "key-unlinked-routing"));
assert_eq!(*upstream_hits.lock().expect("mutex should lock"), 0);
gateway_handle.abort();