1
0
Files
nex/backend/pkg/modelid/model_id.go
lanyuanxiaoyao 395887667d feat: 实现统一模型 ID 机制
实现统一模型 ID 格式 (provider_id/model_name),支持跨协议模型标识和 Smart Passthrough。

核心变更:
- 新增 pkg/modelid 包:解析、格式化、校验统一模型 ID
- 数据库迁移:models 表使用 UUID 主键 + UNIQUE(provider_id, model_name) 约束
- Repository 层:FindByProviderAndModelName、ListEnabled 方法
- Service 层:联合唯一校验、provider ID 字符集校验
- Conversion 层:ExtractModelName、RewriteRequestModelName/RewriteResponseModelName 方法
- Handler 层:统一模型 ID 路由、Smart Passthrough、Models API 本地聚合
- 新增 error-responses、unified-model-id 规范

测试覆盖:
- 单元测试:modelid、conversion、handler、service、repository
- 集成测试:统一模型 ID 路由、Smart Passthrough 保真性、跨协议转换
- 迁移测试:UUID 主键、UNIQUE 约束、级联删除

OpenSpec:
- 归档 unified-model-id 变更到 archive/2026-04-21-unified-model-id
- 同步 11 个 delta specs 到 main specs
- 新增 error-responses、unified-model-id 规范文件
2026-04-21 18:14:10 +08:00

64 lines
1.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package modelid
import (
"errors"
"regexp"
"strings"
)
var providerIDRegex = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
// ParseUnifiedModelID 将 "provider_id/model_name" 格式的字符串解析为 providerID 和 modelName
// 在第一个 "/" 处分割model_name 可以包含 "/"
func ParseUnifiedModelID(id string) (providerID, modelName string, err error) {
if id == "" {
return "", "", errors.New("统一模型 ID 不能为空")
}
parts := strings.SplitN(id, "/", 2)
if len(parts) != 2 {
return "", "", errors.New("统一模型 ID 格式错误,缺少分隔符 \"/\"")
}
providerID = parts[0]
modelName = parts[1]
if providerID == "" {
return "", "", errors.New("provider_id 不能为空")
}
if modelName == "" {
return "", "", errors.New("model_name 不能为空")
}
if !providerIDRegex.MatchString(providerID) {
return "", "", errors.New("provider_id 仅允许字母、数字、下划线")
}
return providerID, modelName, nil
}
// FormatUnifiedModelID 将 providerID 和 modelName 组合格式化为统一模型 ID
func FormatUnifiedModelID(providerID, modelName string) string {
return providerID + "/" + modelName
}
// ValidateProviderID 校验 providerID 仅包含字母、数字、下划线,长度 1-64
func ValidateProviderID(id string) error {
if id == "" {
return errors.New("provider_id 不能为空")
}
if len(id) > 64 {
return errors.New("provider_id 长度不能超过 64 个字符")
}
if !providerIDRegex.MatchString(id) {
return errors.New("provider_id 仅允许字母、数字、下划线")
}
return nil
}
// IsValidUnifiedModelID 判断字符串是否为合法的统一模型 ID 格式
func IsValidUnifiedModelID(id string) bool {
_, _, err := ParseUnifiedModelID(id)
return err == nil
}