///|
/// ═══ 功能组:registry+match(注册与路由)═══
/// 本文件:路由匹配(match_provider_name / match_by_model)。
/// 注册表定义见 registry.mbt。
///|
/// match.mbt — Provider 名称/模型模式匹配
///
/// 从 provider_capability.mbt 拆分,职责单一:字符串匹配逻辑。
/// Go 最佳实践:小文件、单一职责,匹配逻辑与注册表解耦。
///
/// T6:匹配叠加动态注册表(register_provider 注册的部署态条目)——
/// 静态表优先、动态表兜底;无动态注册时行为与此前完全一致。
///|
/// 在给定注册数组中按名称匹配(精确主名 → 别名)
fn match_name_in(
providers : Array[ProviderRegistration],
provider_key : String,
) -> ProviderRegistration? {
let mut i = 0
while i < providers.length() {
let reg = providers[i]
// 1. 精确匹配主名称
if reg.name == provider_key {
return Some(reg)
}
// 2. 匹配别名
let mut j = 0
while j < reg.aliases.length() {
if reg.aliases[j] == provider_key {
return Some(reg)
}
j = j + 1
}
i = i + 1
}
None
}
///|
/// 根据名称匹配 Provider
///
/// 匹配策略:
/// 1. 静态表精确匹配 name / 别名
/// 2. 动态表(register_provider)精确匹配 name / 别名
/// 3. 均未命中时,把输入当作模型名走 `match_by_model` 回退
///
/// 若只想按模型名匹配,直接调用 `match_by_model`。
pub fn match_provider_name(provider_name : String) -> ProviderRegistration? {
let provider_key = provider_name.trim().to_owned()
// 1+2. 静态表优先,动态表兜底
match match_name_in(providers_cache, provider_key) {
Some(reg) => Some(reg)
None =>
match match_name_in(dynamic_providers, provider_key) {
Some(reg) => Some(reg)
// 3. 未命中名称时,把输入当作模型名匹配最具体的注册项
None => match_by_model(provider_key)
}
}
}
///|
/// 根据 model_name 匹配 Provider(通过 model_pattern 子串匹配)
///
/// 返回最具体的匹配(pattern 中 * 越多越优先),或 None。
/// 静态表与动态表联合评分:特异性严格大于才替换(同分静态优先)。
pub fn match_by_model(model_name : String) -> ProviderRegistration? {
let (best_static, score_static) = scan_by_model(
providers_cache,
model_name,
None,
0,
)
let (best_final, _) = scan_by_model(
dynamic_providers, model_name, best_static, score_static,
)
best_final
}
///|
/// 扫描一张表按 model_pattern 评分,返回(最优项, 最优分)
fn scan_by_model(
providers : Array[ProviderRegistration],
model_name : String,
initial_best : ProviderRegistration?,
initial_score : Int,
) -> (ProviderRegistration?, Int) {
let mut best = initial_best
let mut best_score = initial_score
let mut i = 0
while i < providers.length() {
let reg = providers[i]
let score = pattern_specificity(reg.model_pattern)
if score > 0 &&
score > best_score &&
model_pattern_matches(reg.model_pattern, model_name) {
best = Some(reg)
best_score = score
}
i = i + 1
}
(best, best_score)
}
///|
/// 获取所有 Provider 的主名称列表(静态表 + 动态表,动态在后)
pub fn list_provider_names() -> Array[String] {
let result : Array[String] = []
for reg in providers_cache {
result.push(reg.name)
}
for reg in dynamic_providers {
result.push(reg.name)
}
result
}
///|
/// 朴素 model_pattern 匹配
///
/// 支持逗号分隔的多 pattern(如 "gpt-*,o*")。
/// 单 pattern 格式:
/// - "claude-*" → 前缀匹配 "claude-"
/// - "gpt-*" / "o*" → 前缀匹配
/// - "gpt-*-codex" → 前后缀匹配
///
/// 这是最简实现,未来可替换为完整 glob/regex 引擎。
fn model_pattern_matches(pattern : String, model_name : String) -> Bool {
let parts = pattern_parts(pattern)
let mut i = 0
while i < parts.length() {
if single_pattern_matches(parts[i], model_name) {
return true
}
i = i + 1
}
false
}
///|
/// 单个 pattern 的匹配(含通配符)
fn single_pattern_matches(pattern : String, model_name : String) -> Bool {
if !pattern.contains("*") {
return pattern == model_name
}
let star_pos = find_first_star(pattern)
let prefix = pattern[0:star_pos].to_owned()
let suffix = pattern[star_pos + 1:pattern.length()].to_owned()
if suffix.contains("*") {
let second_star = find_first_star(suffix)
let mid_end = substring(suffix, 0, second_star)
let final_suffix = substring(suffix, second_star + 1, suffix.length())
let prefix_ok = prefix.length() <= model_name.length() &&
substring(model_name, 0, prefix.length()) == prefix
let suffix_ok = final_suffix.length() <= model_name.length() &&
substring(
model_name,
model_name.length() - final_suffix.length(),
model_name.length(),
) ==
final_suffix
prefix_ok &&
mid_end != "" &&
contains_after(model_name, prefix, mid_end) &&
suffix_ok
} else if prefix.length() <= model_name.length() &&
suffix.length() <= model_name.length() &&
substring(model_name, 0, prefix.length()) == prefix &&
substring(
model_name,
model_name.length() - suffix.length(),
model_name.length(),
) ==
suffix {
true
} else {
false
}
}
///|
/// 逗号分隔 pattern 拆分为数组(如 "gpt-*,o*" -> ["gpt-*" "o*"])
fn pattern_parts(pattern : String) -> Array[String] {
let result : Array[String] = []
let mut pos = 0
while pos < pattern.length() {
let comma = find_comma(pattern, pos)
if comma == -1 {
result.push(pattern[pos:pattern.length()].to_owned())
pos = pattern.length()
} else {
result.push(pattern[pos:comma].to_owned())
pos = comma + 1
}
}
result
}
///|
fn find_comma(s : String, start : Int) -> Int {
let mut i = start
while i < s.length() {
if s[i] == ',' {
return i
}
i = i + 1
}
-1
}
///|
/// 返回 pattern 的特异性评分
///
/// 评分 = stars * 1000 + 长度(无通配符的字符数)
/// * 数量越多越具体;同 * 数量时,越长越具体
/// 例如:"gpt-*-codex" (1011) > "gpt-*" (1004) > "o*" (1002)
fn pattern_specificity(pattern : String) -> Int {
let parts = pattern_parts(pattern)
let mut best = 0
let mut i = 0
while i < parts.length() {
let count = count_star(parts[i])
let score = count * 1000 + parts[i].length()
if score > best {
best = score
}
i = i + 1
}
best
}
///|
/// 统计字符串中 * 的个数
fn count_star(s : String) -> Int {
let mut total = 0
let mut i = 0
while i < s.length() {
if s[i] == '*' {
total = total + 1
}
i = i + 1
}
total
}
///|
/// 查找第一个 '*' 的位置,未找到返回 -1
fn find_first_star(s : String) -> Int {
let mut i = 0
while i < s.length() {
if s[i] == '*' {
return i
}
i = i + 1
}
-1
}
///|
/// 提取子串
fn substring(s : String, start : Int, end : Int) -> String {
if start < 0 || end > s.length() || start > end {
return ""
}
s[start:end].to_owned()
}
///|
/// 检查 s 中 position 之后的子串中是否包含 needle
fn contains_after(s : String, skip_prefix : String, needle : String) -> Bool {
let rest = substring(s, skip_prefix.length(), s.length())
rest.contains(needle)
}