2024-02-21 01:21:19 +00:00
package http
2024-01-05 17:04:46 +00:00
import (
2024-04-17 21:33:49 +00:00
"encoding/json"
2024-01-05 17:04:46 +00:00
"errors"
2024-04-17 21:33:49 +00:00
"os"
2024-01-05 17:04:46 +00:00
"strings"
2024-04-17 21:33:49 +00:00
"github.com/go-skynet/LocalAI/pkg/utils"
2024-03-27 20:10:58 +00:00
2024-03-14 22:08:34 +00:00
"github.com/go-skynet/LocalAI/core/http/endpoints/elevenlabs"
2024-03-01 15:19:53 +00:00
"github.com/go-skynet/LocalAI/core/http/endpoints/localai"
"github.com/go-skynet/LocalAI/core/http/endpoints/openai"
2024-04-17 21:33:49 +00:00
"github.com/go-skynet/LocalAI/core/config"
2024-02-21 01:21:19 +00:00
"github.com/go-skynet/LocalAI/core/schema"
2024-03-01 15:19:53 +00:00
"github.com/go-skynet/LocalAI/core/services"
2024-01-05 17:04:46 +00:00
"github.com/go-skynet/LocalAI/internal"
2024-04-17 21:33:49 +00:00
"github.com/go-skynet/LocalAI/pkg/model"
2024-01-05 17:04:46 +00:00
2024-04-20 08:43:37 +00:00
"github.com/gofiber/contrib/fiberzerolog"
2024-01-05 17:04:46 +00:00
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/recover"
2024-04-20 08:43:37 +00:00
"github.com/gofiber/swagger" // swagger handler
"github.com/rs/zerolog/log"
2024-01-05 17:04:46 +00:00
)
2024-03-14 22:08:34 +00:00
func readAuthHeader ( c * fiber . Ctx ) string {
authHeader := c . Get ( "Authorization" )
// elevenlabs
xApiKey := c . Get ( "xi-api-key" )
if xApiKey != "" {
authHeader = "Bearer " + xApiKey
}
// anthropic
xApiKey = c . Get ( "x-api-key" )
if xApiKey != "" {
authHeader = "Bearer " + xApiKey
}
return authHeader
}
2024-03-29 21:29:33 +00:00
// @title LocalAI API
// @version 2.0.0
// @description The LocalAI Rest API.
// @termsOfService
// @contact.name LocalAI
// @contact.url https://localai.io
// @license.name MIT
// @license.url https://raw.githubusercontent.com/mudler/LocalAI/master/LICENSE
// @BasePath /
// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
2024-04-17 21:33:49 +00:00
func App ( cl * config . BackendConfigLoader , ml * model . ModelLoader , appConfig * config . ApplicationConfig ) ( * fiber . App , error ) {
2024-01-05 17:04:46 +00:00
// Return errors as JSON responses
app := fiber . New ( fiber . Config {
2024-04-20 08:43:37 +00:00
Views : renderEngine ( ) ,
BodyLimit : appConfig . UploadLimitMB * 1024 * 1024 , // this is the default limit of 4MB
// We disable the Fiber startup message as it does not conform to structured logging.
// We register a startup log line with connection information in the OnListen hook to keep things user friendly though
DisableStartupMessage : true ,
2024-01-05 17:04:46 +00:00
// Override default error handler
ErrorHandler : func ( ctx * fiber . Ctx , err error ) error {
// Status code defaults to 500
code := fiber . StatusInternalServerError
// Retrieve the custom status code if it's a *fiber.Error
var e * fiber . Error
if errors . As ( err , & e ) {
code = e . Code
}
// Send custom error page
return ctx . Status ( code ) . JSON (
schema . ErrorResponse {
Error : & schema . APIError { Message : err . Error ( ) , Code : code } ,
} ,
)
} ,
} )
2024-04-20 08:43:37 +00:00
app . Hooks ( ) . OnListen ( func ( listenData fiber . ListenData ) error {
scheme := "http"
if listenData . TLS {
scheme = "https"
}
log . Info ( ) . Str ( "endpoint" , scheme + "://" + listenData . Host + ":" + listenData . Port ) . Msg ( "LocalAI API is listening! Please connect to the endpoint for API documentation." )
return nil
} )
// Have Fiber use zerolog like the rest of the application rather than it's built-in logger
logger := log . Logger
app . Use ( fiberzerolog . New ( fiberzerolog . Config {
Logger : & logger ,
} ) )
2024-01-05 17:04:46 +00:00
// Default middleware config
2024-02-17 09:00:34 +00:00
2024-04-17 21:33:49 +00:00
if ! appConfig . Debug {
2024-02-17 09:00:34 +00:00
app . Use ( recover . New ( ) )
}
2024-03-01 15:19:53 +00:00
metricsService , err := services . NewLocalAIMetricsService ( )
if err != nil {
return nil , err
}
if metricsService != nil {
app . Use ( localai . LocalAIMetricsAPIMiddleware ( metricsService ) )
app . Hooks ( ) . OnShutdown ( func ( ) error {
return metricsService . Shutdown ( )
} )
2024-01-05 17:04:46 +00:00
}
// Auth middleware checking if API key is valid. If no API key is set, no auth is required.
auth := func ( c * fiber . Ctx ) error {
2024-04-17 21:33:49 +00:00
if len ( appConfig . ApiKeys ) == 0 {
return c . Next ( )
}
// Check for api_keys.json file
fileContent , err := os . ReadFile ( "api_keys.json" )
if err == nil {
// Parse JSON content from the file
var fileKeys [ ] string
err := json . Unmarshal ( fileContent , & fileKeys )
if err != nil {
return c . Status ( fiber . StatusInternalServerError ) . JSON ( fiber . Map { "message" : "Error parsing api_keys.json" } )
}
// Add file keys to options.ApiKeys
appConfig . ApiKeys = append ( appConfig . ApiKeys , fileKeys ... )
}
if len ( appConfig . ApiKeys ) == 0 {
2024-01-05 17:04:46 +00:00
return c . Next ( )
}
2024-03-14 22:08:34 +00:00
authHeader := readAuthHeader ( c )
2024-01-05 17:04:46 +00:00
if authHeader == "" {
return c . Status ( fiber . StatusUnauthorized ) . JSON ( fiber . Map { "message" : "Authorization header missing" } )
}
2024-03-14 22:08:34 +00:00
// If it's a bearer token
2024-01-05 17:04:46 +00:00
authHeaderParts := strings . Split ( authHeader , " " )
if len ( authHeaderParts ) != 2 || authHeaderParts [ 0 ] != "Bearer" {
return c . Status ( fiber . StatusUnauthorized ) . JSON ( fiber . Map { "message" : "Invalid Authorization header format" } )
}
apiKey := authHeaderParts [ 1 ]
2024-04-17 21:33:49 +00:00
for _ , key := range appConfig . ApiKeys {
2024-01-05 17:04:46 +00:00
if apiKey == key {
return c . Next ( )
}
}
return c . Status ( fiber . StatusUnauthorized ) . JSON ( fiber . Map { "message" : "Invalid API key" } )
}
2024-04-17 21:33:49 +00:00
if appConfig . CORS {
2024-01-05 17:04:46 +00:00
var c func ( ctx * fiber . Ctx ) error
2024-04-17 21:33:49 +00:00
if appConfig . CORSAllowOrigins == "" {
2024-01-05 17:04:46 +00:00
c = cors . New ( )
} else {
2024-04-17 21:33:49 +00:00
c = cors . New ( cors . Config { AllowOrigins : appConfig . CORSAllowOrigins } )
2024-01-05 17:04:46 +00:00
}
app . Use ( c )
}
// LocalAI API endpoints
2024-04-17 21:33:49 +00:00
galleryService := services . NewGalleryService ( appConfig . ModelPath )
galleryService . Start ( appConfig . Context , cl )
2024-01-05 17:04:46 +00:00
app . Get ( "/version" , auth , func ( c * fiber . Ctx ) error {
return c . JSON ( struct {
Version string ` json:"version" `
} { Version : internal . PrintableVersion ( ) } )
} )
2024-04-17 21:33:49 +00:00
// Make sure directories exists
os . MkdirAll ( appConfig . ImageDir , 0755 )
os . MkdirAll ( appConfig . AudioDir , 0755 )
os . MkdirAll ( appConfig . UploadDir , 0755 )
os . MkdirAll ( appConfig . ConfigsDir , 0755 )
os . MkdirAll ( appConfig . ModelPath , 0755 )
// Load config jsons
utils . LoadConfig ( appConfig . UploadDir , openai . UploadedFilesFile , & openai . UploadedFiles )
utils . LoadConfig ( appConfig . ConfigsDir , openai . AssistantsConfigFile , & openai . Assistants )
utils . LoadConfig ( appConfig . ConfigsDir , openai . AssistantsFileConfigFile , & openai . AssistantFiles )
2024-03-29 21:29:33 +00:00
app . Get ( "/swagger/*" , swagger . HandlerDefault ) // default
2024-03-28 20:52:52 +00:00
welcomeRoute (
app ,
2024-04-17 21:33:49 +00:00
cl ,
ml ,
appConfig ,
2024-03-28 20:52:52 +00:00
auth ,
)
2024-03-27 20:10:58 +00:00
2024-04-17 21:33:49 +00:00
modelGalleryEndpointService := localai . CreateModelGalleryEndpointService ( appConfig . Galleries , appConfig . ModelPath , galleryService )
2024-03-01 15:19:53 +00:00
app . Post ( "/models/apply" , auth , modelGalleryEndpointService . ApplyModelGalleryEndpoint ( ) )
app . Get ( "/models/available" , auth , modelGalleryEndpointService . ListModelFromGalleryEndpoint ( ) )
app . Get ( "/models/galleries" , auth , modelGalleryEndpointService . ListModelGalleriesEndpoint ( ) )
app . Post ( "/models/galleries" , auth , modelGalleryEndpointService . AddModelGalleryEndpoint ( ) )
app . Delete ( "/models/galleries" , auth , modelGalleryEndpointService . RemoveModelGalleryEndpoint ( ) )
app . Get ( "/models/jobs/:uuid" , auth , modelGalleryEndpointService . GetOpStatusEndpoint ( ) )
app . Get ( "/models/jobs" , auth , modelGalleryEndpointService . GetAllStatusEndpoint ( ) )
2024-01-05 17:04:46 +00:00
2024-04-17 21:33:49 +00:00
app . Post ( "/tts" , auth , localai . TTSEndpoint ( cl , ml , appConfig ) )
// Elevenlabs
app . Post ( "/v1/text-to-speech/:voice-id" , auth , elevenlabs . TTSEndpoint ( cl , ml , appConfig ) )
2024-03-22 20:14:04 +00:00
// Stores
2024-04-17 21:33:49 +00:00
sl := model . NewModelLoader ( "" )
app . Post ( "/stores/set" , auth , localai . StoresSetEndpoint ( sl , appConfig ) )
app . Post ( "/stores/delete" , auth , localai . StoresDeleteEndpoint ( sl , appConfig ) )
app . Post ( "/stores/get" , auth , localai . StoresGetEndpoint ( sl , appConfig ) )
app . Post ( "/stores/find" , auth , localai . StoresFindEndpoint ( sl , appConfig ) )
2024-03-22 20:14:04 +00:00
2024-04-17 21:33:49 +00:00
// openAI compatible API endpoint
2024-01-05 17:04:46 +00:00
// chat
2024-04-17 21:33:49 +00:00
app . Post ( "/v1/chat/completions" , auth , openai . ChatEndpoint ( cl , ml , appConfig ) )
app . Post ( "/chat/completions" , auth , openai . ChatEndpoint ( cl , ml , appConfig ) )
2024-01-05 17:04:46 +00:00
// edit
2024-04-17 21:33:49 +00:00
app . Post ( "/v1/edits" , auth , openai . EditEndpoint ( cl , ml , appConfig ) )
app . Post ( "/edits" , auth , openai . EditEndpoint ( cl , ml , appConfig ) )
2024-01-05 17:04:46 +00:00
2024-03-26 17:54:35 +00:00
// assistant
2024-04-17 21:33:49 +00:00
app . Get ( "/v1/assistants" , auth , openai . ListAssistantsEndpoint ( cl , ml , appConfig ) )
app . Get ( "/assistants" , auth , openai . ListAssistantsEndpoint ( cl , ml , appConfig ) )
app . Post ( "/v1/assistants" , auth , openai . CreateAssistantEndpoint ( cl , ml , appConfig ) )
app . Post ( "/assistants" , auth , openai . CreateAssistantEndpoint ( cl , ml , appConfig ) )
app . Delete ( "/v1/assistants/:assistant_id" , auth , openai . DeleteAssistantEndpoint ( cl , ml , appConfig ) )
app . Delete ( "/assistants/:assistant_id" , auth , openai . DeleteAssistantEndpoint ( cl , ml , appConfig ) )
app . Get ( "/v1/assistants/:assistant_id" , auth , openai . GetAssistantEndpoint ( cl , ml , appConfig ) )
app . Get ( "/assistants/:assistant_id" , auth , openai . GetAssistantEndpoint ( cl , ml , appConfig ) )
app . Post ( "/v1/assistants/:assistant_id" , auth , openai . ModifyAssistantEndpoint ( cl , ml , appConfig ) )
app . Post ( "/assistants/:assistant_id" , auth , openai . ModifyAssistantEndpoint ( cl , ml , appConfig ) )
app . Get ( "/v1/assistants/:assistant_id/files" , auth , openai . ListAssistantFilesEndpoint ( cl , ml , appConfig ) )
app . Get ( "/assistants/:assistant_id/files" , auth , openai . ListAssistantFilesEndpoint ( cl , ml , appConfig ) )
app . Post ( "/v1/assistants/:assistant_id/files" , auth , openai . CreateAssistantFileEndpoint ( cl , ml , appConfig ) )
app . Post ( "/assistants/:assistant_id/files" , auth , openai . CreateAssistantFileEndpoint ( cl , ml , appConfig ) )
app . Delete ( "/v1/assistants/:assistant_id/files/:file_id" , auth , openai . DeleteAssistantFileEndpoint ( cl , ml , appConfig ) )
app . Delete ( "/assistants/:assistant_id/files/:file_id" , auth , openai . DeleteAssistantFileEndpoint ( cl , ml , appConfig ) )
app . Get ( "/v1/assistants/:assistant_id/files/:file_id" , auth , openai . GetAssistantFileEndpoint ( cl , ml , appConfig ) )
app . Get ( "/assistants/:assistant_id/files/:file_id" , auth , openai . GetAssistantFileEndpoint ( cl , ml , appConfig ) )
2024-03-26 17:54:35 +00:00
2024-02-18 10:12:02 +00:00
// files
2024-04-17 21:33:49 +00:00
app . Post ( "/v1/files" , auth , openai . UploadFilesEndpoint ( cl , appConfig ) )
app . Post ( "/files" , auth , openai . UploadFilesEndpoint ( cl , appConfig ) )
app . Get ( "/v1/files" , auth , openai . ListFilesEndpoint ( cl , appConfig ) )
app . Get ( "/files" , auth , openai . ListFilesEndpoint ( cl , appConfig ) )
app . Get ( "/v1/files/:file_id" , auth , openai . GetFilesEndpoint ( cl , appConfig ) )
app . Get ( "/files/:file_id" , auth , openai . GetFilesEndpoint ( cl , appConfig ) )
app . Delete ( "/v1/files/:file_id" , auth , openai . DeleteFilesEndpoint ( cl , appConfig ) )
app . Delete ( "/files/:file_id" , auth , openai . DeleteFilesEndpoint ( cl , appConfig ) )
app . Get ( "/v1/files/:file_id/content" , auth , openai . GetFilesContentsEndpoint ( cl , appConfig ) )
app . Get ( "/files/:file_id/content" , auth , openai . GetFilesContentsEndpoint ( cl , appConfig ) )
2024-02-18 10:12:02 +00:00
2024-01-05 17:04:46 +00:00
// completion
2024-04-17 21:33:49 +00:00
app . Post ( "/v1/completions" , auth , openai . CompletionEndpoint ( cl , ml , appConfig ) )
app . Post ( "/completions" , auth , openai . CompletionEndpoint ( cl , ml , appConfig ) )
app . Post ( "/v1/engines/:model/completions" , auth , openai . CompletionEndpoint ( cl , ml , appConfig ) )
2024-01-05 17:04:46 +00:00
// embeddings
2024-04-17 21:33:49 +00:00
app . Post ( "/v1/embeddings" , auth , openai . EmbeddingsEndpoint ( cl , ml , appConfig ) )
app . Post ( "/embeddings" , auth , openai . EmbeddingsEndpoint ( cl , ml , appConfig ) )
app . Post ( "/v1/engines/:model/embeddings" , auth , openai . EmbeddingsEndpoint ( cl , ml , appConfig ) )
2024-01-05 17:04:46 +00:00
// audio
2024-04-17 21:33:49 +00:00
app . Post ( "/v1/audio/transcriptions" , auth , openai . TranscriptEndpoint ( cl , ml , appConfig ) )
app . Post ( "/v1/audio/speech" , auth , localai . TTSEndpoint ( cl , ml , appConfig ) )
2024-01-05 17:04:46 +00:00
// images
2024-04-17 21:33:49 +00:00
app . Post ( "/v1/images/generations" , auth , openai . ImageEndpoint ( cl , ml , appConfig ) )
2024-01-05 17:04:46 +00:00
2024-04-17 21:33:49 +00:00
if appConfig . ImageDir != "" {
app . Static ( "/generated-images" , appConfig . ImageDir )
2024-01-05 17:04:46 +00:00
}
2024-04-17 21:33:49 +00:00
if appConfig . AudioDir != "" {
app . Static ( "/generated-audio" , appConfig . AudioDir )
2024-01-05 17:04:46 +00:00
}
ok := func ( c * fiber . Ctx ) error {
return c . SendStatus ( 200 )
}
// Kubernetes health checks
app . Get ( "/healthz" , ok )
app . Get ( "/readyz" , ok )
// Experimental Backend Statistics Module
2024-04-17 21:33:49 +00:00
backendMonitor := services . NewBackendMonitor ( cl , ml , appConfig ) // Split out for now
app . Get ( "/backend/monitor" , auth , localai . BackendMonitorEndpoint ( backendMonitor ) )
app . Post ( "/backend/shutdown" , auth , localai . BackendShutdownEndpoint ( backendMonitor ) )
2024-01-05 17:04:46 +00:00
// models
2024-04-17 21:33:49 +00:00
app . Get ( "/v1/models" , auth , openai . ListModelsEndpoint ( cl , ml ) )
app . Get ( "/models" , auth , openai . ListModelsEndpoint ( cl , ml ) )
2024-01-05 17:04:46 +00:00
2024-03-28 20:52:52 +00:00
app . Get ( "/metrics" , auth , localai . LocalAIMetricsEndpoint ( ) )
2024-01-05 17:04:46 +00:00
2024-03-27 20:10:58 +00:00
// Define a custom 404 handler
2024-03-28 20:52:52 +00:00
// Note: keep this at the bottom!
app . Use ( notFoundHandler )
2024-03-27 20:10:58 +00:00
2024-01-05 17:04:46 +00:00
return app , nil
}