///|
/// ProviderCapability Registry — SDK 层 Provider 注册表
///
/// 将所有 Provider 的 capability() + 6 个转换函数集中管理。
/// 新增 Provider 只需在 `ALL_PROVIDERS` 追加一条,无需改动 dispatch 逻辑。
///
/// 匹配规则(`match_provider_name`):
/// 1. 精确匹配 provider_name(如 "anthropic" → "anthropic" 条目)
/// 2. 匹配 alias 列表(如 "google" → "gemini" 条目,"google-vertex" → "gemini-vertex" 条目)
/// 3. 若未匹配,通过 model_name 正则匹配 model_pattern(如 "gpt-4o-codex" → "openai-codex")
///|
/// 单个 Provider 注册条目
pub struct ProviderRegistration {
/// 主名称(如 "openai" "anthropic")
name : String
/// 别名列表(如 "google" "openai-responses")
aliases : Array[String]
/// 能力声明
capability : @lux.ProviderCapability
/// model pattern(从 capability 提取,用于文本通配符匹配)
model_pattern : String
/// 请求解码:provider JSON → LucentRequest(带诊断)
request_decode : (String) -> Result[
@lux.ConversionResult[@lux.LucentRequest],
String,
]
/// 请求编码:LucentRequest → provider JSON(带诊断)
request_encode : (@lux.LucentRequest) -> Result[
@lux.ConversionResult[String],
String,
]
/// 响应解码:provider JSON → LucentResponse(带诊断)
response_decode : (String) -> Result[
@lux.ConversionResult[@lux.LucentResponse],
String,
]
/// 响应编码:LucentResponse → provider JSON(带诊断)
response_encode : (@lux.LucentResponse) -> Result[
@lux.ConversionResult[String],
String,
]
/// 流式解码:SSE 文本 → StreamEvent 数组(带诊断)
events_decode : (String) -> Result[
@lux.ConversionResult[Array[@lux.LucentStreamEvent]],
String,
]
/// 流式编码:StreamEvent 数组 → SSE 文本(带诊断)
events_encode : (Array[@lux.LucentStreamEvent]) -> Result[
@lux.ConversionResult[String],
String,
]
}
///|
pub fn ProviderRegistration::new(
name : String,
aliases : Array[String],
capability : @lux.ProviderCapability,
model_pattern : String,
request_decode : (String) -> Result[
@lux.ConversionResult[@lux.LucentRequest],
String,
],
request_encode : (@lux.LucentRequest) -> Result[
@lux.ConversionResult[String],
String,
],
response_decode : (String) -> Result[
@lux.ConversionResult[@lux.LucentResponse],
String,
],
response_encode : (@lux.LucentResponse) -> Result[
@lux.ConversionResult[String],
String,
],
events_decode : (String) -> Result[
@lux.ConversionResult[Array[@lux.LucentStreamEvent]],
String,
],
events_encode : (Array[@lux.LucentStreamEvent]) -> Result[
@lux.ConversionResult[String],
String,
],
) -> ProviderRegistration {
{
name,
aliases,
capability,
model_pattern,
request_decode,
request_encode,
response_decode,
response_encode,
events_decode,
events_encode,
}
}
///|
/// 所有已注册的 Provider
///
/// 新增 Provider 只需在 `build_providers` 中追加。
/// 注意:条目顺序影响匹配优先级(精确匹配优先于正则匹配)。
///
/// 每次调用返回独立数组,调用方可安全修改;内部匹配路径改用
/// `providers_cache` 避免重复构造。
pub fn all_providers() -> Array[ProviderRegistration] {
build_providers()
}
///|
/// 注册表缓存 — 构造一次复用
///
/// `build_providers` 会为每个条目调用 `capability()`,其中包含数组分配;
/// 匹配函数(`match_provider_name` / `match_by_model` / `list_provider_names`)
/// 属于每次 SDK 调用都会经过的热路径,故在模块初始化时构造一次。
/// 仅供只读使用。
let providers_cache : Array[ProviderRegistration] = build_providers()
///|
/// 注册表构造 — 唯一的 Provider 清单来源
fn build_providers() -> Array[ProviderRegistration] {
[
///| OpenAI Responses(主)
ProviderRegistration::new(
"openai",
["openai-responses", "oai-responses"],
@openai_responses.capability(),
"gpt-*,o*",
@openai_responses.openai_responses_to_lux_request,
@openai_responses.lux_request_to_openai_responses,
@openai_responses.openai_responses_to_lux_response,
@openai_responses.lux_response_to_openai_responses,
@openai_responses.openai_responses_sse_to_events,
@openai_responses.lux_events_to_openai_responses_sse,
),
///| OpenAI Chat Completions
ProviderRegistration::new(
"openai-chat",
["openai_chat", "oai-chat", "chat"],
@openai_chat.capability(),
"gpt-*,o*",
@openai_chat.openai_chat_to_lux_request,
@openai_chat.lux_request_to_openai_chat,
@openai_chat.openai_chat_to_lux_response,
@openai_chat.lux_response_to_openai_chat,
@openai_chat.openai_chat_sse_to_events,
@openai_chat.lux_events_to_openai_chat_sse,
),
///| Anthropic Messages
ProviderRegistration::new(
"anthropic",
["claude", "claude-messages"],
@anthropic.capability(),
"claude-*",
@anthropic.anthropic_to_lux_request,
@anthropic.lux_request_to_anthropic,
@anthropic.anthropic_to_lux_response,
@anthropic.lux_response_to_anthropic,
@anthropic.anthropic_sse_to_events,
@anthropic.lux_events_to_anthropic_sse,
),
///| Google Gemini(REST API)
ProviderRegistration::new(
"gemini",
["google", "google-gemini"],
@gemini.capability(),
"gemini-*",
@gemini.gemini_to_lux_request,
@gemini.lux_request_to_gemini,
@gemini.gemini_to_lux_response,
@gemini.lux_response_to_gemini,
@gemini.gemini_sse_to_events,
@gemini.lux_events_to_gemini_sse,
),
///| Google Vertex AI Gemini
ProviderRegistration::new(
"google-vertex",
["vertex", "gemini-vertex"],
@gemini_vertex.capability(),
"gemini-*",
@gemini_vertex.vertex_to_lux_request,
@gemini_vertex.lux_request_to_vertex,
@gemini_vertex.vertex_to_lux_response,
@gemini_vertex.lux_response_to_vertex,
@gemini_vertex.vertex_sse_to_events,
@gemini_vertex.lux_events_to_vertex_sse,
),
///| Azure OpenAI Responses
ProviderRegistration::new(
"azure-openai",
["azure", "azure-openai-responses"],
@openai_azure.capability(),
"azure-*",
@openai_azure.azure_to_lux_request,
@openai_azure.lux_request_to_azure,
@openai_azure.azure_to_lux_response,
@openai_azure.lux_response_to_azure,
@openai_azure.azure_sse_to_events,
@openai_azure.lux_events_to_azure_sse,
),
///| OpenAI Codex(Responses 子协议)
ProviderRegistration::new(
"openai-codex",
["codex", "gpt-codex"],
@openai_codex.capability(),
"gpt-*-codex",
@openai_codex.codex_to_lux_request,
@openai_codex.lux_request_to_codex,
@openai_codex.codex_to_lux_response,
@openai_codex.lux_response_to_codex,
@openai_codex.codex_sse_to_events,
@openai_codex.lux_events_to_codex_sse,
),
///| OpenAI vLLM(Chat 兼容子协议,承载 message.reasoning / delta.reasoning)
ProviderRegistration::new(
"openai-vllm",
["vllm", "vllm-chat"],
@openai_vllm.capability(),
"qwen-*,deepseek-*,glm-*,yi-*",
@openai_vllm.vllm_to_lux_request,
@openai_vllm.lux_request_to_vllm,
@openai_vllm.vllm_to_lux_response,
@openai_vllm.lux_response_to_vllm,
@openai_vllm.vllm_sse_to_events,
@openai_vllm.lux_events_to_vllm_sse,
),
]
}
///|
/// 根据名称匹配 Provider
///
/// 匹配策略:
/// 1. 精确匹配 name
/// 2. 匹配 aliases 中的别名
/// 3. 均未命中时,把输入当作模型名走 `match_by_model` 回退
///
/// 若只想按模型名匹配(不做名称/别名命中),直接调用 `match_by_model`。
pub fn match_provider_name(provider_name : String) -> ProviderRegistration? {
let providers = providers_cache
let mut i = 0
while i < providers.length() {
let reg = providers[i]
// 1. 精确匹配
if reg.name == provider_name {
return Some(reg)
}
// 2. 别名匹配
let mut j = 0
while j < reg.aliases.length() {
if reg.aliases[j] == provider_name {
return Some(reg)
}
j = j + 1
}
i = i + 1
}
// 3. 未命中名称时,把输入当作模型名匹配最具体的注册项
match_by_model(provider_name)
}
///|
/// 根据 model_name 匹配 Provider(通过 model_pattern 子串匹配)
///
/// 返回最具体的匹配(pattern 中 * 越多越优先),或 None。
pub fn match_by_model(model_name : String) -> ProviderRegistration? {
let providers = providers_cache
let mut best : ProviderRegistration? = None
let mut best_score = 0
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
}
///|
/// 朴素 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)
}
///|
/// 获取所有 Provider 的主名称列表
pub fn list_provider_names() -> Array[String] {
let providers = providers_cache
let result : Array[String] = []
let mut i = 0
while i < providers.length() {
result.push(providers[i].name)
i = i + 1
}
result
}