sub2api/backend/internal/server/routes/antigravity_http.go
win b8a4372328 feat: Complete Wire dependency injection and HTTP API routes for Antigravity
已完成:
1.  修复 SoraMediaCleanupService 未定义问题(从 provideCleanup 中删除)
2.  创建 LanguageServerService 的 Wire 提供函数
3.  更新 ProvideRouter 签名以接收 langServerService 参数
4.  更新 SetupRouter 和 registerRoutes 函数签名
5.  创建完整的 HTTP 路由处理器 (antigravity_http.go)
6.  注册 Antigravity HTTP 路由到 v1 API 分组
7.  Wire_gen.go 自动生成 LanguageServerService 的注入代码
8.  项目成功编译

三层架构已就位:
  下游客户端 (HTTP)
    ↓
  sub2api HTTP 服务层 (/api/v1/cascade/*)
    ↓
  LanguageServerService (业务逻辑层)
    ↓
  官方 Anthropic API (上游)

POST /api/v1/cascade/start      - 启动会话
POST /api/v1/cascade/message    - 发送消息(SSE 流式)
POST /api/v1/cascade/cancel     - 取消会话
GET  /api/v1/models             - 获取模型列表
GET  /api/v1/health             - 健康检查

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-10 21:23:54 +08:00

193 lines
5.2 KiB
Go
Raw Permalink 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 routes
import (
"encoding/json"
"log/slog"
"net/http"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
)
// RegisterAntigravityHTTPRoutes 注册 Antigravity HTTP API 路由
func RegisterAntigravityHTTPRoutes(v1 *gin.RouterGroup, langServerService *service.LanguageServerService) {
logger := slog.Default()
// 创建处理器
cascadeGroup := v1.Group("/cascade")
{
// 启动 Cascade 会话
cascadeGroup.POST("/start", func(c *gin.Context) {
handleStartCascade(c, langServerService, logger)
})
// 发送消息到 Cascade流式响应
cascadeGroup.POST("/message", func(c *gin.Context) {
handleSendMessage(c, langServerService, logger)
})
// 取消 Cascade 会话
cascadeGroup.POST("/cancel", func(c *gin.Context) {
handleCancelCascade(c, langServerService, logger)
})
}
// 模型列表
v1.GET("/models", func(c *gin.Context) {
handleGetModels(c, langServerService, logger)
})
// 健康检查
v1.GET("/health", func(c *gin.Context) {
handleHealth(c, logger)
})
}
// handleStartCascade 处理启动 Cascade 请求
func handleStartCascade(c *gin.Context, svc *service.LanguageServerService, logger *slog.Logger) {
type StartCascadeRequest struct {
Model string `json:"model" binding:"required"`
SystemPrompt string `json:"system_prompt"`
Metadata map[string]string `json:"metadata"`
}
var req StartCascadeRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.Error("invalid start cascade request", "error", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
return
}
// 获取 OAuth token
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "missing authorization header"})
return
}
// 调用服务
cascadeID, err := svc.StartCascade(
c.Request.Context(),
req.Model,
req.SystemPrompt,
req.Metadata,
token,
)
if err != nil {
logger.Error("start cascade failed", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"cascade_id": cascadeID})
}
// handleSendMessage 处理发送消息请求(流式)
func handleSendMessage(c *gin.Context, svc *service.LanguageServerService, logger *slog.Logger) {
type SendMessageRequest struct {
CascadeID string `json:"cascade_id" binding:"required"`
Message string `json:"message" binding:"required"`
}
var req SendMessageRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.Error("invalid send message request", "error", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
return
}
// 获取 OAuth token
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "missing authorization header"})
return
}
// 调用服务并获取流式更新通道
updateChan, err := svc.SendUserMessage(c.Request.Context(), req.CascadeID, req.Message, token)
if err != nil {
logger.Error("send message failed", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// 设置 SSE 响应头
c.Header("Content-Type", "text/event-stream")
c.Header("Cache-Control", "no-cache")
c.Header("Connection", "keep-alive")
c.Header("X-Accel-Buffering", "no")
c.Status(http.StatusOK)
// 流式发送更新到客户端
flusher, ok := c.Writer.(http.Flusher)
if !ok {
logger.Error("response writer does not support flushing")
return
}
for event := range updateChan {
if event == nil {
break
}
// 将事件序列化为 JSON
eventJSON, err := marshalJSON(event)
if err != nil {
logger.Error("failed to marshal event", "error", err)
continue
}
// 发送 SSE 格式的数据
_, _ = c.Writer.WriteString("data: " + string(eventJSON) + "\n\n")
flusher.Flush()
}
}
// handleCancelCascade 处理取消 Cascade 请求
func handleCancelCascade(c *gin.Context, svc *service.LanguageServerService, logger *slog.Logger) {
type CancelRequest struct {
CascadeID string `json:"cascade_id" binding:"required"`
}
var req CancelRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.Error("invalid cancel request", "error", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
return
}
err := svc.CancelCascade(c.Request.Context(), req.CascadeID)
if err != nil {
logger.Error("cancel cascade failed", "error", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "cascade cancelled"})
}
// handleGetModels 处理获取模型列表请求
func handleGetModels(c *gin.Context, svc *service.LanguageServerService, logger *slog.Logger) {
models, err := svc.GetAvailableModels(c.Request.Context())
if err != nil {
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-6",
})
}
// handleHealth 处理健康检查请求
func handleHealth(c *gin.Context, logger *slog.Logger) {
c.JSON(http.StatusOK, gin.H{"status": "healthy"})
}
// marshalJSON 辅助函数用于序列化事件
func marshalJSON(v interface{}) ([]byte, error) {
return json.Marshal(v)
}