|
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) |
|
} |
|
|