mirror of
https://github.com/mudler/LocalAI.git
synced 2026-05-24 08:38:02 -04:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc11323d1c | ||
|
|
e88468640f | ||
|
|
81890e76a0 | ||
|
|
a91c2e7aaa | ||
|
|
7748eb6553 | ||
|
|
835932e95e | ||
|
|
ae1ec4e096 | ||
|
|
c75ecfa009 | ||
|
|
8737a65760 | ||
|
|
418c582430 | ||
|
|
6fd0341eca | ||
|
|
ccc7cb0287 | ||
|
|
a1d6cc93a8 | ||
|
|
dc14d80f51 | ||
|
|
b8eb10b6b7 | ||
|
|
0f6b4513bf | ||
|
|
6f0c936f74 | ||
|
|
42136b6f27 | ||
|
|
2810e3ea5c | ||
|
|
11d34e38dc | ||
|
|
06951cdd6b | ||
|
|
103af480c7 | ||
|
|
db401b4d84 | ||
|
|
e0c876aae1 | ||
|
|
5e0847b3d7 | ||
|
|
ee5ca49bc1 |
8
.github/workflows/test-extra.yml
vendored
8
.github/workflows/test-extra.yml
vendored
@@ -105,6 +105,14 @@ jobs:
|
|||||||
tests-parler-tts:
|
tests-parler-tts:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Force Install GIT latest
|
||||||
|
run: |
|
||||||
|
sudo apt-get update \
|
||||||
|
&& sudo apt-get install -y software-properties-common \
|
||||||
|
&& sudo apt-get update \
|
||||||
|
&& sudo add-apt-repository -y ppa:git-core/ppa \
|
||||||
|
&& sudo apt-get update \
|
||||||
|
&& sudo apt-get install -y git
|
||||||
- name: Clone
|
- name: Clone
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -8,7 +8,7 @@ DETECT_LIBS?=true
|
|||||||
# llama.cpp versions
|
# llama.cpp versions
|
||||||
GOLLAMA_REPO?=https://github.com/go-skynet/go-llama.cpp
|
GOLLAMA_REPO?=https://github.com/go-skynet/go-llama.cpp
|
||||||
GOLLAMA_VERSION?=2b57a8ae43e4699d3dc5d1496a1ccd42922993be
|
GOLLAMA_VERSION?=2b57a8ae43e4699d3dc5d1496a1ccd42922993be
|
||||||
CPPLLAMA_VERSION?=45f097645efb11b6d09a5b4adbbfd7c312ac0126
|
CPPLLAMA_VERSION?=0a1c750c80147687df267114c81956757cc14382
|
||||||
|
|
||||||
# go-rwkv version
|
# go-rwkv version
|
||||||
RWKV_REPO?=https://github.com/donomii/go-rwkv.cpp
|
RWKV_REPO?=https://github.com/donomii/go-rwkv.cpp
|
||||||
@@ -16,7 +16,7 @@ RWKV_VERSION?=661e7ae26d442f5cfebd2a0881b44e8c55949ec6
|
|||||||
|
|
||||||
# whisper.cpp version
|
# whisper.cpp version
|
||||||
WHISPER_REPO?=https://github.com/ggerganov/whisper.cpp
|
WHISPER_REPO?=https://github.com/ggerganov/whisper.cpp
|
||||||
WHISPER_CPP_VERSION?=a5abfe6a90495f7bf19fe70d016ecc255e97359c
|
WHISPER_CPP_VERSION?=0fbaac9c891055796456df7b9122a70c220f9ca1
|
||||||
|
|
||||||
# bert.cpp version
|
# bert.cpp version
|
||||||
BERT_REPO?=https://github.com/go-skynet/go-bert.cpp
|
BERT_REPO?=https://github.com/go-skynet/go-bert.cpp
|
||||||
|
|||||||
@@ -219,6 +219,7 @@ message ModelOptions {
|
|||||||
int32 SwapSpace = 53;
|
int32 SwapSpace = 53;
|
||||||
int32 MaxModelLen = 54;
|
int32 MaxModelLen = 54;
|
||||||
int32 TensorParallelSize = 55;
|
int32 TensorParallelSize = 55;
|
||||||
|
string LoadFormat = 58;
|
||||||
|
|
||||||
string MMProj = 41;
|
string MMProj = 41;
|
||||||
|
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ installRequirements
|
|||||||
|
|
||||||
# https://github.com/descriptinc/audiotools/issues/101
|
# https://github.com/descriptinc/audiotools/issues/101
|
||||||
# incompatible protobuf versions.
|
# incompatible protobuf versions.
|
||||||
PYDIR=python3.10
|
# PYDIR=python3.10
|
||||||
pyenv="${MY_DIR}/venv/lib/${PYDIR}/site-packages/google/protobuf/internal/"
|
# pyenv="${MY_DIR}/venv/lib/${PYDIR}/site-packages/google/protobuf/internal/"
|
||||||
|
|
||||||
if [ ! -d ${pyenv} ]; then
|
# if [ ! -d ${pyenv} ]; then
|
||||||
echo "(parler-tts/install.sh): Error: ${pyenv} does not exist"
|
# echo "(parler-tts/install.sh): Error: ${pyenv} does not exist"
|
||||||
exit 1
|
# exit 1
|
||||||
fi
|
# fi
|
||||||
|
|
||||||
curl -L https://raw.githubusercontent.com/protocolbuffers/protobuf/main/python/google/protobuf/internal/builder.py -o ${pyenv}/builder.py
|
# curl -L https://raw.githubusercontent.com/protocolbuffers/protobuf/main/python/google/protobuf/internal/builder.py -o ${pyenv}/builder.py
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
git+https://github.com/huggingface/parler-tts.git@8e465f1b5fcd223478e07175cb40494d19ffbe17
|
git+https://github.com/huggingface/parler-tts.git@8e465f1b5fcd223478e07175cb40494d19ffbe17
|
||||||
llvmlite==0.43.0
|
llvmlite==0.43.0
|
||||||
numba==0.60.0
|
numba==0.60.0
|
||||||
|
git+https://github.com/descriptinc/audiotools
|
||||||
@@ -95,6 +95,8 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
|
|||||||
|
|
||||||
if request.Quantization != "":
|
if request.Quantization != "":
|
||||||
engine_args.quantization = request.Quantization
|
engine_args.quantization = request.Quantization
|
||||||
|
if request.LoadFormat != "":
|
||||||
|
engine_args.load_format = request.LoadFormat
|
||||||
if request.GPUMemoryUtilization != 0:
|
if request.GPUMemoryUtilization != 0:
|
||||||
engine_args.gpu_memory_utilization = request.GPUMemoryUtilization
|
engine_args.gpu_memory_utilization = request.GPUMemoryUtilization
|
||||||
if request.TrustRemoteCode:
|
if request.TrustRemoteCode:
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ func grpcModelOpts(c config.BackendConfig) *pb.ModelOptions {
|
|||||||
DraftModel: c.DraftModel,
|
DraftModel: c.DraftModel,
|
||||||
AudioPath: c.VallE.AudioPath,
|
AudioPath: c.VallE.AudioPath,
|
||||||
Quantization: c.Quantization,
|
Quantization: c.Quantization,
|
||||||
|
LoadFormat: c.LoadFormat,
|
||||||
GPUMemoryUtilization: c.GPUMemoryUtilization,
|
GPUMemoryUtilization: c.GPUMemoryUtilization,
|
||||||
TrustRemoteCode: c.TrustRemoteCode,
|
TrustRemoteCode: c.TrustRemoteCode,
|
||||||
EnforceEager: c.EnforceEager,
|
EnforceEager: c.EnforceEager,
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ type RunCMD struct {
|
|||||||
OpaqueErrors bool `env:"LOCALAI_OPAQUE_ERRORS" default:"false" help:"If true, all error responses are replaced with blank 500 errors. This is intended only for hardening against information leaks and is normally not recommended." group:"hardening"`
|
OpaqueErrors bool `env:"LOCALAI_OPAQUE_ERRORS" default:"false" help:"If true, all error responses are replaced with blank 500 errors. This is intended only for hardening against information leaks and is normally not recommended." group:"hardening"`
|
||||||
UseSubtleKeyComparison bool `env:"LOCALAI_SUBTLE_KEY_COMPARISON" default:"false" help:"If true, API Key validation comparisons will be performed using constant-time comparisons rather than simple equality. This trades off performance on each request for resiliancy against timing attacks." group:"hardening"`
|
UseSubtleKeyComparison bool `env:"LOCALAI_SUBTLE_KEY_COMPARISON" default:"false" help:"If true, API Key validation comparisons will be performed using constant-time comparisons rather than simple equality. This trades off performance on each request for resiliancy against timing attacks." group:"hardening"`
|
||||||
DisableApiKeyRequirementForHttpGet bool `env:"LOCALAI_DISABLE_API_KEY_REQUIREMENT_FOR_HTTP_GET" default:"false" help:"If true, a valid API key is not required to issue GET requests to portions of the web ui. This should only be enabled in secure testing environments" group:"hardening"`
|
DisableApiKeyRequirementForHttpGet bool `env:"LOCALAI_DISABLE_API_KEY_REQUIREMENT_FOR_HTTP_GET" default:"false" help:"If true, a valid API key is not required to issue GET requests to portions of the web ui. This should only be enabled in secure testing environments" group:"hardening"`
|
||||||
|
DisableMetricsEndpoint bool `env:"LOCALAI_DISABLE_METRICS_ENDPOINT,DISABLE_METRICS_ENDPOINT" default:"false" help:"Disable the /metrics endpoint" group:"api"`
|
||||||
HttpGetExemptedEndpoints []string `env:"LOCALAI_HTTP_GET_EXEMPTED_ENDPOINTS" default:"^/$,^/browse/?$,^/talk/?$,^/p2p/?$,^/chat/?$,^/text2image/?$,^/tts/?$,^/static/.*$,^/swagger.*$" help:"If LOCALAI_DISABLE_API_KEY_REQUIREMENT_FOR_HTTP_GET is overriden to true, this is the list of endpoints to exempt. Only adjust this in case of a security incident or as a result of a personal security posture review" group:"hardening"`
|
HttpGetExemptedEndpoints []string `env:"LOCALAI_HTTP_GET_EXEMPTED_ENDPOINTS" default:"^/$,^/browse/?$,^/talk/?$,^/p2p/?$,^/chat/?$,^/text2image/?$,^/tts/?$,^/static/.*$,^/swagger.*$" help:"If LOCALAI_DISABLE_API_KEY_REQUIREMENT_FOR_HTTP_GET is overriden to true, this is the list of endpoints to exempt. Only adjust this in case of a security incident or as a result of a personal security posture review" group:"hardening"`
|
||||||
Peer2Peer bool `env:"LOCALAI_P2P,P2P" name:"p2p" default:"false" help:"Enable P2P mode" group:"p2p"`
|
Peer2Peer bool `env:"LOCALAI_P2P,P2P" name:"p2p" default:"false" help:"Enable P2P mode" group:"p2p"`
|
||||||
Peer2PeerDHTInterval int `env:"LOCALAI_P2P_DHT_INTERVAL,P2P_DHT_INTERVAL" default:"360" name:"p2p-dht-interval" help:"Interval for DHT refresh (used during token generation)" group:"p2p"`
|
Peer2PeerDHTInterval int `env:"LOCALAI_P2P_DHT_INTERVAL,P2P_DHT_INTERVAL" default:"360" name:"p2p-dht-interval" help:"Interval for DHT refresh (used during token generation)" group:"p2p"`
|
||||||
@@ -108,6 +109,10 @@ func (r *RunCMD) Run(ctx *cliContext.Context) error {
|
|||||||
config.WithLoadToMemory(r.LoadToMemory),
|
config.WithLoadToMemory(r.LoadToMemory),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.DisableMetricsEndpoint {
|
||||||
|
opts = append(opts, config.DisableMetricsEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
token := ""
|
token := ""
|
||||||
if r.Peer2Peer || r.Peer2PeerToken != "" {
|
if r.Peer2Peer || r.Peer2PeerToken != "" {
|
||||||
log.Info().Msg("P2P mode enabled")
|
log.Info().Msg("P2P mode enabled")
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ type ApplicationConfig struct {
|
|||||||
OpaqueErrors bool
|
OpaqueErrors bool
|
||||||
UseSubtleKeyComparison bool
|
UseSubtleKeyComparison bool
|
||||||
DisableApiKeyRequirementForHttpGet bool
|
DisableApiKeyRequirementForHttpGet bool
|
||||||
|
DisableMetrics bool
|
||||||
HttpGetExemptedEndpoints []*regexp.Regexp
|
HttpGetExemptedEndpoints []*regexp.Regexp
|
||||||
DisableGalleryEndpoint bool
|
DisableGalleryEndpoint bool
|
||||||
LoadToMemory []string
|
LoadToMemory []string
|
||||||
@@ -350,6 +351,10 @@ func WithDisableApiKeyRequirementForHttpGet(required bool) AppOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DisableMetricsEndpoint AppOption = func(o *ApplicationConfig) {
|
||||||
|
o.DisableMetrics = true
|
||||||
|
}
|
||||||
|
|
||||||
func WithHttpGetExemptedEndpoints(endpoints []string) AppOption {
|
func WithHttpGetExemptedEndpoints(endpoints []string) AppOption {
|
||||||
return func(o *ApplicationConfig) {
|
return func(o *ApplicationConfig) {
|
||||||
o.HttpGetExemptedEndpoints = []*regexp.Regexp{}
|
o.HttpGetExemptedEndpoints = []*regexp.Regexp{}
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ type LLMConfig struct {
|
|||||||
DraftModel string `yaml:"draft_model"`
|
DraftModel string `yaml:"draft_model"`
|
||||||
NDraft int32 `yaml:"n_draft"`
|
NDraft int32 `yaml:"n_draft"`
|
||||||
Quantization string `yaml:"quantization"`
|
Quantization string `yaml:"quantization"`
|
||||||
|
LoadFormat string `yaml:"load_format"`
|
||||||
GPUMemoryUtilization float32 `yaml:"gpu_memory_utilization"` // vLLM
|
GPUMemoryUtilization float32 `yaml:"gpu_memory_utilization"` // vLLM
|
||||||
TrustRemoteCode bool `yaml:"trust_remote_code"` // vLLM
|
TrustRemoteCode bool `yaml:"trust_remote_code"` // vLLM
|
||||||
EnforceEager bool `yaml:"enforce_eager"` // vLLM
|
EnforceEager bool `yaml:"enforce_eager"` // vLLM
|
||||||
@@ -197,9 +198,7 @@ type TemplateConfig struct {
|
|||||||
// It defaults to \n
|
// It defaults to \n
|
||||||
JoinChatMessagesByCharacter *string `yaml:"join_chat_messages_by_character"`
|
JoinChatMessagesByCharacter *string `yaml:"join_chat_messages_by_character"`
|
||||||
|
|
||||||
Video string `yaml:"video"`
|
Multimodal string `yaml:"multimodal"`
|
||||||
Image string `yaml:"image"`
|
|
||||||
Audio string `yaml:"audio"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *BackendConfig) UnmarshalYAML(value *yaml.Node) error {
|
func (c *BackendConfig) UnmarshalYAML(value *yaml.Node) error {
|
||||||
|
|||||||
@@ -109,19 +109,21 @@ func App(cl *config.BackendConfigLoader, ml *model.ModelLoader, appConfig *confi
|
|||||||
app.Use(recover.New())
|
app.Use(recover.New())
|
||||||
}
|
}
|
||||||
|
|
||||||
metricsService, err := services.NewLocalAIMetricsService()
|
if !appConfig.DisableMetrics {
|
||||||
if err != nil {
|
metricsService, err := services.NewLocalAIMetricsService()
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if metricsService != nil {
|
if metricsService != nil {
|
||||||
app.Use(localai.LocalAIMetricsAPIMiddleware(metricsService))
|
app.Use(localai.LocalAIMetricsAPIMiddleware(metricsService))
|
||||||
app.Hooks().OnShutdown(func() error {
|
app.Hooks().OnShutdown(func() error {
|
||||||
return metricsService.Shutdown()
|
return metricsService.Shutdown()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Health Checks should always be exempt from auth, so register these first
|
}
|
||||||
|
// Health Checks should always be exempt from auth, so register these first
|
||||||
routes.HealthRoutes(app)
|
routes.HealthRoutes(app)
|
||||||
|
|
||||||
kaConfig, err := middleware.GetKeyAuthConfig(appConfig)
|
kaConfig, err := middleware.GetKeyAuthConfig(appConfig)
|
||||||
|
|||||||
@@ -149,6 +149,10 @@ func updateRequestConfig(config *config.BackendConfig, input *schema.OpenAIReque
|
|||||||
// Decode each request's message content
|
// Decode each request's message content
|
||||||
imgIndex, vidIndex, audioIndex := 0, 0, 0
|
imgIndex, vidIndex, audioIndex := 0, 0, 0
|
||||||
for i, m := range input.Messages {
|
for i, m := range input.Messages {
|
||||||
|
nrOfImgsInMessage := 0
|
||||||
|
nrOfVideosInMessage := 0
|
||||||
|
nrOfAudiosInMessage := 0
|
||||||
|
|
||||||
switch content := m.Content.(type) {
|
switch content := m.Content.(type) {
|
||||||
case string:
|
case string:
|
||||||
input.Messages[i].StringContent = content
|
input.Messages[i].StringContent = content
|
||||||
@@ -156,11 +160,16 @@ func updateRequestConfig(config *config.BackendConfig, input *schema.OpenAIReque
|
|||||||
dat, _ := json.Marshal(content)
|
dat, _ := json.Marshal(content)
|
||||||
c := []schema.Content{}
|
c := []schema.Content{}
|
||||||
json.Unmarshal(dat, &c)
|
json.Unmarshal(dat, &c)
|
||||||
|
|
||||||
|
textContent := ""
|
||||||
|
// we will template this at the end
|
||||||
|
|
||||||
CONTENT:
|
CONTENT:
|
||||||
for _, pp := range c {
|
for _, pp := range c {
|
||||||
switch pp.Type {
|
switch pp.Type {
|
||||||
case "text":
|
case "text":
|
||||||
input.Messages[i].StringContent = pp.Text
|
textContent += pp.Text
|
||||||
|
//input.Messages[i].StringContent = pp.Text
|
||||||
case "video", "video_url":
|
case "video", "video_url":
|
||||||
// Decode content as base64 either if it's an URL or base64 text
|
// Decode content as base64 either if it's an URL or base64 text
|
||||||
base64, err := utils.GetContentURIAsBase64(pp.VideoURL.URL)
|
base64, err := utils.GetContentURIAsBase64(pp.VideoURL.URL)
|
||||||
@@ -169,14 +178,8 @@ func updateRequestConfig(config *config.BackendConfig, input *schema.OpenAIReque
|
|||||||
continue CONTENT
|
continue CONTENT
|
||||||
}
|
}
|
||||||
input.Messages[i].StringVideos = append(input.Messages[i].StringVideos, base64) // TODO: make sure that we only return base64 stuff
|
input.Messages[i].StringVideos = append(input.Messages[i].StringVideos, base64) // TODO: make sure that we only return base64 stuff
|
||||||
|
|
||||||
t := "[vid-{{.ID}}]{{.Text}}"
|
|
||||||
if config.TemplateConfig.Video != "" {
|
|
||||||
t = config.TemplateConfig.Video
|
|
||||||
}
|
|
||||||
// set a placeholder for each image
|
|
||||||
input.Messages[i].StringContent, _ = templates.TemplateMultiModal(t, vidIndex, input.Messages[i].StringContent)
|
|
||||||
vidIndex++
|
vidIndex++
|
||||||
|
nrOfVideosInMessage++
|
||||||
case "audio_url", "audio":
|
case "audio_url", "audio":
|
||||||
// Decode content as base64 either if it's an URL or base64 text
|
// Decode content as base64 either if it's an URL or base64 text
|
||||||
base64, err := utils.GetContentURIAsBase64(pp.AudioURL.URL)
|
base64, err := utils.GetContentURIAsBase64(pp.AudioURL.URL)
|
||||||
@@ -185,13 +188,8 @@ func updateRequestConfig(config *config.BackendConfig, input *schema.OpenAIReque
|
|||||||
continue CONTENT
|
continue CONTENT
|
||||||
}
|
}
|
||||||
input.Messages[i].StringAudios = append(input.Messages[i].StringAudios, base64) // TODO: make sure that we only return base64 stuff
|
input.Messages[i].StringAudios = append(input.Messages[i].StringAudios, base64) // TODO: make sure that we only return base64 stuff
|
||||||
// set a placeholder for each image
|
|
||||||
t := "[audio-{{.ID}}]{{.Text}}"
|
|
||||||
if config.TemplateConfig.Audio != "" {
|
|
||||||
t = config.TemplateConfig.Audio
|
|
||||||
}
|
|
||||||
input.Messages[i].StringContent, _ = templates.TemplateMultiModal(t, audioIndex, input.Messages[i].StringContent)
|
|
||||||
audioIndex++
|
audioIndex++
|
||||||
|
nrOfAudiosInMessage++
|
||||||
case "image_url", "image":
|
case "image_url", "image":
|
||||||
// Decode content as base64 either if it's an URL or base64 text
|
// Decode content as base64 either if it's an URL or base64 text
|
||||||
base64, err := utils.GetContentURIAsBase64(pp.ImageURL.URL)
|
base64, err := utils.GetContentURIAsBase64(pp.ImageURL.URL)
|
||||||
@@ -200,16 +198,21 @@ func updateRequestConfig(config *config.BackendConfig, input *schema.OpenAIReque
|
|||||||
continue CONTENT
|
continue CONTENT
|
||||||
}
|
}
|
||||||
|
|
||||||
t := "[img-{{.ID}}]{{.Text}}"
|
|
||||||
if config.TemplateConfig.Image != "" {
|
|
||||||
t = config.TemplateConfig.Image
|
|
||||||
}
|
|
||||||
input.Messages[i].StringImages = append(input.Messages[i].StringImages, base64) // TODO: make sure that we only return base64 stuff
|
input.Messages[i].StringImages = append(input.Messages[i].StringImages, base64) // TODO: make sure that we only return base64 stuff
|
||||||
// set a placeholder for each image
|
|
||||||
input.Messages[i].StringContent, _ = templates.TemplateMultiModal(t, imgIndex, input.Messages[i].StringContent)
|
|
||||||
imgIndex++
|
imgIndex++
|
||||||
|
nrOfImgsInMessage++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.Messages[i].StringContent, _ = templates.TemplateMultiModal(config.TemplateConfig.Multimodal, templates.MultiModalOptions{
|
||||||
|
TotalImages: imgIndex,
|
||||||
|
TotalVideos: vidIndex,
|
||||||
|
TotalAudios: audioIndex,
|
||||||
|
ImagesInMessage: nrOfImgsInMessage,
|
||||||
|
VideosInMessage: nrOfVideosInMessage,
|
||||||
|
AudiosInMessage: nrOfAudiosInMessage,
|
||||||
|
}, textContent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ func RegisterLocalAIRoutes(app *fiber.App,
|
|||||||
app.Post("/stores/get", localai.StoresGetEndpoint(sl, appConfig))
|
app.Post("/stores/get", localai.StoresGetEndpoint(sl, appConfig))
|
||||||
app.Post("/stores/find", localai.StoresFindEndpoint(sl, appConfig))
|
app.Post("/stores/find", localai.StoresFindEndpoint(sl, appConfig))
|
||||||
|
|
||||||
app.Get("/metrics", localai.LocalAIMetricsEndpoint())
|
if !appConfig.DisableMetrics {
|
||||||
|
app.Get("/metrics", localai.LocalAIMetricsEndpoint())
|
||||||
|
}
|
||||||
|
|
||||||
// Experimental Backend Statistics Module
|
// Experimental Backend Statistics Module
|
||||||
backendMonitorService := services.NewBackendMonitorService(ml, cl, appConfig) // Split out for now
|
backendMonitorService := services.NewBackendMonitorService(ml, cl, appConfig) // Split out for now
|
||||||
|
|||||||
@@ -30,6 +30,19 @@ For a full list of options, refer to the [Installer Options]({{% relref "docs/ad
|
|||||||
|
|
||||||
Binaries can also be [manually downloaded]({{% relref "docs/reference/binaries" %}}).
|
Binaries can also be [manually downloaded]({{% relref "docs/reference/binaries" %}}).
|
||||||
|
|
||||||
|
## Using Homebrew on MacOS
|
||||||
|
|
||||||
|
{{% alert icon="⚠️" %}}
|
||||||
|
The Homebrew formula currently doesn't have the same options than the bash script
|
||||||
|
{{% /alert %}}
|
||||||
|
|
||||||
|
You can install Homebrew's [LocalAI](https://formulae.brew.sh/formula/localai) with the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
brew install localai
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Using Container Images or Kubernetes
|
## Using Container Images or Kubernetes
|
||||||
|
|
||||||
LocalAI is available as a container image compatible with various container engines such as Docker, Podman, and Kubernetes. Container images are published on [quay.io](https://quay.io/repository/go-skynet/local-ai?tab=tags&tag=latest) and [Docker Hub](https://hub.docker.com/r/localai/localai).
|
LocalAI is available as a container image compatible with various container engines such as Docker, Podman, and Kubernetes. Container images are published on [quay.io](https://quay.io/repository/go-skynet/local-ai?tab=tags&tag=latest) and [Docker Hub](https://hub.docker.com/r/localai/localai).
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"version": "v2.22.0"
|
"version": "v2.22.1"
|
||||||
}
|
}
|
||||||
|
|||||||
2
docs/themes/hugo-theme-relearn
vendored
2
docs/themes/hugo-theme-relearn
vendored
Submodule docs/themes/hugo-theme-relearn updated: 007cc20686...06e70da8a6
@@ -1,6 +1,6 @@
|
|||||||
llama_index==0.11.17
|
llama_index==0.11.19
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
weaviate_client==4.8.1
|
weaviate_client==4.9.0
|
||||||
transformers
|
transformers
|
||||||
torch
|
torch
|
||||||
chainlit
|
chainlit
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
langchain==0.3.3
|
langchain==0.3.4
|
||||||
openai==1.51.2
|
openai==1.52.0
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
langchain==0.3.3
|
langchain==0.3.3
|
||||||
openai==1.51.2
|
openai==1.52.0
|
||||||
chromadb==0.5.13
|
chromadb==0.5.13
|
||||||
llama-index==0.11.17
|
llama-index==0.11.19
|
||||||
@@ -11,8 +11,8 @@ frozenlist==1.4.1
|
|||||||
greenlet==3.1.1
|
greenlet==3.1.1
|
||||||
idna==3.10
|
idna==3.10
|
||||||
langchain==0.3.3
|
langchain==0.3.3
|
||||||
langchain-community==0.3.2
|
langchain-community==0.3.3
|
||||||
marshmallow==3.22.0
|
marshmallow==3.23.0
|
||||||
marshmallow-enum==1.5.1
|
marshmallow-enum==1.5.1
|
||||||
multidict==6.1.0
|
multidict==6.1.0
|
||||||
mypy-extensions==1.0.0
|
mypy-extensions==1.0.0
|
||||||
@@ -24,10 +24,10 @@ packaging>=23.2
|
|||||||
pydantic==2.9.2
|
pydantic==2.9.2
|
||||||
PyYAML==6.0.2
|
PyYAML==6.0.2
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
SQLAlchemy==2.0.35
|
SQLAlchemy==2.0.36
|
||||||
tenacity==8.5.0
|
tenacity==8.5.0
|
||||||
tqdm==4.66.5
|
tqdm==4.66.5
|
||||||
typing-inspect==0.9.0
|
typing-inspect==0.9.0
|
||||||
typing_extensions==4.12.2
|
typing_extensions==4.12.2
|
||||||
urllib3==2.2.3
|
urllib3==2.2.3
|
||||||
yarl==1.15.2
|
yarl==1.16.0
|
||||||
|
|||||||
@@ -20,4 +20,6 @@ config_file: |
|
|||||||
completion: |
|
completion: |
|
||||||
{{.Input}}
|
{{.Input}}
|
||||||
use_tokenizer_template: false
|
use_tokenizer_template: false
|
||||||
|
multimodal: "{{ range .Images }}<|image_{{ add1 .ID}}|>{{end}}\n{{.Text}}"
|
||||||
|
# XXX: The one below can be dropped after a new release is out
|
||||||
image: "<|image_{{ add1 .ID }}|>\n{{.Text}}"
|
image: "<|image_{{ add1 .ID }}|>\n{{.Text}}"
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -231,7 +231,7 @@ require (
|
|||||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
github.com/moby/term v0.5.0 // indirect
|
github.com/moby/term v0.5.0 // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
github.com/mudler/go-piper v0.0.0-20240315144837-9d0100873a7d
|
github.com/mudler/go-piper v0.0.0-20241022074816-3854e0221ffb
|
||||||
github.com/mudler/water v0.0.0-20221010214108-8c7313014ce0 // indirect
|
github.com/mudler/water v0.0.0-20221010214108-8c7313014ce0 // indirect
|
||||||
github.com/muesli/reflow v0.3.0 // indirect
|
github.com/muesli/reflow v0.3.0 // indirect
|
||||||
github.com/muesli/termenv v0.15.2 // indirect
|
github.com/muesli/termenv v0.15.2 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -498,6 +498,8 @@ github.com/mudler/edgevpn v0.28.3 h1:yIuoMExwKHy/mNMBXIsm6FUFbnB9ELIxw9KXrK9KHDk
|
|||||||
github.com/mudler/edgevpn v0.28.3/go.mod h1:HWcdIwj5zBgOD04Hn3I+J5E5Yb3kK1CwwWaEe6/QERo=
|
github.com/mudler/edgevpn v0.28.3/go.mod h1:HWcdIwj5zBgOD04Hn3I+J5E5Yb3kK1CwwWaEe6/QERo=
|
||||||
github.com/mudler/go-piper v0.0.0-20240315144837-9d0100873a7d h1:8udOFrDf/I83JL0/u22j6U6Q9z9LoSdby2a/DWdd0/s=
|
github.com/mudler/go-piper v0.0.0-20240315144837-9d0100873a7d h1:8udOFrDf/I83JL0/u22j6U6Q9z9LoSdby2a/DWdd0/s=
|
||||||
github.com/mudler/go-piper v0.0.0-20240315144837-9d0100873a7d/go.mod h1:O7SwdSWMilAWhBZMK9N9Y/oBDyMMzshE3ju8Xkexwig=
|
github.com/mudler/go-piper v0.0.0-20240315144837-9d0100873a7d/go.mod h1:O7SwdSWMilAWhBZMK9N9Y/oBDyMMzshE3ju8Xkexwig=
|
||||||
|
github.com/mudler/go-piper v0.0.0-20241022074816-3854e0221ffb h1:5qcuxQEpAqeV4ftV5nUt3/hB/RoTXq3MaaauOAedyXo=
|
||||||
|
github.com/mudler/go-piper v0.0.0-20241022074816-3854e0221ffb/go.mod h1:O7SwdSWMilAWhBZMK9N9Y/oBDyMMzshE3ju8Xkexwig=
|
||||||
github.com/mudler/go-processmanager v0.0.0-20240820160718-8b802d3ecf82 h1:FVT07EI8njvsD4tC2Hw8Xhactp5AWhsQWD4oTeQuSAU=
|
github.com/mudler/go-processmanager v0.0.0-20240820160718-8b802d3ecf82 h1:FVT07EI8njvsD4tC2Hw8Xhactp5AWhsQWD4oTeQuSAU=
|
||||||
github.com/mudler/go-processmanager v0.0.0-20240820160718-8b802d3ecf82/go.mod h1:Urp7LG5jylKoDq0663qeBh0pINGcRl35nXdKx82PSoU=
|
github.com/mudler/go-processmanager v0.0.0-20240820160718-8b802d3ecf82/go.mod h1:Urp7LG5jylKoDq0663qeBh0pINGcRl35nXdKx82PSoU=
|
||||||
github.com/mudler/go-stable-diffusion v0.0.0-20240429204715-4a3cd6aeae6f h1:cxtMSRkUfy+mjIQ3yMrU0txwQ4It913NEN4m1H8WWgo=
|
github.com/mudler/go-stable-diffusion v0.0.0-20240429204715-4a3cd6aeae6f h1:cxtMSRkUfy+mjIQ3yMrU0txwQ4It913NEN4m1H8WWgo=
|
||||||
|
|||||||
@@ -7,20 +7,60 @@ import (
|
|||||||
"github.com/Masterminds/sprig/v3"
|
"github.com/Masterminds/sprig/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TemplateMultiModal(templateString string, templateID int, text string) (string, error) {
|
type MultiModalOptions struct {
|
||||||
|
TotalImages int
|
||||||
|
TotalAudios int
|
||||||
|
TotalVideos int
|
||||||
|
|
||||||
|
ImagesInMessage int
|
||||||
|
AudiosInMessage int
|
||||||
|
VideosInMessage int
|
||||||
|
}
|
||||||
|
|
||||||
|
type MultimodalContent struct {
|
||||||
|
ID int
|
||||||
|
}
|
||||||
|
|
||||||
|
const DefaultMultiModalTemplate = "{{ range .Audio }}[audio-{{.ID}}]{{end}}{{ range .Images }}[img-{{.ID}}]{{end}}{{ range .Video }}[vid-{{.ID}}]{{end}}{{.Text}}"
|
||||||
|
|
||||||
|
func TemplateMultiModal(templateString string, opts MultiModalOptions, text string) (string, error) {
|
||||||
|
if templateString == "" {
|
||||||
|
templateString = DefaultMultiModalTemplate
|
||||||
|
}
|
||||||
|
|
||||||
// compile the template
|
// compile the template
|
||||||
tmpl, err := template.New("template").Funcs(sprig.FuncMap()).Parse(templateString)
|
tmpl, err := template.New("template").Funcs(sprig.FuncMap()).Parse(templateString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
videos := []MultimodalContent{}
|
||||||
|
for i := 0; i < opts.VideosInMessage; i++ {
|
||||||
|
videos = append(videos, MultimodalContent{ID: i + (opts.TotalVideos - opts.VideosInMessage)})
|
||||||
|
}
|
||||||
|
|
||||||
|
audios := []MultimodalContent{}
|
||||||
|
for i := 0; i < opts.AudiosInMessage; i++ {
|
||||||
|
audios = append(audios, MultimodalContent{ID: i + (opts.TotalAudios - opts.AudiosInMessage)})
|
||||||
|
}
|
||||||
|
|
||||||
|
images := []MultimodalContent{}
|
||||||
|
for i := 0; i < opts.ImagesInMessage; i++ {
|
||||||
|
images = append(images, MultimodalContent{ID: i + (opts.TotalImages - opts.ImagesInMessage)})
|
||||||
|
}
|
||||||
|
|
||||||
result := bytes.NewBuffer(nil)
|
result := bytes.NewBuffer(nil)
|
||||||
// execute the template
|
// execute the template
|
||||||
err = tmpl.Execute(result, struct {
|
err = tmpl.Execute(result, struct {
|
||||||
ID int
|
Audio []MultimodalContent
|
||||||
Text string
|
Images []MultimodalContent
|
||||||
|
Video []MultimodalContent
|
||||||
|
Text string
|
||||||
}{
|
}{
|
||||||
ID: templateID,
|
Audio: audios,
|
||||||
Text: text,
|
Images: images,
|
||||||
|
Video: videos,
|
||||||
|
Text: text,
|
||||||
})
|
})
|
||||||
return result.String(), err
|
return result.String(), err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,77 @@ import (
|
|||||||
var _ = Describe("EvaluateTemplate", func() {
|
var _ = Describe("EvaluateTemplate", func() {
|
||||||
Context("templating simple strings for multimodal chat", func() {
|
Context("templating simple strings for multimodal chat", func() {
|
||||||
It("should template messages correctly", func() {
|
It("should template messages correctly", func() {
|
||||||
result, err := TemplateMultiModal("[img-{{.ID}}]{{.Text}}", 1, "bar")
|
result, err := TemplateMultiModal("", MultiModalOptions{
|
||||||
|
TotalImages: 1,
|
||||||
|
TotalAudios: 0,
|
||||||
|
TotalVideos: 0,
|
||||||
|
ImagesInMessage: 1,
|
||||||
|
AudiosInMessage: 0,
|
||||||
|
VideosInMessage: 0,
|
||||||
|
}, "bar")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(result).To(Equal("[img-0]bar"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("should handle messages with more images correctly", func() {
|
||||||
|
result, err := TemplateMultiModal("", MultiModalOptions{
|
||||||
|
TotalImages: 2,
|
||||||
|
TotalAudios: 0,
|
||||||
|
TotalVideos: 0,
|
||||||
|
ImagesInMessage: 2,
|
||||||
|
AudiosInMessage: 0,
|
||||||
|
VideosInMessage: 0,
|
||||||
|
}, "bar")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(result).To(Equal("[img-0][img-1]bar"))
|
||||||
|
})
|
||||||
|
It("should handle messages with more images correctly", func() {
|
||||||
|
result, err := TemplateMultiModal("", MultiModalOptions{
|
||||||
|
TotalImages: 4,
|
||||||
|
TotalAudios: 1,
|
||||||
|
TotalVideos: 0,
|
||||||
|
ImagesInMessage: 2,
|
||||||
|
AudiosInMessage: 1,
|
||||||
|
VideosInMessage: 0,
|
||||||
|
}, "bar")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(result).To(Equal("[audio-0][img-2][img-3]bar"))
|
||||||
|
})
|
||||||
|
It("should handle messages with more images correctly", func() {
|
||||||
|
result, err := TemplateMultiModal("", MultiModalOptions{
|
||||||
|
TotalImages: 3,
|
||||||
|
TotalAudios: 1,
|
||||||
|
TotalVideos: 0,
|
||||||
|
ImagesInMessage: 1,
|
||||||
|
AudiosInMessage: 1,
|
||||||
|
VideosInMessage: 0,
|
||||||
|
}, "bar")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(result).To(Equal("[audio-0][img-2]bar"))
|
||||||
|
})
|
||||||
|
It("should handle messages with more images correctly", func() {
|
||||||
|
result, err := TemplateMultiModal("", MultiModalOptions{
|
||||||
|
TotalImages: 0,
|
||||||
|
TotalAudios: 0,
|
||||||
|
TotalVideos: 0,
|
||||||
|
ImagesInMessage: 0,
|
||||||
|
AudiosInMessage: 0,
|
||||||
|
VideosInMessage: 0,
|
||||||
|
}, "bar")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(result).To(Equal("bar"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Context("templating with custom defaults", func() {
|
||||||
|
It("should handle messages with more images correctly", func() {
|
||||||
|
result, err := TemplateMultiModal("{{ range .Audio }}[audio-{{ add1 .ID}}]{{end}}{{ range .Images }}[img-{{ add1 .ID}}]{{end}}{{ range .Video }}[vid-{{ add1 .ID}}]{{end}}{{.Text}}", MultiModalOptions{
|
||||||
|
TotalImages: 1,
|
||||||
|
TotalAudios: 0,
|
||||||
|
TotalVideos: 0,
|
||||||
|
ImagesInMessage: 1,
|
||||||
|
AudiosInMessage: 0,
|
||||||
|
VideosInMessage: 0,
|
||||||
|
}, "bar")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(result).To(Equal("[img-1]bar"))
|
Expect(result).To(Equal("[img-1]bar"))
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user