diff --git a/core/config/application_config.go b/core/config/application_config.go index c2d4e13a..49b35f97 100644 --- a/core/config/application_config.go +++ b/core/config/application_config.go @@ -15,6 +15,7 @@ type ApplicationConfig struct { ConfigFile string ModelPath string UploadLimitMB, Threads, ContextSize int + DisableWelcomePage bool F16 bool Debug, DisableMessage bool ImageDir string @@ -105,6 +106,10 @@ var EnableWatchDogBusyCheck = func(o *ApplicationConfig) { o.WatchDogBusy = true } +var DisableWelcomePage = func(o *ApplicationConfig) { + o.DisableWelcomePage = true +} + func SetWatchDogBusyTimeout(t time.Duration) AppOption { return func(o *ApplicationConfig) { o.WatchDogBusyTimeout = t diff --git a/core/http/api.go b/core/http/api.go index de0a4939..365407d8 100644 --- a/core/http/api.go +++ b/core/http/api.go @@ -1,12 +1,15 @@ package http import ( + "embed" "encoding/json" "errors" - "github.com/go-skynet/LocalAI/pkg/utils" + "net/http" "os" "strings" + "github.com/go-skynet/LocalAI/pkg/utils" + "github.com/go-skynet/LocalAI/core/http/endpoints/elevenlabs" "github.com/go-skynet/LocalAI/core/http/endpoints/localai" "github.com/go-skynet/LocalAI/core/http/endpoints/openai" @@ -21,6 +24,7 @@ import ( "github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/gofiber/template/html/v2" ) func readAuthHeader(c *fiber.Ctx) string { @@ -41,9 +45,14 @@ func readAuthHeader(c *fiber.Ctx) string { return authHeader } +//go:embed views/* +var viewsfs embed.FS + func App(cl *config.BackendConfigLoader, ml *model.ModelLoader, appConfig *config.ApplicationConfig) (*fiber.App, error) { + engine := html.NewFileSystem(http.FS(viewsfs), ".html") // Return errors as JSON responses app := fiber.New(fiber.Config{ + Views: engine, BodyLimit: appConfig.UploadLimitMB * 1024 * 1024, // this is the default limit of 4MB DisableStartupMessage: appConfig.DisableMessage, // Override default error handler @@ -168,6 +177,21 @@ func App(cl *config.BackendConfigLoader, ml *model.ModelLoader, appConfig *confi utils.LoadConfig(appConfig.ConfigsDir, openai.AssistantsConfigFile, &openai.Assistants) utils.LoadConfig(appConfig.ConfigsDir, openai.AssistantsFileConfigFile, &openai.AssistantFiles) + if !appConfig.DisableWelcomePage { + models, _ := ml.ListModels() + backendConfigs := cl.GetAllBackendConfigs() + app.Get("/", auth, func(c *fiber.Ctx) error { + // Render index + return c.Render("views/index", fiber.Map{ + "Title": "LocalAI API - " + internal.PrintableVersion(), + "Version": internal.PrintableVersion(), + "Models": models, + "ModelsConfig": backendConfigs, + "ApplicationConfig": appConfig, + }) + }) + } + modelGalleryEndpointService := localai.CreateModelGalleryEndpointService(appConfig.Galleries, appConfig.ModelPath, galleryService) app.Post("/models/apply", auth, modelGalleryEndpointService.ApplyModelGalleryEndpoint()) app.Get("/models/available", auth, modelGalleryEndpointService.ListModelFromGalleryEndpoint()) @@ -275,5 +299,21 @@ func App(cl *config.BackendConfigLoader, ml *model.ModelLoader, appConfig *confi app.Get("/metrics", localai.LocalAIMetricsEndpoint()) + // Define a custom 404 handler + app.Use(func(c *fiber.Ctx) error { + + // Check if the request accepts JSON + if string(c.Context().Request.Header.ContentType()) == "application/json" || len(c.Accepts("html")) == 0 { + // The client expects a JSON response + c.Status(fiber.StatusNotFound).JSON(fiber.Map{ + "error": "Resource not found", + }) + } else { + // The client expects an HTML response + c.Status(fiber.StatusNotFound).Render("views/404", fiber.Map{}) + } + return nil + }) + return app, nil } diff --git a/core/http/views/404.html b/core/http/views/404.html new file mode 100644 index 00000000..359d8505 --- /dev/null +++ b/core/http/views/404.html @@ -0,0 +1,33 @@ + + + +{{template "views/partials/head" .}} + + +
+ + {{template "views/partials/navbar" .}} + +
+
+

Welcome to your LocalAI instance!

+
+ +
+

The FOSS alternative to OpenAI, Claude, ...

+ Documentation +
+ +
+

Nothing found!

+
+
+ + {{template "views/partials/footer" .}} +
+ + + diff --git a/core/http/views/index.html b/core/http/views/index.html new file mode 100644 index 00000000..ad14f667 --- /dev/null +++ b/core/http/views/index.html @@ -0,0 +1,58 @@ + + + + + + {{.Title}} + + + + + + + +
+ + {{template "views/partials/navbar" .}} + +
+
+

Welcome to your LocalAI instance!

+
+ +
+

The FOSS alternative to OpenAI, Claude, ...

+ Documentation +
+ +
+

Installed models

+

We have {{len .ModelsConfig}} pre-loaded models available.

+
    + {{ range .ModelsConfig }} +
  • +

    {{.Name}}

    + {{ if .Usage }} +
    {{.Usage}}
    + {{ end }} + {{ if .Description }} +

    {{.Description}}

    + {{ end }} +
  • + {{ end }} +
+
+
+ + {{template "views/partials/footer" .}} +
+ + + diff --git a/core/http/views/partials/footer.html b/core/http/views/partials/footer.html new file mode 100644 index 00000000..7fc7e504 --- /dev/null +++ b/core/http/views/partials/footer.html @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/core/http/views/partials/head.html b/core/http/views/partials/head.html new file mode 100644 index 00000000..59cdea33 --- /dev/null +++ b/core/http/views/partials/head.html @@ -0,0 +1,13 @@ + + + + {{.Title}} + + + + + \ No newline at end of file diff --git a/core/http/views/partials/navbar.html b/core/http/views/partials/navbar.html new file mode 100644 index 00000000..2717f974 --- /dev/null +++ b/core/http/views/partials/navbar.html @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/go.mod b/go.mod index 8a43df1d..79068904 100644 --- a/go.mod +++ b/go.mod @@ -75,6 +75,9 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/gofiber/template v1.8.3 // indirect + github.com/gofiber/template/html/v2 v2.1.1 // indirect + github.com/gofiber/utils v1.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.2 // indirect diff --git a/go.sum b/go.sum index bef84d57..a2c5b912 100644 --- a/go.sum +++ b/go.sum @@ -96,6 +96,12 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw= github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw= +github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc= +github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8= +github.com/gofiber/template/html/v2 v2.1.1 h1:QEy3O3EBkvwDthy5bXVGUseOyO6ldJoiDxlF4+MJiV8= +github.com/gofiber/template/html/v2 v2.1.1/go.mod h1:2G0GHHOUx70C1LDncoBpe4T6maQbNa4x1CVNFW0wju0= +github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= +github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= diff --git a/main.go b/main.go index 651dd1c2..f000aa71 100644 --- a/main.go +++ b/main.go @@ -189,6 +189,12 @@ func main() { EnvVars: []string{"WATCHDOG_IDLE"}, Value: false, }, + &cli.BoolFlag{ + Name: "disable-welcome", + Usage: "Disable welcome pages", + EnvVars: []string{"DISABLE_WELCOME"}, + Value: false, + }, &cli.BoolFlag{ Name: "enable-watchdog-busy", Usage: "Enable watchdog for stopping busy backends that exceed a defined threshold.", @@ -264,6 +270,11 @@ For a list of compatible model, check out: https://localai.io/model-compatibilit idleWatchDog := ctx.Bool("enable-watchdog-idle") busyWatchDog := ctx.Bool("enable-watchdog-busy") + + if ctx.Bool("disable-welcome") { + opts = append(opts, config.DisableWelcomePage) + } + if idleWatchDog || busyWatchDog { opts = append(opts, config.EnableWatchDog) if idleWatchDog {