| | package http |
| |
|
| | import ( |
| | "embed" |
| | "errors" |
| | "fmt" |
| | "io/fs" |
| | "net/http" |
| | "os" |
| | "path/filepath" |
| | "strings" |
| |
|
| | "github.com/labstack/echo/v4" |
| | "github.com/labstack/echo/v4/middleware" |
| |
|
| | "github.com/mudler/LocalAI/core/http/endpoints/localai" |
| | httpMiddleware "github.com/mudler/LocalAI/core/http/middleware" |
| | "github.com/mudler/LocalAI/core/http/routes" |
| |
|
| | "github.com/mudler/LocalAI/core/application" |
| | "github.com/mudler/LocalAI/core/schema" |
| | "github.com/mudler/LocalAI/core/services" |
| |
|
| | "github.com/mudler/xlog" |
| | ) |
| |
|
| | |
| | |
| | |
| | var embedDirStatic embed.FS |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | func API(application *application.Application) (*echo.Echo, error) { |
| | e := echo.New() |
| |
|
| | |
| | if application.ApplicationConfig().UploadLimitMB > 0 { |
| | e.Use(middleware.BodyLimit(fmt.Sprintf("%dM", application.ApplicationConfig().UploadLimitMB))) |
| | } |
| |
|
| | |
| | if !application.ApplicationConfig().OpaqueErrors { |
| | e.HTTPErrorHandler = func(err error, c echo.Context) { |
| | code := http.StatusInternalServerError |
| | var he *echo.HTTPError |
| | if errors.As(err, &he) { |
| | code = he.Code |
| | } |
| |
|
| | |
| | if code == http.StatusNotFound { |
| | notFoundHandler(c) |
| | return |
| | } |
| |
|
| | |
| | c.JSON(code, schema.ErrorResponse{ |
| | Error: &schema.APIError{Message: err.Error(), Code: code}, |
| | }) |
| | } |
| | } else { |
| | e.HTTPErrorHandler = func(err error, c echo.Context) { |
| | code := http.StatusInternalServerError |
| | var he *echo.HTTPError |
| | if errors.As(err, &he) { |
| | code = he.Code |
| | } |
| | c.NoContent(code) |
| | } |
| | } |
| |
|
| | |
| | e.Renderer = renderEngine() |
| |
|
| | |
| | e.HideBanner = true |
| | e.HidePort = true |
| |
|
| | |
| | e.Pre(httpMiddleware.StripPathPrefix()) |
| |
|
| | e.Pre(middleware.RemoveTrailingSlash()) |
| |
|
| | if application.ApplicationConfig().MachineTag != "" { |
| | e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { |
| | return func(c echo.Context) error { |
| | c.Response().Header().Set("Machine-Tag", application.ApplicationConfig().MachineTag) |
| | return next(c) |
| | } |
| | }) |
| | } |
| |
|
| | |
| | e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { |
| | return func(c echo.Context) error { |
| | req := c.Request() |
| | res := c.Response() |
| | err := next(c) |
| | xlog.Info("HTTP request", "method", req.Method, "path", req.URL.Path, "status", res.Status) |
| | return err |
| | } |
| | }) |
| |
|
| | |
| | if !application.ApplicationConfig().Debug { |
| | e.Use(middleware.Recover()) |
| | } |
| |
|
| | |
| | if !application.ApplicationConfig().DisableMetrics { |
| | metricsService, err := services.NewLocalAIMetricsService() |
| | if err != nil { |
| | return nil, err |
| | } |
| |
|
| | if metricsService != nil { |
| | e.Use(localai.LocalAIMetricsAPIMiddleware(metricsService)) |
| | e.Server.RegisterOnShutdown(func() { |
| | metricsService.Shutdown() |
| | }) |
| | } |
| | } |
| |
|
| | |
| | routes.HealthRoutes(e) |
| |
|
| | |
| | keyAuthMiddleware, err := httpMiddleware.GetKeyAuthConfig(application.ApplicationConfig()) |
| | if err != nil { |
| | return nil, fmt.Errorf("failed to create key auth config: %w", err) |
| | } |
| |
|
| | |
| | e.GET("/favicon.svg", func(c echo.Context) error { |
| | data, err := embedDirStatic.ReadFile("static/favicon.svg") |
| | if err != nil { |
| | return c.NoContent(http.StatusNotFound) |
| | } |
| | c.Response().Header().Set("Content-Type", "image/svg+xml") |
| | return c.Blob(http.StatusOK, "image/svg+xml", data) |
| | }) |
| |
|
| | |
| | staticFS, err := fs.Sub(embedDirStatic, "static") |
| | if err != nil { |
| | return nil, fmt.Errorf("failed to create static filesystem: %w", err) |
| | } |
| | e.StaticFS("/static", staticFS) |
| |
|
| | |
| | if application.ApplicationConfig().GeneratedContentDir != "" { |
| | os.MkdirAll(application.ApplicationConfig().GeneratedContentDir, 0750) |
| | audioPath := filepath.Join(application.ApplicationConfig().GeneratedContentDir, "audio") |
| | imagePath := filepath.Join(application.ApplicationConfig().GeneratedContentDir, "images") |
| | videoPath := filepath.Join(application.ApplicationConfig().GeneratedContentDir, "videos") |
| |
|
| | os.MkdirAll(audioPath, 0750) |
| | os.MkdirAll(imagePath, 0750) |
| | os.MkdirAll(videoPath, 0750) |
| |
|
| | e.Static("/generated-audio", audioPath) |
| | e.Static("/generated-images", imagePath) |
| | e.Static("/generated-videos", videoPath) |
| | } |
| |
|
| | |
| | e.Use(keyAuthMiddleware) |
| |
|
| | |
| | if application.ApplicationConfig().CORS { |
| | corsConfig := middleware.CORSConfig{} |
| | if application.ApplicationConfig().CORSAllowOrigins != "" { |
| | corsConfig.AllowOrigins = strings.Split(application.ApplicationConfig().CORSAllowOrigins, ",") |
| | } |
| | e.Use(middleware.CORSWithConfig(corsConfig)) |
| | } |
| |
|
| | |
| | if application.ApplicationConfig().CSRF { |
| | xlog.Debug("Enabling CSRF middleware. Tokens are now required for state-modifying requests") |
| | e.Use(middleware.CSRF()) |
| | } |
| |
|
| | requestExtractor := httpMiddleware.NewRequestExtractor(application.ModelConfigLoader(), application.ModelLoader(), application.ApplicationConfig()) |
| |
|
| | routes.RegisterElevenLabsRoutes(e, requestExtractor, application.ModelConfigLoader(), application.ModelLoader(), application.ApplicationConfig()) |
| |
|
| | |
| | var opcache *services.OpCache |
| | if !application.ApplicationConfig().DisableWebUI { |
| | opcache = services.NewOpCache(application.GalleryService()) |
| | } |
| |
|
| | routes.RegisterLocalAIRoutes(e, requestExtractor, application.ModelConfigLoader(), application.ModelLoader(), application.ApplicationConfig(), application.GalleryService(), opcache, application.TemplatesEvaluator(), application) |
| | routes.RegisterOpenAIRoutes(e, requestExtractor, application) |
| | routes.RegisterAnthropicRoutes(e, requestExtractor, application) |
| | if !application.ApplicationConfig().DisableWebUI { |
| | routes.RegisterUIAPIRoutes(e, application.ModelConfigLoader(), application.ModelLoader(), application.ApplicationConfig(), application.GalleryService(), opcache, application) |
| | routes.RegisterUIRoutes(e, application.ModelConfigLoader(), application.ModelLoader(), application.ApplicationConfig(), application.GalleryService()) |
| | } |
| | routes.RegisterJINARoutes(e, requestExtractor, application.ModelConfigLoader(), application.ModelLoader(), application.ApplicationConfig()) |
| |
|
| | |
| |
|
| | |
| | e.Server.RegisterOnShutdown(func() { |
| | xlog.Info("LocalAI API server shutting down") |
| | }) |
| |
|
| | return e, nil |
| | } |
| |
|