| package apiserver |
|
|
| import ( |
| "monica-proxy/internal/middleware" |
| "monica-proxy/internal/monica" |
| "monica-proxy/internal/types" |
| "net/http" |
|
|
| "github.com/labstack/echo/v4" |
| "github.com/sashabaranov/go-openai" |
| ) |
|
|
| |
| func RegisterRoutes(e *echo.Echo) { |
| |
| e.GET("/", handleHome) |
|
|
| |
| apiGroup := e.Group("/hf") |
| apiGroup.Use(middleware.BearerAuth()) |
|
|
| |
| apiGroup.POST("/v1/chat/completions", handleChatCompletion) |
| apiGroup.GET("/v1/models", handleListModels) |
| } |
|
|
| |
| func handleHome(c echo.Context) error { |
| html := ` |
| <!DOCTYPE html> |
| <html> |
| <head> |
| <title>Monica Proxy API</title> |
| <style> |
| body { |
| font-family: system-ui, -apple-system, sans-serif; |
| max-width: 800px; |
| margin: 0 auto; |
| padding: 2rem; |
| line-height: 1.6; |
| } |
| code { |
| background: #f4f4f4; |
| padding: 0.2em 0.4em; |
| border-radius: 3px; |
| } |
| pre { |
| background: #f4f4f4; |
| padding: 1em; |
| border-radius: 5px; |
| overflow-x: auto; |
| } |
| </style> |
| </head> |
| <body> |
| <h1>Monica Proxy API</h1> |
| <p>This is a proxy service that converts Monica's service to ChatGPT-compatible API format.</p> |
| |
| <h2>API Endpoints</h2> |
| <ul> |
| <li><code>POST /hf/v1/chat/completions</code> - Chat completion endpoint</li> |
| <li><code>GET /hf/v1/models</code> - List available models</li> |
| </ul> |
| |
| <h2>Example Usage</h2> |
| <pre> |
| curl -X POST https://xxx-xxx.hf.space/hf/v1/chat/completions \ |
| -H "Authorization: Bearer YOUR_BEARER_TOKEN" \ |
| -H "Content-Type: application/json" \ |
| -d '{ |
| "model": "gpt-4o-mini", |
| "messages": [ |
| { |
| "role": "user", |
| "content": "Hello!" |
| } |
| ], |
| "stream": true |
| }'</pre> |
| </body> |
| </html>` |
|
|
| return c.HTML(http.StatusOK, html) |
| } |
|
|
| |
| func handleChatCompletion(c echo.Context) error { |
| var req openai.ChatCompletionRequest |
| if err := c.Bind(&req); err != nil { |
| return c.JSON(http.StatusBadRequest, map[string]interface{}{ |
| "error": "Invalid request payload", |
| }) |
| } |
|
|
| |
| if len(req.Messages) == 0 { |
| return c.JSON(http.StatusBadRequest, map[string]interface{}{ |
| "error": "No messages found", |
| }) |
| } |
|
|
| |
| monicaReq, err := types.ChatGPTToMonica(req) |
| if err != nil { |
| return c.JSON(http.StatusInternalServerError, map[string]interface{}{ |
| "error": err.Error(), |
| }) |
| } |
|
|
| |
| stream, err := monica.SendMonicaRequest(c.Request().Context(), monicaReq) |
| if err != nil { |
| return c.JSON(http.StatusInternalServerError, map[string]interface{}{ |
| "error": err.Error(), |
| }) |
| } |
| |
| defer stream.RawBody().Close() |
|
|
| |
| c.Response().Header().Set(echo.HeaderContentType, "text/event-stream") |
| c.Response().Header().Set("Cache-Control", "no-cache") |
| c.Response().Header().Set("Transfer-Encoding", "chunked") |
| c.Response().WriteHeader(http.StatusOK) |
|
|
| |
| if err := monica.StreamMonicaSSEToClient(req.Model, c.Response().Writer, stream.RawBody()); err != nil { |
| return err |
| } |
|
|
| return nil |
| } |
|
|
| |
| func handleListModels(c echo.Context) error { |
| models := types.GetSupportedModels() |
| return c.JSON(http.StatusOK, models) |
| } |
|
|