package handler import ( "encoding/json" "log/slog" "net/http" "github.com/Wei-Shaw/sub2api/internal/service" "github.com/gin-gonic/gin" ) // AntigravityHTTPHandler 处理下游客户端的 HTTP 请求 // 内部调用 LanguageServerService,再转发到上游 API type AntigravityHTTPHandler struct { langServerService *service.LanguageServerService logger *slog.Logger } func NewAntigravityHTTPHandler( langServerService *service.LanguageServerService, logger *slog.Logger, ) *AntigravityHTTPHandler { return &AntigravityHTTPHandler{ langServerService: langServerService, logger: logger, } } // ============================================================================ // Cascade 流程 API // ============================================================================ // StartCascadeRequest HTTP 请求格式 type StartCascadeRequest struct { Model string `json:"model"` // 模型名称 SystemPrompt string `json:"system_prompt"` // 系统提示 Metadata map[string]string `json:"metadata"` // 设备指纹等伪装信息 } // StartCascadeResponse HTTP 响应格式 type StartCascadeResponse struct { CascadeID string `json:"cascade_id"` } // POST /api/v1/cascade/start // 启动新的 Cascade Agent 会话 func (h *AntigravityHTTPHandler) StartCascade(c *gin.Context) { var req StartCascadeRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("invalid request", "error", err) c.JSON(http.StatusBadRequest, gin.H{ "error": "invalid request: " + err.Error(), }) return } // 提取 OAuth token token := c.GetHeader("Authorization") if token == "" { c.JSON(http.StatusUnauthorized, gin.H{ "error": "missing authorization header", }) return } // 调用内部 LanguageServerService cascadeID, err := h.langServerService.StartCascade( c.Request.Context(), req.Model, req.SystemPrompt, req.Metadata, token, ) if err != nil { h.logger.Error("start cascade failed", "error", err) c.JSON(http.StatusInternalServerError, gin.H{ "error": err.Error(), }) return } h.logger.Info("cascade started", "cascade_id", cascadeID, "model", req.Model) c.JSON(http.StatusOK, StartCascadeResponse{ CascadeID: cascadeID, }) } // ============================================================================ // SendUserMessageRequest HTTP 请求格式 type SendUserMessageRequest struct { CascadeID string `json:"cascade_id"` // 会话 ID Message string `json:"message"` // 用户消息 Context map[string]string `json:"context"` // 上下文(可选) } // CascadeUpdate 流式响应格式(Server-Sent Events) type CascadeUpdate struct { Type string `json:"type"` // "message_delta", "tool_call", etc. Payload string `json:"payload"` // JSON 格式的负载 } // POST /api/v1/cascade/message (流式) // 发送用户消息,接收流式更新 func (h *AntigravityHTTPHandler) SendUserMessage(c *gin.Context) { var req SendUserMessageRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("invalid request", "error", err) c.JSON(http.StatusBadRequest, gin.H{ "error": "invalid request: " + err.Error(), }) return } // 提取 OAuth token token := c.GetHeader("Authorization") if token == "" { c.JSON(http.StatusUnauthorized, gin.H{ "error": "missing authorization header", }) return } // 设置 Server-Sent Events 响应头 c.Header("Content-Type", "text/event-stream") c.Header("Cache-Control", "no-cache") c.Header("Connection", "keep-alive") // 调用内部 LanguageServerService,获取流式响应 updateChan, err := h.langServerService.SendUserMessage( c.Request.Context(), req.CascadeID, req.Message, token, ) if err != nil { h.logger.Error("send user message failed", "error", err) c.JSON(http.StatusInternalServerError, gin.H{ "error": err.Error(), }) return } // 逐个推送更新到客户端(SSE) for update := range updateChan { data, _ := json.Marshal(update) c.SSEvent("update", string(data)) c.Writer.Flush() } h.logger.Info("cascade message processed", "cascade_id", req.CascadeID) } // ============================================================================ // POST /api/v1/cascade/cancel // 取消 Cascade 调用 func (h *AntigravityHTTPHandler) CancelCascade(c *gin.Context) { var req struct { CascadeID string `json:"cascade_id"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "invalid request", }) return } if err := h.langServerService.CancelCascade( c.Request.Context(), req.CascadeID, ); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": err.Error(), }) return } h.logger.Info("cascade cancelled", "cascade_id", req.CascadeID) c.JSON(http.StatusOK, gin.H{ "success": true, }) } // ============================================================================ // 模型配置 API // ============================================================================ // ModelConfig 模型配置 type ModelConfig struct { Name string `json:"name"` DisplayName string `json:"display_name"` MaxTokens int `json:"max_tokens"` SupportsThinking bool `json:"supports_thinking"` ThinkingBudget int `json:"thinking_budget,omitempty"` SupportsImages bool `json:"supports_images"` Provider string `json:"provider"` // anthropic, google, openai } // GET /api/v1/models // 获取可用模型列表 func (h *AntigravityHTTPHandler) GetModels(c *gin.Context) { models, err := h.langServerService.GetAvailableModels(c.Request.Context()) if err != nil { h.logger.Error("get models failed", "error", err) c.JSON(http.StatusInternalServerError, gin.H{ "error": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "models": models, "default_model": "claude-opus-4-7", }) } // ============================================================================ // 健康检查 API // ============================================================================ // GET /api/v1/health // 健康检查 func (h *AntigravityHTTPHandler) Health(c *gin.Context) { status, err := h.langServerService.GetStatus(c.Request.Context()) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "status": "unhealthy", "error": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "status": status, "version": "1.0.0", }) } // ============================================================================ // RegisterRoutes 注册所有 HTTP 路由 func (h *AntigravityHTTPHandler) RegisterRoutes(router *gin.Engine) { api := router.Group("/api/v1") // Cascade 流程 api.POST("/cascade/start", h.StartCascade) api.POST("/cascade/message", h.SendUserMessage) api.POST("/cascade/cancel", h.CancelCascade) // 模型列表 api.GET("/models", h.GetModels) // 健康检查 api.GET("/health", h.Health) h.logger.Info("antigravity http handler registered", "routes", []string{ "/api/v1/cascade/start", "/api/v1/cascade/message", "/api/v1/cascade/cancel", "/api/v1/models", "/api/v1/health", }) }