1
0
Files
nex/backend/internal/conversion/adapter.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

107 lines
3.5 KiB
Go

package conversion
import (
"fmt"
"sync"
"nex/backend/internal/conversion/canonical"
)
// ProtocolAdapter 协议适配器接口
type ProtocolAdapter interface {
ProtocolName() string
ProtocolVersion() string
SupportsPassthrough() bool
DetectInterfaceType(nativePath string) InterfaceType
BuildUrl(nativePath string, interfaceType InterfaceType) string
BuildHeaders(provider *TargetProvider) map[string]string
SupportsInterface(interfaceType InterfaceType) bool
DecodeRequest(raw []byte) (*canonical.CanonicalRequest, error)
EncodeRequest(req *canonical.CanonicalRequest, provider *TargetProvider) ([]byte, error)
DecodeResponse(raw []byte) (*canonical.CanonicalResponse, error)
EncodeResponse(resp *canonical.CanonicalResponse) ([]byte, error)
CreateStreamDecoder() StreamDecoder
CreateStreamEncoder() StreamEncoder
EncodeError(err *ConversionError) ([]byte, int)
DecodeModelsResponse(raw []byte) (*canonical.CanonicalModelList, error)
EncodeModelsResponse(list *canonical.CanonicalModelList) ([]byte, error)
DecodeModelInfoResponse(raw []byte) (*canonical.CanonicalModelInfo, error)
EncodeModelInfoResponse(info *canonical.CanonicalModelInfo) ([]byte, error)
DecodeEmbeddingRequest(raw []byte) (*canonical.CanonicalEmbeddingRequest, error)
EncodeEmbeddingRequest(req *canonical.CanonicalEmbeddingRequest, provider *TargetProvider) ([]byte, error)
DecodeEmbeddingResponse(raw []byte) (*canonical.CanonicalEmbeddingResponse, error)
EncodeEmbeddingResponse(resp *canonical.CanonicalEmbeddingResponse) ([]byte, error)
DecodeRerankRequest(raw []byte) (*canonical.CanonicalRerankRequest, error)
EncodeRerankRequest(req *canonical.CanonicalRerankRequest, provider *TargetProvider) ([]byte, error)
DecodeRerankResponse(raw []byte) (*canonical.CanonicalRerankResponse, error)
EncodeRerankResponse(resp *canonical.CanonicalRerankResponse) ([]byte, error)
// 统一模型 ID 相关方法
ExtractUnifiedModelID(nativePath string) (string, error)
ExtractModelName(body []byte, ifaceType InterfaceType) (string, error)
RewriteRequestModelName(body []byte, newModel string, ifaceType InterfaceType) ([]byte, error)
RewriteResponseModelName(body []byte, newModel string, ifaceType InterfaceType) ([]byte, error)
}
// AdapterRegistry 适配器注册表接口
type AdapterRegistry interface {
Register(adapter ProtocolAdapter) error
Get(protocolName string) (ProtocolAdapter, error)
ListProtocols() []string
}
// memoryRegistry 基于内存的适配器注册表
type memoryRegistry struct {
mu sync.RWMutex
adapters map[string]ProtocolAdapter
}
// NewMemoryRegistry 创建内存注册表
func NewMemoryRegistry() AdapterRegistry {
return &memoryRegistry{
adapters: make(map[string]ProtocolAdapter),
}
}
// Register 注册适配器
func (r *memoryRegistry) Register(adapter ProtocolAdapter) error {
r.mu.Lock()
defer r.mu.Unlock()
name := adapter.ProtocolName()
if _, exists := r.adapters[name]; exists {
return fmt.Errorf("适配器已注册: %s", name)
}
r.adapters[name] = adapter
return nil
}
// Get 获取适配器
func (r *memoryRegistry) Get(protocolName string) (ProtocolAdapter, error) {
r.mu.RLock()
defer r.mu.RUnlock()
adapter, ok := r.adapters[protocolName]
if !ok {
return nil, fmt.Errorf("未找到适配器: %s", protocolName)
}
return adapter, nil
}
// ListProtocols 列出所有已注册协议
func (r *memoryRegistry) ListProtocols() []string {
r.mu.RLock()
defer r.mu.RUnlock()
protocols := make([]string, 0, len(r.adapters))
for name := range r.adapters {
protocols = append(protocols, name)
}
return protocols
}