package handler import ( "bufio" "fmt" "net/http" "github.com/gin-gonic/gin" "nex/backend/internal/config" "nex/backend/internal/protocol/anthropic" "nex/backend/internal/protocol/openai" "nex/backend/internal/provider" "nex/backend/internal/router" ) // AnthropicHandler Anthropic 协议处理器 type AnthropicHandler struct { client *provider.Client router *router.Router } // NewAnthropicHandler 创建 Anthropic 处理器 func NewAnthropicHandler() *AnthropicHandler { return &AnthropicHandler{ client: provider.NewClient(), router: router.NewRouter(), } } // HandleMessages 处理 Messages 请求 func (h *AnthropicHandler) HandleMessages(c *gin.Context) { // 解析 Anthropic 请求 var req anthropic.MessagesRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, anthropic.ErrorResponse{ Type: "error", Error: anthropic.ErrorDetail{ Type: "invalid_request_error", Message: "无效的请求格式: " + err.Error(), }, }) return } // 检查多模态内容 if err := h.checkMultimodalContent(&req); err != nil { c.JSON(http.StatusBadRequest, anthropic.ErrorResponse{ Type: "error", Error: anthropic.ErrorDetail{ Type: "invalid_request_error", Message: err.Error(), }, }) return } // 转换为 OpenAI 请求 openaiReq, err := anthropic.ConvertRequest(&req) if err != nil { c.JSON(http.StatusBadRequest, anthropic.ErrorResponse{ Type: "error", Error: anthropic.ErrorDetail{ Type: "invalid_request_error", Message: "请求转换失败: " + err.Error(), }, }) return } // 路由到供应商 routeResult, err := h.router.Route(openaiReq.Model) if err != nil { h.handleError(c, err) return } // 根据是否流式选择处理方式 if req.Stream { h.handleStreamRequest(c, openaiReq, routeResult) } else { h.handleNonStreamRequest(c, openaiReq, routeResult) } } // handleNonStreamRequest 处理非流式请求 func (h *AnthropicHandler) handleNonStreamRequest(c *gin.Context, openaiReq *openai.ChatCompletionRequest, routeResult *router.RouteResult) { // 发送请求到供应商 openaiResp, err := h.client.SendRequest(c.Request.Context(), openaiReq, routeResult.Provider.APIKey, routeResult.Provider.BaseURL) if err != nil { c.JSON(http.StatusInternalServerError, anthropic.ErrorResponse{ Type: "error", Error: anthropic.ErrorDetail{ Type: "api_error", Message: "供应商请求失败: " + err.Error(), }, }) return } // 转换为 Anthropic 响应 anthropicResp, err := anthropic.ConvertResponse(openaiResp) if err != nil { c.JSON(http.StatusInternalServerError, anthropic.ErrorResponse{ Type: "error", Error: anthropic.ErrorDetail{ Type: "api_error", Message: "响应转换失败: " + err.Error(), }, }) return } // 记录统计 go func() { _ = config.RecordRequest(routeResult.Provider.ID, openaiReq.Model) }() // 返回响应 c.JSON(http.StatusOK, anthropicResp) } // handleStreamRequest 处理流式请求 func (h *AnthropicHandler) handleStreamRequest(c *gin.Context, openaiReq *openai.ChatCompletionRequest, routeResult *router.RouteResult) { // 发送流式请求到供应商 eventChan, err := h.client.SendStreamRequest(c.Request.Context(), openaiReq, routeResult.Provider.APIKey, routeResult.Provider.BaseURL) if err != nil { c.JSON(http.StatusInternalServerError, anthropic.ErrorResponse{ Type: "error", Error: anthropic.ErrorDetail{ Type: "api_error", Message: "供应商请求失败: " + err.Error(), }, }) return } // 设置 SSE 响应头 c.Header("Content-Type", "text/event-stream") c.Header("Cache-Control", "no-cache") c.Header("Connection", "keep-alive") // 创建流写入器 writer := bufio.NewWriter(c.Writer) // 创建流式转换器 converter := anthropic.NewStreamConverter( fmt.Sprintf("msg_%s", routeResult.Provider.ID), openaiReq.Model, ) // 流式转发事件 for event := range eventChan { if event.Error != nil { fmt.Printf("流错误: %v\n", event.Error) break } if event.Done { break } // 解析 OpenAI 流块 chunk, err := openai.NewAdapter().ParseStreamChunk(event.Data) if err != nil { fmt.Printf("解析流块失败: %v\n", err) continue } // 转换为 Anthropic 事件 anthropicEvents, err := converter.ConvertChunk(chunk) if err != nil { fmt.Printf("转换事件失败: %v\n", err) continue } // 写入事件 for _, ae := range anthropicEvents { eventStr, err := anthropic.SerializeEvent(ae) if err != nil { fmt.Printf("序列化事件失败: %v\n", err) continue } writer.WriteString(eventStr) writer.Flush() } } // 记录统计 go func() { _ = config.RecordRequest(routeResult.Provider.ID, openaiReq.Model) }() } // checkMultimodalContent 检查多模态内容 func (h *AnthropicHandler) checkMultimodalContent(req *anthropic.MessagesRequest) error { for _, msg := range req.Messages { for _, block := range msg.Content { if block.Type == "image" { return fmt.Errorf("MVP 不支持多模态内容(图片)") } } } return nil } // handleError 处理路由错误 func (h *AnthropicHandler) handleError(c *gin.Context, err error) { switch err { case router.ErrModelNotFound: c.JSON(http.StatusNotFound, anthropic.ErrorResponse{ Type: "error", Error: anthropic.ErrorDetail{ Type: "not_found_error", Message: "模型未找到", }, }) case router.ErrModelDisabled: c.JSON(http.StatusNotFound, anthropic.ErrorResponse{ Type: "error", Error: anthropic.ErrorDetail{ Type: "not_found_error", Message: "模型已禁用", }, }) case router.ErrProviderDisabled: c.JSON(http.StatusNotFound, anthropic.ErrorResponse{ Type: "error", Error: anthropic.ErrorDetail{ Type: "not_found_error", Message: "供应商已禁用", }, }) default: c.JSON(http.StatusInternalServerError, anthropic.ErrorResponse{ Type: "error", Error: anthropic.ErrorDetail{ Type: "internal_error", Message: "内部错误: " + err.Error(), }, }) } }