package router import ( "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/controller" "github.com/QuantumNous/new-api/middleware" "github.com/QuantumNous/new-api/relay" "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) // relayHandler returns a handler that calls controller.Relay with the given format. func relayHandler(format types.RelayFormat) gin.HandlerFunc { return func(c *gin.Context) { controller.Relay(c, format) } } func SetRelayRouter(router *gin.Engine) { router.Use(middleware.CORS()) router.Use(middleware.DecompressRequestMiddleware()) router.Use(middleware.BodyStorageCleanup()) // 清理请求体存储 router.Use(middleware.StatsMiddleware()) // https://platform.openai.com/docs/api-reference/introduction modelsRouter := router.Group("/v1/models") modelsRouter.Use(middleware.TokenAuth()) { modelsRouter.GET("", func(c *gin.Context) { switch { case c.GetHeader("x-api-key") != "" && c.GetHeader("anthropic-version") != "": controller.ListModels(c, constant.ChannelTypeAnthropic) case c.GetHeader("x-goog-api-key") != "" || c.Query("key") != "": // 单独的适配 controller.RetrieveModel(c, constant.ChannelTypeGemini) default: controller.ListModels(c, constant.ChannelTypeOpenAI) } }) modelsRouter.GET("/:model", func(c *gin.Context) { switch { case c.GetHeader("x-api-key") != "" && c.GetHeader("anthropic-version") != "": controller.RetrieveModel(c, constant.ChannelTypeAnthropic) default: controller.RetrieveModel(c, constant.ChannelTypeOpenAI) } }) } geminiRouter := router.Group("/v1beta/models") geminiRouter.Use(middleware.TokenAuth()) { geminiRouter.GET("", func(c *gin.Context) { controller.ListModels(c, constant.ChannelTypeGemini) }) } geminiCompatibleRouter := router.Group("/v1beta/openai/models") geminiCompatibleRouter.Use(middleware.TokenAuth()) { geminiCompatibleRouter.GET("", func(c *gin.Context) { controller.ListModels(c, constant.ChannelTypeOpenAI) }) } playgroundRouter := router.Group("/pg") playgroundRouter.Use(middleware.SystemPerformanceCheck()) playgroundRouter.Use(middleware.UserAuth(), middleware.Distribute()) { playgroundRouter.POST("/chat/completions", controller.Playground) } relayV1Router := router.Group("/v1") relayV1Router.Use(middleware.SystemPerformanceCheck()) relayV1Router.Use(middleware.TokenAuth()) relayV1Router.Use(middleware.ModelRequestRateLimit()) relayV1Router.Use(middleware.RelayCaptureMiddleware()) { // WebSocket 路由(统一到 Relay) wsRouter := relayV1Router.Group("") wsRouter.Use(middleware.Distribute()) wsRouter.Use(middleware.EndpointFormatGuard()) wsRouter.GET("/realtime", relayHandler(types.RelayFormatOpenAIRealtime)) } { //http router httpRouter := relayV1Router.Group("") httpRouter.Use(middleware.Distribute()) httpRouter.Use(middleware.EndpointFormatGuard()) httpRouter.Use(middleware.UserModelRateLimit()) // claude related routes httpRouter.POST("/messages", relayHandler(types.RelayFormatClaude)) // chat related routes httpRouter.POST("/completions", relayHandler(types.RelayFormatOpenAI)) httpRouter.POST("/chat/completions", relayHandler(types.RelayFormatOpenAI)) // response related routes httpRouter.POST("/responses", relayHandler(types.RelayFormatOpenAIResponses)) httpRouter.POST("/responses/compact", relayHandler(types.RelayFormatOpenAIResponsesCompaction)) // image related routes httpRouter.POST("/edits", relayHandler(types.RelayFormatOpenAIImage)) httpRouter.POST("/images/generations", relayHandler(types.RelayFormatOpenAIImage)) httpRouter.POST("/images/edits", relayHandler(types.RelayFormatOpenAIImage)) // embedding related routes httpRouter.POST("/embeddings", relayHandler(types.RelayFormatEmbedding)) // audio related routes httpRouter.POST("/audio/transcriptions", relayHandler(types.RelayFormatOpenAIAudio)) httpRouter.POST("/audio/translations", relayHandler(types.RelayFormatOpenAIAudio)) httpRouter.POST("/audio/speech", relayHandler(types.RelayFormatOpenAIAudio)) // rerank related routes httpRouter.POST("/rerank", relayHandler(types.RelayFormatRerank)) // gemini relay routes httpRouter.POST("/engines/:model/embeddings", relayHandler(types.RelayFormatGemini)) httpRouter.POST("/models/*path", relayHandler(types.RelayFormatGemini)) // other relay routes httpRouter.POST("/moderations", relayHandler(types.RelayFormatOpenAI)) // not implemented httpRouter.POST("/images/variations", controller.RelayNotImplemented) httpRouter.GET("/files", controller.RelayNotImplemented) httpRouter.POST("/files", controller.RelayNotImplemented) httpRouter.DELETE("/files/:id", controller.RelayNotImplemented) httpRouter.GET("/files/:id", controller.RelayNotImplemented) httpRouter.GET("/files/:id/content", controller.RelayNotImplemented) httpRouter.POST("/fine-tunes", controller.RelayNotImplemented) httpRouter.GET("/fine-tunes", controller.RelayNotImplemented) httpRouter.GET("/fine-tunes/:id", controller.RelayNotImplemented) httpRouter.POST("/fine-tunes/:id/cancel", controller.RelayNotImplemented) httpRouter.GET("/fine-tunes/:id/events", controller.RelayNotImplemented) httpRouter.DELETE("/models/:model", controller.RelayNotImplemented) } relayMjRouter := router.Group("/mj") relayMjRouter.Use(middleware.SystemPerformanceCheck()) registerMjRouterGroup(relayMjRouter) relayMjModeRouter := router.Group("/:mode/mj") relayMjModeRouter.Use(middleware.SystemPerformanceCheck()) registerMjRouterGroup(relayMjModeRouter) //relayMjRouter.Use() relaySunoRouter := router.Group("/suno") relaySunoRouter.Use(middleware.SystemPerformanceCheck()) relaySunoRouter.Use(middleware.TokenAuth(), middleware.Distribute()) { relaySunoRouter.POST("/submit/:action", controller.RelayTask) relaySunoRouter.POST("/fetch", controller.RelayTaskFetch) relaySunoRouter.GET("/fetch/:id", controller.RelayTaskFetch) } relayGeminiRouter := router.Group("/v1beta") relayGeminiRouter.Use(middleware.SystemPerformanceCheck()) relayGeminiRouter.Use(middleware.TokenAuth()) relayGeminiRouter.Use(middleware.ModelRequestRateLimit()) relayGeminiRouter.Use(middleware.RelayCaptureMiddleware()) relayGeminiRouter.Use(middleware.Distribute()) relayGeminiRouter.Use(middleware.EndpointFormatGuard()) relayGeminiRouter.Use(middleware.UserModelRateLimit()) { // Gemini API 路径格式: /v1beta/models/{model_name}:{action} relayGeminiRouter.POST("/models/*path", relayHandler(types.RelayFormatGemini)) } } func registerMjRouterGroup(relayMjRouter *gin.RouterGroup) { relayMjRouter.GET("/image/:id", relay.RelayMidjourneyImage) relayMjRouter.Use(middleware.TokenAuth(), middleware.Distribute()) { relayMjRouter.POST("/submit/action", controller.RelayMidjourney) relayMjRouter.POST("/submit/shorten", controller.RelayMidjourney) relayMjRouter.POST("/submit/modal", controller.RelayMidjourney) relayMjRouter.POST("/submit/imagine", controller.RelayMidjourney) relayMjRouter.POST("/submit/change", controller.RelayMidjourney) relayMjRouter.POST("/submit/simple-change", controller.RelayMidjourney) relayMjRouter.POST("/submit/describe", controller.RelayMidjourney) relayMjRouter.POST("/submit/blend", controller.RelayMidjourney) relayMjRouter.POST("/submit/edits", controller.RelayMidjourney) relayMjRouter.POST("/submit/video", controller.RelayMidjourney) relayMjRouter.POST("/notify", controller.RelayMidjourney) relayMjRouter.GET("/task/:id/fetch", controller.RelayMidjourney) relayMjRouter.GET("/task/:id/image-seed", controller.RelayMidjourney) relayMjRouter.POST("/task/list-by-condition", controller.RelayMidjourney) relayMjRouter.POST("/insight-face/swap", controller.RelayMidjourney) relayMjRouter.POST("/submit/upload-discord-images", controller.RelayMidjourney) } }