package anthropic import ( "encoding/json" "fmt" "strings" "nex/backend/internal/conversion" "nex/backend/internal/conversion/canonical" ) // Adapter Anthropic 协议适配器 type Adapter struct{} // NewAdapter 创建 Anthropic 适配器 func NewAdapter() *Adapter { return &Adapter{} } // ProtocolName 返回协议名称 func (a *Adapter) ProtocolName() string { return "anthropic" } // ProtocolVersion 返回协议版本 func (a *Adapter) ProtocolVersion() string { return "2023-06-01" } // SupportsPassthrough 支持同协议透传 func (a *Adapter) SupportsPassthrough() bool { return true } // DetectInterfaceType 根据路径检测接口类型 func (a *Adapter) DetectInterfaceType(nativePath string) conversion.InterfaceType { switch { case nativePath == "/v1/messages": return conversion.InterfaceTypeChat case nativePath == "/v1/models": return conversion.InterfaceTypeModels case isModelInfoPath(nativePath): return conversion.InterfaceTypeModelInfo default: return conversion.InterfaceTypePassthrough } } // isModelInfoPath 判断是否为模型详情路径(/v1/models/{id},允许 id 含 /) func isModelInfoPath(path string) bool { if !strings.HasPrefix(path, "/v1/models/") { return false } suffix := path[len("/v1/models/"):] return suffix != "" } // BuildUrl 根据接口类型构建 URL func (a *Adapter) BuildUrl(nativePath string, interfaceType conversion.InterfaceType) string { switch interfaceType { case conversion.InterfaceTypeChat: return "/v1/messages" case conversion.InterfaceTypeModels: return "/v1/models" default: return nativePath } } // BuildHeaders 构建请求头 func (a *Adapter) BuildHeaders(provider *conversion.TargetProvider) map[string]string { headers := map[string]string{ "x-api-key": provider.APIKey, "anthropic-version": "2023-06-01", "Content-Type": "application/json", } if v, ok := provider.AdapterConfig["anthropic_version"].(string); ok && v != "" { headers["anthropic-version"] = v } if betas, ok := provider.AdapterConfig["anthropic_beta"].([]string); ok && len(betas) > 0 { headers["anthropic-beta"] = strings.Join(betas, ",") } else if betas, ok := provider.AdapterConfig["anthropic_beta"].([]any); ok && len(betas) > 0 { var parts []string for _, b := range betas { if s, ok := b.(string); ok { parts = append(parts, s) } } if len(parts) > 0 { headers["anthropic-beta"] = strings.Join(parts, ",") } } return headers } // SupportsInterface 检查是否支持接口类型 func (a *Adapter) SupportsInterface(interfaceType conversion.InterfaceType) bool { switch interfaceType { case conversion.InterfaceTypeChat, conversion.InterfaceTypeModels, conversion.InterfaceTypeModelInfo: return true default: return false } } // DecodeRequest 解码请求 func (a *Adapter) DecodeRequest(raw []byte) (*canonical.CanonicalRequest, error) { return decodeRequest(raw) } // EncodeRequest 编码请求 func (a *Adapter) EncodeRequest(req *canonical.CanonicalRequest, provider *conversion.TargetProvider) ([]byte, error) { return encodeRequest(req, provider) } // DecodeResponse 解码响应 func (a *Adapter) DecodeResponse(raw []byte) (*canonical.CanonicalResponse, error) { return decodeResponse(raw) } // EncodeResponse 编码响应 func (a *Adapter) EncodeResponse(resp *canonical.CanonicalResponse) ([]byte, error) { return encodeResponse(resp) } // CreateStreamDecoder 创建流式解码器 func (a *Adapter) CreateStreamDecoder() conversion.StreamDecoder { return NewStreamDecoder() } // CreateStreamEncoder 创建流式编码器 func (a *Adapter) CreateStreamEncoder() conversion.StreamEncoder { return NewStreamEncoder() } // EncodeError 编码错误 func (a *Adapter) EncodeError(err *conversion.ConversionError) ([]byte, int) { errType := string(err.Code) statusCode := 500 errMsg := ErrorResponse{ Type: "error", Error: ErrorDetail{ Type: errType, Message: err.Message, }, } body, _ := json.Marshal(errMsg) return body, statusCode } // DecodeModelsResponse 解码模型列表响应 func (a *Adapter) DecodeModelsResponse(raw []byte) (*canonical.CanonicalModelList, error) { return decodeModelsResponse(raw) } // EncodeModelsResponse 编码模型列表响应 func (a *Adapter) EncodeModelsResponse(list *canonical.CanonicalModelList) ([]byte, error) { return encodeModelsResponse(list) } // DecodeModelInfoResponse 解码模型详情响应 func (a *Adapter) DecodeModelInfoResponse(raw []byte) (*canonical.CanonicalModelInfo, error) { return decodeModelInfoResponse(raw) } // EncodeModelInfoResponse 编码模型详情响应 func (a *Adapter) EncodeModelInfoResponse(info *canonical.CanonicalModelInfo) ([]byte, error) { return encodeModelInfoResponse(info) } // DecodeEmbeddingRequest Anthropic 不支持嵌入 func (a *Adapter) DecodeEmbeddingRequest(raw []byte) (*canonical.CanonicalEmbeddingRequest, error) { return nil, conversion.NewConversionError(conversion.ErrorCodeInterfaceNotSupported, "Anthropic 不支持 Embeddings 接口") } // EncodeEmbeddingRequest Anthropic 不支持嵌入 func (a *Adapter) EncodeEmbeddingRequest(req *canonical.CanonicalEmbeddingRequest, provider *conversion.TargetProvider) ([]byte, error) { return nil, conversion.NewConversionError(conversion.ErrorCodeInterfaceNotSupported, "Anthropic 不支持 Embeddings 接口") } // DecodeEmbeddingResponse Anthropic 不支持嵌入 func (a *Adapter) DecodeEmbeddingResponse(raw []byte) (*canonical.CanonicalEmbeddingResponse, error) { return nil, conversion.NewConversionError(conversion.ErrorCodeInterfaceNotSupported, "Anthropic 不支持 Embeddings 接口") } // EncodeEmbeddingResponse Anthropic 不支持嵌入 func (a *Adapter) EncodeEmbeddingResponse(resp *canonical.CanonicalEmbeddingResponse) ([]byte, error) { return nil, conversion.NewConversionError(conversion.ErrorCodeInterfaceNotSupported, "Anthropic 不支持 Embeddings 接口") } // DecodeRerankRequest Anthropic 不支持重排序 func (a *Adapter) DecodeRerankRequest(raw []byte) (*canonical.CanonicalRerankRequest, error) { return nil, conversion.NewConversionError(conversion.ErrorCodeInterfaceNotSupported, "Anthropic 不支持 Rerank 接口") } // EncodeRerankRequest Anthropic 不支持重排序 func (a *Adapter) EncodeRerankRequest(req *canonical.CanonicalRerankRequest, provider *conversion.TargetProvider) ([]byte, error) { return nil, conversion.NewConversionError(conversion.ErrorCodeInterfaceNotSupported, "Anthropic 不支持 Rerank 接口") } // DecodeRerankResponse Anthropic 不支持重排序 func (a *Adapter) DecodeRerankResponse(raw []byte) (*canonical.CanonicalRerankResponse, error) { return nil, conversion.NewConversionError(conversion.ErrorCodeInterfaceNotSupported, "Anthropic 不支持 Rerank 接口") } // EncodeRerankResponse Anthropic 不支持重排序 func (a *Adapter) EncodeRerankResponse(resp *canonical.CanonicalRerankResponse) ([]byte, error) { return nil, conversion.NewConversionError(conversion.ErrorCodeInterfaceNotSupported, "Anthropic 不支持 Rerank 接口") } // ExtractUnifiedModelID 从路径中提取统一模型 ID(/v1/models/{provider_id}/{model_name}) func (a *Adapter) ExtractUnifiedModelID(nativePath string) (string, error) { if !strings.HasPrefix(nativePath, "/v1/models/") { return "", fmt.Errorf("不是模型详情路径: %s", nativePath) } suffix := nativePath[len("/v1/models/"):] if suffix == "" { return "", fmt.Errorf("路径缺少模型 ID") } return suffix, nil } // locateModelFieldInRequest 定位请求体中 model 字段的值并提供改写函数 func locateModelFieldInRequest(body []byte, ifaceType conversion.InterfaceType) (string, func(string) ([]byte, error), error) { var m map[string]json.RawMessage if err := json.Unmarshal(body, &m); err != nil { return "", nil, err } switch ifaceType { case conversion.InterfaceTypeChat: raw, exists := m["model"] if !exists { return "", nil, fmt.Errorf("请求体中缺少 model 字段") } var current string if err := json.Unmarshal(raw, ¤t); err != nil { return "", nil, err } rewriteFunc := func(newModel string) ([]byte, error) { m["model"], _ = json.Marshal(newModel) return json.Marshal(m) } return current, rewriteFunc, nil default: return "", nil, fmt.Errorf("不支持的接口类型: %s", ifaceType) } } // ExtractModelName 从请求体中提取 model 值 func (a *Adapter) ExtractModelName(body []byte, ifaceType conversion.InterfaceType) (string, error) { model, _, err := locateModelFieldInRequest(body, ifaceType) return model, err } // RewriteRequestModelName 最小化改写请求体中的 model 字段 func (a *Adapter) RewriteRequestModelName(body []byte, newModel string, ifaceType conversion.InterfaceType) ([]byte, error) { _, rewriteFunc, err := locateModelFieldInRequest(body, ifaceType) if err != nil { return nil, err } return rewriteFunc(newModel) } // RewriteResponseModelName 最小化改写响应体中的 model 字段 func (a *Adapter) RewriteResponseModelName(body []byte, newModel string, ifaceType conversion.InterfaceType) ([]byte, error) { var m map[string]json.RawMessage if err := json.Unmarshal(body, &m); err != nil { return nil, err } switch ifaceType { case conversion.InterfaceTypeChat: // Chat 响应必须有 model 字段,存在则改写,不存在则添加 m["model"], _ = json.Marshal(newModel) return json.Marshal(m) default: return body, nil } }