// +build ignore package main import ( "encoding/json" "fmt" "log" "net/http" "os" "time" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/middleware" "github.com/gin-gonic/gin" ) func main() { gin.SetMode(gin.ReleaseMode) middleware.SetCaptureEnabled(1, true) router := gin.New() router.Use(gin.Recovery()) // 模拟 TokenAuth router.Use(func(c *gin.Context) { auth := c.GetHeader("Authorization") if auth == "" || auth == "Bearer disabled" { c.JSON(401, gin.H{"error": gin.H{"message": "Unauthorized"}}) c.Abort() return } c.Set("id", 1) c.Next() }) // 模拟 RequestId router.Use(func(c *gin.Context) { reqID := c.GetHeader(common.RequestIdKey) if reqID == "" { reqID = fmt.Sprintf("req-%d", time.Now().UnixNano()) } c.Set(common.RequestIdKey, reqID) c.Next() }) router.Use(middleware.RelayCaptureMiddleware()) // ═══════════════════════════════════════════════════════ // OpenAI Chat Completions API // ═══════════════════════════════════════════════════════ router.POST("/v1/chat/completions", func(c *gin.Context) { stream := c.Query("stream") == "true" if stream { c.Header("Content-Type", "text/event-stream") f := c.Writer.(http.Flusher) for _, s := range []string{ "data: {\"id\":\"chatcmpl-001\",\"choices\":[{\"delta\":{\"content\":\"Hello \"}}]}\n\n", "data: {\"id\":\"chatcmpl-001\",\"choices\":[{\"delta\":{\"content\":\"World!\"}}]}\n\n", "data: [DONE]\n\n", } { c.Writer.Write([]byte(s)) f.Flush() } return } c.JSON(200, gin.H{ "id": "chatcmpl-001", "object": "chat.completion", "model": "gpt-4", "choices": []gin.H{{"index": 0, "message": gin.H{"role": "assistant", "content": "Hello from Chat Completions!"}}}, "usage": gin.H{"prompt_tokens": 5, "completion_tokens": 5, "total_tokens": 10}, }) }) // ═══════════════════════════════════════════════════════ // OpenAI Responses API (new) // ═══════════════════════════════════════════════════════ router.POST("/v1/responses", func(c *gin.Context) { c.JSON(200, gin.H{ "id": "resp_abc123", "object": "response", "status": "completed", "output": []gin.H{{ "type": "message", "role": "assistant", "content": []gin.H{{ "type": "output_text", "text": "Hello from Responses API!", }}, }}, "usage": gin.H{"input_tokens": 8, "output_tokens": 4, "total_tokens": 12}, }) }) // ═══════════════════════════════════════════════════════ // Claude Messages API (Anthropic) // ═══════════════════════════════════════════════════════ router.POST("/v1/messages", func(c *gin.Context) { c.JSON(200, gin.H{ "id": "msg_01AbCdEfGhIjKlMnOpQrStUv", "type": "message", "role": "assistant", "model": "claude-sonnet-4-6", "content": []gin.H{{"type": "text", "text": "Hello! How can I assist you today?"}}, "stop_reason": "end_turn", "usage": gin.H{"input_tokens": 8, "output_tokens": 12}, }) }) // ═══════════════════════════════════════════════════════ // Gemini Native API (/v1beta/models/*) // ═══════════════════════════════════════════════════════ router.POST("/v1beta/models/*path", func(c *gin.Context) { path := c.Param("path") c.JSON(200, gin.H{ "candidates": []gin.H{{ "content": gin.H{ "role": "model", "parts": []gin.H{{"text": fmt.Sprintf("Gemini response for: /%s", path)}}, }, "finishReason": "STOP", }}, "usageMetadata": gin.H{"promptTokenCount": 5, "candidatesTokenCount": 10, "totalTokenCount": 15}, }) }) // ═══════════════════════════════════════════════════════ // Gemini via /v1/models/* (main route) // ═══════════════════════════════════════════════════════ router.POST("/v1/models/*path", func(c *gin.Context) { path := c.Param("path") c.JSON(200, gin.H{ "candidates": []gin.H{{ "content": gin.H{ "role": "model", "parts": []gin.H{{"text": fmt.Sprintf("Gemini(v1) response for: /%s", path)}}, }, "finishReason": "STOP", }}, }) }) // ═══════════════════════════════════════════════════════ // Models list // ═══════════════════════════════════════════════════════ router.GET("/v1/models", func(c *gin.Context) { c.JSON(200, gin.H{"data": []gin.H{{"id": "gpt-4"}, {"id": "claude-3"}, {"id": "gemini-2.0-flash"}}}) }) // ═══════════════════════════════════════════════════════ // Error endpoint for testing error status codes // ═══════════════════════════════════════════════════════ router.POST("/v1/error", func(c *gin.Context) { code := 500 if c := c.Query("code"); c != "" { fmt.Sscanf(c, "%d", &code) } c.JSON(code, gin.H{"error": gin.H{"message": "test error", "type": "test_error"}}) }) // ═══════════════════════════════════════════════════════ // Capture file management endpoints // ═══════════════════════════════════════════════════════ router.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }) router.GET("/capture-files", func(c *gin.Context) { today := time.Now().Format("2006-01-02") dir := fmt.Sprintf("./data/relay-capture/%s", today) entries, err := os.ReadDir(dir) if err != nil { c.JSON(200, gin.H{"files": []string{}, "error": err.Error()}) return } var files []string for _, e := range entries { files = append(files, e.Name()) } c.JSON(200, gin.H{"dir": dir, "files": files}) }) router.GET("/capture-files/:name", func(c *gin.Context) { today := time.Now().Format("2006-01-02") path := fmt.Sprintf("./data/relay-capture/%s/%s", today, c.Param("name")) data, err := os.ReadFile(path) if err != nil { c.JSON(404, gin.H{"error": err.Error()}) return } var record map[string]interface{} json.Unmarshal(data, &record) c.JSON(200, record) }) router.DELETE("/capture-files", func(c *gin.Context) { os.RemoveAll("./data/relay-capture") c.JSON(200, gin.H{"status": "cleaned"}) }) port := "18080" if p := os.Getenv("PORT"); p != "" { port = p } log.Printf("Mock Relay Server on :%s (chat|responses|messages|gemini)", port) http.ListenAndServe(":"+port, router) }