aaxaxax's picture
force update files
1de7911
package handler
import (
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"zencoder-2api/internal/service"
)
type GeminiHandler struct {
svc *service.GeminiService
}
func NewGeminiHandler() *GeminiHandler {
return &GeminiHandler{svc: service.NewGeminiService()}
}
// generateTraceID 生成一个随机的 trace ID
func generateGeminiTraceID() string {
b := make([]byte, 16)
rand.Read(b)
return hex.EncodeToString(b)
}
// HandleRequest 处理 POST /v1beta/models/*path
// 路径格式: /model:action 例如 /gemini-3-flash-preview:streamGenerateContent
func (h *GeminiHandler) HandleRequest(c *gin.Context) {
path := c.Param("path")
// 去掉开头的斜杠
path = strings.TrimPrefix(path, "/")
// 解析 model:action
parts := strings.SplitN(path, ":", 2)
if len(parts) != 2 {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid path format"})
return
}
modelName := parts[0]
action := parts[1]
body, err := io.ReadAll(c.Request.Body)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
switch action {
case "generateContent":
if err := h.svc.GenerateContentProxy(c.Request.Context(), c.Writer, modelName, body); err != nil {
h.handleError(c, err)
}
case "streamGenerateContent":
if err := h.svc.StreamGenerateContentProxy(c.Request.Context(), c.Writer, modelName, body); err != nil {
h.handleError(c, err)
}
default:
c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported action: " + action})
}
}
// handleError 统一处理错误,特别是没有可用账号的错误
func (h *GeminiHandler) handleError(c *gin.Context, err error) {
if errors.Is(err, service.ErrNoAvailableAccount) || errors.Is(err, service.ErrNoPermission) {
traceID := generateGeminiTraceID()
errMsg := fmt.Sprintf("没有可用token(traceid: %s)", traceID)
c.JSON(http.StatusServiceUnavailable, gin.H{"error": errMsg})
return
}
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}