Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
b0fe0e0c27 chore(deps): bump github.com/swaggo/echo-swagger from 1.4.1 to 1.5.0
Bumps [github.com/swaggo/echo-swagger](https://github.com/swaggo/echo-swagger) from 1.4.1 to 1.5.0.
- [Release notes](https://github.com/swaggo/echo-swagger/releases)
- [Commits](https://github.com/swaggo/echo-swagger/compare/v1.4.1...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/swaggo/echo-swagger
  dependency-version: 1.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-18 21:22:19 +00:00
27 changed files with 226 additions and 227 deletions

View File

@@ -1,5 +1,5 @@
LLAMA_VERSION?=11c325c6e0666a30590cde390d5746a405e536b9
LLAMA_VERSION?=2b089c77580d347767f440205103e4da8ec33d89
LLAMA_REPO?=https://github.com/ggerganov/llama.cpp
CMAKE_ARGS?=

View File

@@ -8,7 +8,7 @@ JOBS?=$(shell nproc --ignore=1)
# whisper.cpp version
WHISPER_REPO?=https://github.com/ggml-org/whisper.cpp
WHISPER_CPP_VERSION?=21411d81ea736ed5d9cdea4df360d3c4b60a4adb
WHISPER_CPP_VERSION?=364c77f4ca2737e3287652e0e8a8c6dce3231bba
SO_TARGET?=libgowhisper.so
CMAKE_ARGS+=-DBUILD_SHARED_LIBS=OFF

View File

@@ -40,21 +40,7 @@ from compel import Compel, ReturnedEmbeddingsType
from optimum.quanto import freeze, qfloat8, quantize
from transformers import T5EncoderModel
from safetensors.torch import load_file
# Try to import sd_embed - it might not always be available
try:
from sd_embed.embedding_funcs import (
get_weighted_text_embeddings_sd15,
get_weighted_text_embeddings_sdxl,
get_weighted_text_embeddings_sd3,
get_weighted_text_embeddings_flux1,
)
SD_EMBED_AVAILABLE = True
except ImportError:
get_weighted_text_embeddings_sd15 = None
get_weighted_text_embeddings_sdxl = None
get_weighted_text_embeddings_sd3 = None
get_weighted_text_embeddings_flux1 = None
SD_EMBED_AVAILABLE = False
from sd_embed.embedding_funcs import get_weighted_text_embeddings_sd15, get_weighted_text_embeddings_sdxl, get_weighted_text_embeddings_sd3, get_weighted_text_embeddings_flux1
# Import LTX-2 specific utilities
from diffusers.pipelines.ltx2.export_utils import encode_video as ltx2_encode_video
@@ -63,9 +49,6 @@ from diffusers import LTX2VideoTransformer3DModel, GGUFQuantizationConfig
_ONE_DAY_IN_SECONDS = 60 * 60 * 24
COMPEL = os.environ.get("COMPEL", "0") == "1"
SD_EMBED = os.environ.get("SD_EMBED", "0") == "1"
# Warn if SD_EMBED is enabled but the module is not available
if SD_EMBED and not SD_EMBED_AVAILABLE:
print("WARNING: SD_EMBED is enabled but sd_embed module is not available. Falling back to standard prompt processing.", file=sys.stderr)
XPU = os.environ.get("XPU", "0") == "1"
CLIPSKIP = os.environ.get("CLIPSKIP", "1") == "1"
SAFETENSORS = os.environ.get("SAFETENSORS", "1") == "1"
@@ -196,7 +179,7 @@ def get_scheduler(name: str, config: dict = {}):
# Implement the BackendServicer class with the service methods
class BackendServicer(backend_pb2_grpc.BackendServicer):
def _load_pipeline(self, request, modelFile, fromSingleFile, torchType, variant, device_map=None):
def _load_pipeline(self, request, modelFile, fromSingleFile, torchType, variant):
"""
Load a diffusers pipeline dynamically using the dynamic loader.
@@ -210,7 +193,6 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
fromSingleFile: Whether to use from_single_file() vs from_pretrained()
torchType: The torch dtype to use
variant: Model variant (e.g., "fp16")
device_map: Device mapping strategy (e.g., "auto" for multi-GPU)
Returns:
The loaded pipeline instance
@@ -232,14 +214,14 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
dtype = torch.bfloat16
bfl_repo = os.environ.get("BFL_REPO", "ChuckMcSneed/FLUX.1-dev")
transformer = FluxTransformer2DModel.from_single_file(modelFile, torch_dtype=dtype, device_map=device_map)
transformer = FluxTransformer2DModel.from_single_file(modelFile, torch_dtype=dtype)
quantize(transformer, weights=qfloat8)
freeze(transformer)
text_encoder_2 = T5EncoderModel.from_pretrained(bfl_repo, subfolder="text_encoder_2", torch_dtype=dtype, device_map=device_map)
text_encoder_2 = T5EncoderModel.from_pretrained(bfl_repo, subfolder="text_encoder_2", torch_dtype=dtype)
quantize(text_encoder_2, weights=qfloat8)
freeze(text_encoder_2)
pipe = FluxPipeline.from_pretrained(bfl_repo, transformer=None, text_encoder_2=None, torch_dtype=dtype, device_map=device_map)
pipe = FluxPipeline.from_pretrained(bfl_repo, transformer=None, text_encoder_2=None, torch_dtype=dtype)
pipe.transformer = transformer
pipe.text_encoder_2 = text_encoder_2
@@ -252,15 +234,13 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
vae = AutoencoderKLWan.from_pretrained(
request.Model,
subfolder="vae",
torch_dtype=torch.float32,
device_map=device_map
torch_dtype=torch.float32
)
pipe = load_diffusers_pipeline(
class_name="WanPipeline",
model_id=request.Model,
vae=vae,
torch_dtype=torchType,
device_map=device_map
torch_dtype=torchType
)
self.txt2vid = True
return pipe
@@ -270,15 +250,13 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
vae = AutoencoderKLWan.from_pretrained(
request.Model,
subfolder="vae",
torch_dtype=torch.float32,
device_map=device_map
torch_dtype=torch.float32
)
pipe = load_diffusers_pipeline(
class_name="WanImageToVideoPipeline",
model_id=request.Model,
vae=vae,
torch_dtype=torchType,
device_map=device_map
torch_dtype=torchType
)
self.img2vid = True
return pipe
@@ -289,8 +267,7 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
class_name="SanaPipeline",
model_id=request.Model,
variant="bf16",
torch_dtype=torch.bfloat16,
device_map=device_map
torch_dtype=torch.bfloat16
)
pipe.vae.to(torch.bfloat16)
pipe.text_encoder.to(torch.bfloat16)
@@ -302,8 +279,7 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
pipe = load_diffusers_pipeline(
class_name="DiffusionPipeline",
model_id=request.Model,
torch_dtype=torchType,
device_map=device_map
torch_dtype=torchType
)
return pipe
@@ -314,8 +290,7 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
class_name="StableVideoDiffusionPipeline",
model_id=request.Model,
torch_dtype=torchType,
variant=variant,
device_map=device_map
variant=variant
)
if not DISABLE_CPU_OFFLOAD:
pipe.enable_model_cpu_offload()
@@ -339,7 +314,6 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
modelFile,
config=request.Model, # Use request.Model as the config/model_id
subfolder="transformer",
device_map=device_map,
**transformer_kwargs,
)
@@ -349,7 +323,6 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
model_id=request.Model,
transformer=transformer,
torch_dtype=torchType,
device_map=device_map,
)
else:
# Single file but not GGUF - use standard single file loading
@@ -358,7 +331,6 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
model_id=modelFile,
from_single_file=True,
torch_dtype=torchType,
device_map=device_map,
)
else:
# Standard loading from pretrained
@@ -366,8 +338,7 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
class_name="LTX2ImageToVideoPipeline",
model_id=request.Model,
torch_dtype=torchType,
variant=variant,
device_map=device_map
variant=variant
)
if not DISABLE_CPU_OFFLOAD:
@@ -392,7 +363,6 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
modelFile,
config=request.Model, # Use request.Model as the config/model_id
subfolder="transformer",
device_map=device_map,
**transformer_kwargs,
)
@@ -402,7 +372,6 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
model_id=request.Model,
transformer=transformer,
torch_dtype=torchType,
device_map=device_map,
)
else:
# Single file but not GGUF - use standard single file loading
@@ -411,7 +380,6 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
model_id=modelFile,
from_single_file=True,
torch_dtype=torchType,
device_map=device_map,
)
else:
# Standard loading from pretrained
@@ -419,8 +387,7 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
class_name="LTX2Pipeline",
model_id=request.Model,
torch_dtype=torchType,
variant=variant,
device_map=device_map
variant=variant
)
if not DISABLE_CPU_OFFLOAD:
@@ -443,10 +410,6 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
if not fromSingleFile:
load_kwargs["use_safetensors"] = SAFETENSORS
# Add device_map for multi-GPU support (when TensorParallelSize > 1)
if device_map:
load_kwargs["device_map"] = device_map
# Determine pipeline class name - default to AutoPipelineForText2Image
effective_pipeline_type = pipeline_type if pipeline_type else "AutoPipelineForText2Image"
@@ -549,13 +512,6 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
print(f"LoadModel: PipelineType from request: {request.PipelineType}", file=sys.stderr)
# Determine device_map for multi-GPU support based on TensorParallelSize
# When TensorParallelSize > 1, use device_map='auto' to distribute model across GPUs
device_map = None
if hasattr(request, 'TensorParallelSize') and request.TensorParallelSize > 1:
device_map = "auto"
print(f"LoadModel: Multi-GPU mode enabled with TensorParallelSize={request.TensorParallelSize}, using device_map='auto'", file=sys.stderr)
# Load pipeline using dynamic loader
# Special cases that require custom initialization are handled first
self.pipe = self._load_pipeline(
@@ -563,8 +519,7 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
modelFile=modelFile,
fromSingleFile=fromSingleFile,
torchType=torchType,
variant=variant,
device_map=device_map
variant=variant
)
print(f"LoadModel: After loading - ltx2_pipeline: {self.ltx2_pipeline}, img2vid: {self.img2vid}, txt2vid: {self.txt2vid}, PipelineType: {self.PipelineType}", file=sys.stderr)
@@ -589,7 +544,7 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
if request.ControlNet:
self.controlnet = ControlNetModel.from_pretrained(
request.ControlNet, torch_dtype=torchType, variant=variant, device_map=device_map
request.ControlNet, torch_dtype=torchType, variant=variant
)
self.pipe.controlnet = self.controlnet
else:
@@ -628,9 +583,7 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
self.pipe.set_adapters(adapters_name, adapter_weights=adapters_weights)
# Only move pipeline to device if NOT using device_map
# device_map handles device placement automatically
if device_map is None and device != "cpu":
if device != "cpu":
self.pipe.to(device)
if self.controlnet:
self.controlnet.to(device)
@@ -790,7 +743,7 @@ class BackendServicer(backend_pb2_grpc.BackendServicer):
guidance_scale=self.cfg_scale,
**kwargs
).images[0]
elif SD_EMBED and SD_EMBED_AVAILABLE:
elif SD_EMBED:
if self.PipelineType == "StableDiffusionPipeline":
(
kwargs["prompt_embeds"],

View File

@@ -4,6 +4,7 @@ git+https://github.com/huggingface/diffusers
transformers
accelerate
compel
git+https://github.com/xhinker/sd_embed
peft
optimum-quanto
numpy<2

View File

@@ -4,6 +4,7 @@ git+https://github.com/huggingface/diffusers
transformers
accelerate
compel
git+https://github.com/xhinker/sd_embed
peft
optimum-quanto
numpy<2

View File

@@ -5,6 +5,7 @@ opencv-python
transformers
accelerate
compel
git+https://github.com/xhinker/sd_embed
peft
sentencepiece
optimum-quanto

View File

@@ -55,22 +55,20 @@ func GetEditModelPage(cl *config.ModelConfigLoader, appConfig *config.Applicatio
// Render the edit page with the current configuration
templateData := struct {
Title string
ModelName string
Config *config.ModelConfig
ConfigJSON string
ConfigYAML string
BaseURL string
Version string
DisableRuntimeSettings bool
Title string
ModelName string
Config *config.ModelConfig
ConfigJSON string
ConfigYAML string
BaseURL string
Version string
}{
Title: "LocalAI - Edit Model " + modelName,
ModelName: modelName,
Config: &modelConfig,
ConfigYAML: string(configData),
BaseURL: httpUtils.BaseURL(c),
Version: internal.PrintableVersion(),
DisableRuntimeSettings: appConfig.DisableRuntimeSettings,
Title: "LocalAI - Edit Model " + modelName,
ModelName: modelName,
Config: &modelConfig,
ConfigYAML: string(configData),
BaseURL: httpUtils.BaseURL(c),
Version: internal.PrintableVersion(),
}
return c.Render(http.StatusOK, "views/model-editor", templateData)

View File

@@ -31,10 +31,9 @@ func RegisterLocalAIRoutes(router *echo.Echo,
// Import model page
router.GET("/import-model", func(c echo.Context) error {
return c.Render(200, "views/model-editor", map[string]interface{}{
"Title": "LocalAI - Import Model",
"BaseURL": middleware.BaseURL(c),
"Version": internal.PrintableVersion(),
"DisableRuntimeSettings": appConfig.DisableRuntimeSettings,
"Title": "LocalAI - Import Model",
"BaseURL": middleware.BaseURL(c),
"Version": internal.PrintableVersion(),
})
})

View File

@@ -39,11 +39,6 @@ body {
background-color: var(--color-bg-primary);
}
/* Pages without sidebar (e.g. login): center content */
.app-layout.no-sidebar .main-content {
margin-left: 0;
}
/* Chat page: fix viewport height so messages scroll and input stays fixed at bottom */
.app-layout.chat-layout {
height: 100vh;

View File

@@ -19,13 +19,15 @@
404 - Page Not Found
</h1>
<p class="text-xl text-[var(--color-text-secondary)] mb-6">The page you're looking for doesn't exist or has been moved</p>
<div class="flex flex-wrap justify-center gap-2">
<a href="./" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-home"></i>
<div class="flex flex-wrap justify-center gap-4">
<a href="./"
class="btn-primary">
<i class="fas fa-home mr-2"></i>
<span>Return Home</span>
</a>
<a href="browse/" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-images"></i>
<a href="browse/"
class="btn-secondary">
<i class="fas fa-images mr-2"></i>
<span>Browse Gallery</span>
</a>
</div>

View File

@@ -39,11 +39,11 @@
}"
class="px-4 py-2 rounded-lg text-sm font-semibold text-white"
x-text="job.status ? job.status.toUpperCase() : 'LOADING...'"></span>
<button type="button" x-show="job.status === 'pending' || job.status === 'running'"
<button x-show="job.status === 'pending' || job.status === 'running'"
@click="cancelJob()"
class="inline-flex items-center gap-1.5 text-xs text-red-400/90 hover:text-red-400 bg-transparent hover:bg-red-500/10 border border-[var(--color-border-subtle)] hover:border-red-500/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-stop"></i>
<span>Cancel</span>
class="btn-primary"
style="background: var(--color-error);">
<i class="fas fa-stop mr-2"></i>Cancel
</button>
</div>
</div>

View File

@@ -61,10 +61,10 @@
<span class="text-[var(--color-text-secondary)] mr-1">Capability:</span>
<span class="font-semibold text-[var(--color-primary)]" x-text="systemCapability"></span>
</div>
<a href="https://localai.io/backends/" target="_blank" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-info-circle"></i>
<a href="https://localai.io/backends/" target="_blank" class="btn-primary">
<i class="fas fa-info-circle mr-2"></i>
<span>Documentation</span>
<i class="fas fa-external-link-alt text-[10px]"></i>
<i class="fas fa-external-link-alt ml-2 text-xs"></i>
</a>
</div>
</div>
@@ -119,12 +119,12 @@
</div>
<div class="flex items-center gap-4">
<button type="button"
<button
@click="installExternalBackend()"
:disabled="installingExternal || !externalBackend.uri"
class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:border-[var(--color-border-subtle)]"
class="btn-primary"
>
<i class="text-[10px]" :class="installingExternal ? 'fas fa-spinner fa-spin' : 'fas fa-download'"></i>
<i class="mr-2" :class="installingExternal ? 'fas fa-spinner fa-spin' : 'fas fa-download'"></i>
<span x-text="installingExternal ? 'Installing...' : 'Install Backend'"></span>
</button>
<span x-show="externalBackendProgress" class="text-sm text-[var(--color-text-secondary)]" x-text="externalBackendProgress"></span>

View File

@@ -19,13 +19,15 @@
{{if .ErrorCode}}{{.ErrorCode}}{{else}}Error{{end}}
</h1>
<p class="text-xl text-[var(--color-text-secondary)] mb-6">{{if .ErrorMessage}}{{.ErrorMessage}}{{else}}An unexpected error occurred{{end}}</p>
<div class="flex flex-wrap justify-center gap-2">
<a href="./" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-home"></i>
<div class="flex flex-wrap justify-center gap-4">
<a href="./"
class="btn-primary">
<i class="fas fa-home mr-2"></i>
<span>Return Home</span>
</a>
<a href="browse/" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-images"></i>
<a href="browse/"
class="btn-secondary">
<i class="fas fa-images mr-2"></i>
<span>Browse Gallery</span>
</a>
</div>

View File

@@ -195,8 +195,9 @@
</div>
<div class="flow-root">
<!-- Toggle button for showing/hiding the form -->
<button type="button" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors float-right mb-2" @click="toggleForm()">
<i :class="showForm ? 'fa-solid fa-times' : 'fa-solid fa-plus'"></i>
<button class="btn-primary float-right mb-2" @click="toggleForm()">
<!-- Conditional icon display -->
<i :class="showForm ? 'fa-solid fa-times' : 'fa-solid fa-plus'" class="mr-2"></i>
<span x-text="showForm ? 'Close' : 'Add New Network'"></span>
</button>
</div>
@@ -215,7 +216,7 @@
<label for="token">Token</label>
<textarea id="token" x-model="newNetwork.token" placeholder="Enter token" class="input"></textarea>
</div>
<button type="button" @click="addNetwork" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors"><i class="fa-solid fa-plus"></i> <span>Add Network</span></button>
<button @click="addNetwork" class="btn-primary"><i class="fa-solid fa-plus"></i> Add Network</button>
<template x-if="errorMessage">
<p class="error" x-text="errorMessage"></p>
</template>

View File

@@ -87,19 +87,19 @@
</div>
</div>
<div class="flex flex-wrap justify-center gap-2 mb-8">
<a href="/browse/" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-images"></i>
<span>Browse Model Gallery</span>
<div class="flex flex-wrap justify-center gap-4 mb-8">
<a href="/browse/" class="btn-primary">
<i class="fas fa-images mr-2"></i>
Browse Model Gallery
</a>
<a href="/import-model" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-upload"></i>
<span>Import Model</span>
<a href="/import-model" class="btn-primary">
<i class="fas fa-upload mr-2"></i>
Import Model
</a>
<a href="https://localai.io/basics/getting_started/" target="_blank" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-graduation-cap"></i>
<span>Getting Started</span>
<i class="fas fa-external-link-alt text-[10px]"></i>
<a href="https://localai.io/basics/getting_started/" target="_blank" class="btn-secondary">
<i class="fas fa-graduation-cap mr-2"></i>
Getting Started
<i class="fas fa-external-link-alt ml-2 text-sm"></i>
</a>
</div>
{{ else }}

View File

@@ -3,7 +3,7 @@
{{template "views/partials/head" .}}
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
<div class="app-layout no-sidebar">
<div class="app-layout">
<main class="main-content">
<div class="main-content-inner">
@@ -44,9 +44,11 @@
</div>
<div>
<button type="submit"
class="inline-flex items-center justify-center gap-1.5 w-full text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-sign-in-alt"></i>
<button
type="submit"
class="btn-primary w-full"
>
<i class="fas fa-sign-in-alt mr-2"></i>
<span>Login</span>
</button>
</div>

View File

@@ -42,6 +42,36 @@
Model & Backend Management
</h1>
<p class="hero-subtitle">Manage your installed models and backends</p>
<!-- Quick Actions -->
<div class="flex flex-wrap justify-center gap-3">
<a href="browse/" class="btn-primary text-sm py-1.5 px-3">
<i class="fas fa-images mr-1.5 text-[10px]"></i>
<span>Model Gallery</span>
</a>
<a href="/import-model" class="btn-primary text-sm py-1.5 px-3">
<i class="fas fa-plus mr-1.5 text-[10px]"></i>
<span>Import Model</span>
</a>
<button id="reload-models-btn" class="btn-primary text-sm py-1.5 px-3">
<i class="fas fa-sync-alt mr-1.5 text-[10px]"></i>
<span>Update Models</span>
</button>
<a href="/browse/backends" class="btn-secondary text-sm py-1.5 px-3">
<i class="fas fa-cogs mr-1.5 text-[10px]"></i>
<span>Backend Gallery</span>
</a>
{{ if not .DisableRuntimeSettings }}
<a href="/settings" class="btn-secondary text-sm py-1.5 px-3">
<i class="fas fa-cog mr-1.5 text-[10px]"></i>
<span>Settings</span>
</a>
{{ end }}
</div>
</div>
</div>
@@ -160,17 +190,17 @@
<p class="text-sm text-[var(--color-text-secondary)] mb-6">Get started by installing a model from the gallery or importing it</p>
<div class="flex flex-wrap justify-center gap-2 mb-6">
<a href="browse" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-images text-[10px]"></i>
<span>Browse Model Gallery</span>
<a href="browse" class="btn-primary text-sm py-1.5 px-3">
<i class="fas fa-images mr-1.5 text-[10px]"></i>
Browse Model Gallery
</a>
<a href="/import-model" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-upload text-[10px]"></i>
<span>Import Model</span>
<a href="/import-model" class="btn-primary text-sm py-1.5 px-3">
<i class="fas fa-upload mr-1.5 text-[10px]"></i>
Import Model
</a>
<a href="https://localai.io/basics/getting_started/" target="_blank" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-book text-[10px]"></i>
<span>Documentation</span>
<a href="https://localai.io/basics/getting_started/" target="_blank" class="btn-secondary text-sm py-1.5 px-3">
<i class="fas fa-book mr-1.5 text-[10px]"></i>
Documentation
</a>
</div>
@@ -198,22 +228,13 @@
{{ $modelsN := len .ModelsConfig}}
{{ $modelsN = add $modelsN (len .Models)}}
<div class="mb-6">
<div class="flex items-center justify-between gap-3 mb-1">
<div>
<h2 class="h3 flex items-center">
<i class="fas fa-brain mr-2 text-[var(--color-primary)] text-sm"></i>
Installed Models
</h2>
<p class="text-sm text-[var(--color-text-secondary)] mt-0.5">
<span class="text-[var(--color-primary)] font-medium">{{$modelsN}}</span> model{{if gt $modelsN 1}}s{{end}} ready to use
</p>
</div>
<button id="reload-models-btn" type="button" title="Update models list from disk"
class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-sync-alt text-[10px]"></i>
<span>Update</span>
</button>
</div>
<h2 class="h3 mb-1 flex items-center">
<i class="fas fa-brain mr-2 text-[var(--color-primary)] text-sm"></i>
Installed Models
</h2>
<p class="text-sm text-[var(--color-text-secondary)] mb-4">
<span class="text-[var(--color-primary)] font-medium">{{$modelsN}}</span> model{{if gt $modelsN 1}}s{{end}} ready to use
</p>
</div>
<div class="overflow-x-auto mb-8">
@@ -318,13 +339,13 @@
<td class="p-2">
<div class="flex items-center justify-end gap-1">
{{ if index $loadedModels .Name }}
<button type="button" class="text-red-400/60 hover:text-red-400 hover:bg-red-500/10 rounded p-1 transition-colors"
<button class="text-red-400/60 hover:text-red-400 hover:bg-red-500/10 rounded p-1 transition-colors"
onclick="handleStopModel('{{.Name}}')"
title="Stop {{.Name}}">
<i class="fas fa-stop text-xs"></i>
</button>
{{ end }}
<button type="button" class="text-red-400/60 hover:text-red-400 hover:bg-red-500/10 rounded p-1 transition-colors"
<button class="text-red-400/60 hover:text-red-400 hover:bg-red-500/10 rounded p-1 transition-colors"
onclick="handleDeleteModel('{{.Name}}')"
title="Delete {{.Name}}">
<i class="fas fa-trash-alt text-xs"></i>
@@ -376,12 +397,12 @@
Installed Backends
</h2>
{{ if gt (len .InstalledBackends) 0 }}
<button type="button"
<button
@click="reinstallAllBackends()"
:disabled="reinstallingAll"
class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:border-[var(--color-border-subtle)]"
class="btn-primary text-sm py-1.5 px-3"
title="Reinstall all backends">
<i class="fas fa-arrow-rotate-right text-[10px]" :class="reinstallingAll ? 'fa-spin' : ''"></i>
<i class="fas fa-arrow-rotate-right mr-1.5 text-[10px]" :class="reinstallingAll ? 'fa-spin' : ''"></i>
<span x-text="reinstallingAll ? 'Reinstalling...' : 'Reinstall All'"></span>
</button>
{{ end }}
@@ -401,14 +422,14 @@
<h2 class="h2 mb-2">No backends installed yet</h2>
<p class="text-sm text-[var(--color-text-secondary)] mb-6">Backends power your AI models. Install them from the backend gallery to get started</p>
<div class="flex flex-wrap justify-center gap-2">
<a href="/browse/backends" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-cogs text-[10px]"></i>
<span>Browse Backend Gallery</span>
<div class="flex flex-wrap justify-center gap-3">
<a href="/browse/backends" class="btn-primary">
<i class="fas fa-cogs mr-2 text-xs"></i>
Browse Backend Gallery
</a>
<a href="https://localai.io/backends/" target="_blank" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-book text-[10px]"></i>
<span>Documentation</span>
<a href="https://localai.io/backends/" target="_blank" class="btn-secondary">
<i class="fas fa-book mr-2 text-xs"></i>
Documentation
</a>
</div>
</div>
@@ -481,14 +502,14 @@
<td class="p-2">
<div class="flex items-center justify-end gap-1">
{{ if not .IsSystem }}
<button type="button"
<button
@click="reinstallBackend('{{.Name}}')"
:disabled="reinstallingBackends['{{.Name}}']"
class="text-[var(--color-primary)]/60 hover:text-[var(--color-primary)] hover:bg-[var(--color-primary)]/10 disabled:opacity-50 disabled:cursor-not-allowed rounded p-1 transition-colors"
title="Reinstall {{.Name}}">
<i class="fas fa-arrow-rotate-right text-xs" :class="reinstallingBackends['{{.Name}}'] ? 'fa-spin' : ''"></i>
</button>
<button type="button"
<button
@click="deleteBackend('{{.Name}}')"
class="text-red-400/60 hover:text-red-400 hover:bg-red-500/10 rounded p-1 transition-colors"
title="Delete {{.Name}}">

View File

@@ -24,30 +24,30 @@
<div class="flex gap-3">
<!-- Mode Toggle (only show when not in edit mode) -->
<template x-if="!isEditMode">
<button type="button" @click="toggleMode()" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas" :class="isAdvancedMode ? 'fa-magic' : 'fa-code'"></i>
<button @click="toggleMode()" class="btn-secondary">
<i class="fas" :class="isAdvancedMode ? 'fa-magic mr-2' : 'fa-code mr-2'"></i>
<span x-text="isAdvancedMode ? 'Simple Mode' : 'Advanced Mode'"></span>
</button>
</template>
<!-- Advanced Mode Buttons -->
<template x-if="isAdvancedMode">
<div class="flex gap-2">
<button type="button" id="validateBtn" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-check"></i>
<div class="flex gap-3">
<button id="validateBtn" class="btn-primary">
<i class="fas fa-check mr-2"></i>
<span>Validate</span>
</button>
<button type="button" id="saveBtn" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-save"></i>
<button id="saveBtn" class="btn-primary">
<i class="fas fa-save mr-2"></i>
<span>{{if .ModelName}}Update{{else}}Create{{end}}</span>
</button>
</div>
</template>
<!-- Simple Mode Button -->
<template x-if="!isAdvancedMode && !isEditMode">
<button type="button" @click="submitImport()"
<button @click="submitImport()"
:disabled="isSubmitting || !importUri.trim()"
class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:border-[var(--color-border-subtle)]">
<i class="fas text-[10px]" :class="isSubmitting ? 'fa-spinner fa-spin' : 'fa-upload'"></i>
class="btn-primary">
<i class="fas" :class="isSubmitting ? 'fa-spinner fa-spin mr-2' : 'fa-upload mr-2'"></i>
<span x-text="isSubmitting ? 'Importing...' : 'Import Model'"></span>
</button>
</template>
@@ -539,6 +539,8 @@
</div>
</div>
</div>
{{template "views/partials/footer" .}}
</div>
<!-- Include JS-YAML library -->

View File

@@ -61,14 +61,14 @@
<span class="font-semibold text-purple-300" x-text="repositories.length"></span>
<span class="text-[var(--color-text-secondary)] ml-1">repositories</span>
</div>
<a href="/import-model" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-upload"></i>
<a href="/import-model" class="btn-primary">
<i class="fas fa-upload mr-2"></i>
<span>Import Model</span>
</a>
<a href="https://localai.io/models/" target="_blank" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-info-circle"></i>
<a href="https://localai.io/models/" target="_blank" class="btn-secondary">
<i class="fas fa-info-circle mr-2"></i>
<span>Documentation</span>
<i class="fas fa-external-link-alt text-[10px]"></i>
<i class="fas fa-external-link-alt ml-2 text-xs"></i>
</a>
</div>
</div>
@@ -453,8 +453,8 @@
</div>
<!-- Modal Footer -->
<div class="flex items-center p-4 md:p-5 border-t border-[var(--color-border-subtle)] rounded-b">
<button type="button" @click="closeModal()"
class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<button @click="closeModal()"
class="btn-secondary">
Close
</button>
</div>

View File

@@ -85,15 +85,9 @@
<span class="nav-label">Swarm</span>
</a>
<a href="/manage" class="nav-item">
<i class="fas fa-server nav-icon"></i>
<span class="nav-label">System</span>
</a>
{{ if not .DisableRuntimeSettings }}
<a href="/settings" class="nav-item">
<i class="fas fa-cog nav-icon"></i>
<span class="nav-label">Settings</span>
</a>
{{ end }}
</div>
</nav>

View File

@@ -36,22 +36,22 @@
<div class="container mx-auto px-4 py-6 flex-grow max-w-4xl">
<!-- Header -->
<div class="mb-4">
<h1 class="h2 mb-2">Application Settings</h1>
<div class="mb-6">
<div class="flex items-center justify-between mb-2">
<h1 class="h2">
Application Settings
</h1>
<a href="/manage"
class="inline-flex items-center text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors">
<i class="fas fa-arrow-left mr-2 text-sm"></i>
<span class="text-sm">Back to Manage</span>
</a>
</div>
<p class="text-sm text-[var(--color-text-secondary)]">Configure watchdog and backend request settings</p>
</div>
<!-- Settings Form -->
<form @submit.prevent="saveSettings()" class="space-y-6">
<!-- Sticky Save bar -->
<div class="sticky top-0 z-10 -mx-4 px-4 py-3 -mt-2 mb-2 bg-[var(--color-bg-primary)] border-b border-[var(--color-border-subtle)] flex justify-end">
<button type="submit"
:disabled="saving"
class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:border-[var(--color-border-subtle)]">
<i class="fas fa-save text-[10px]" :class="saving ? 'fa-spin fa-spinner' : ''"></i>
<span x-text="saving ? 'Saving...' : 'Save Settings'"></span>
</button>
</div>
<!-- Watchdog Settings Section -->
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
@@ -591,6 +591,30 @@
</div>
</div>
</div>
<!-- Source Info -->
<div class="bg-yellow-500/10 border border-yellow-500/20 rounded-lg p-4" x-show="sourceInfo">
<div class="flex items-start">
<i class="fas fa-info-circle text-yellow-400 mr-2 mt-0.5"></i>
<div class="flex-1">
<p class="text-sm text-yellow-300 font-medium mb-1">Configuration Source</p>
<p class="text-xs text-yellow-200" x-text="'Settings are currently loaded from: ' + sourceInfo"></p>
<p class="text-xs text-yellow-200 mt-1" x-show="sourceInfo === 'env'">
Environment variables take precedence. To modify settings via the UI, unset the relevant environment variables first.
</p>
</div>
</div>
</div>
<!-- Save Button -->
<div class="flex justify-end">
<button type="submit"
:disabled="saving"
class="btn-primary">
<i class="fas fa-save mr-2" :class="saving ? 'fa-spin fa-spinner' : ''"></i>
<span x-text="saving ? 'Saving...' : 'Save Settings'"></span>
</button>
</div>
</form>
</div>
@@ -635,6 +659,7 @@ function settingsDashboard() {
agent_job_retention_days: 30,
open_responses_store_ttl: '0'
},
sourceInfo: '',
saving: false,
init() {
@@ -682,6 +707,7 @@ function settingsDashboard() {
agent_job_retention_days: data.agent_job_retention_days || 30,
open_responses_store_ttl: data.open_responses_store_ttl || '0'
};
this.sourceInfo = data.source || 'default';
} else {
this.addNotification('Failed to load settings: ' + (data.error || 'Unknown error'), 'error');
}
@@ -853,6 +879,7 @@ function settingsDashboard() {
if (response.ok && data.success) {
this.addNotification('Settings saved successfully!', 'success');
// Reload settings to get updated source info
setTimeout(() => this.loadSettings(), 1000);
} else {
this.addNotification('Failed to save settings: ' + (data.error || 'Unknown error'), 'error');

View File

@@ -149,7 +149,7 @@
</div>
<div class="pt-4">
<button type="submit" id="generate-btn" class="inline-flex items-center justify-center gap-1.5 w-full text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<button type="submit" id="generate-btn" class="btn-primary w-full py-3 flex items-center justify-center gap-2">
<i class="fas fa-music"></i>
<span>Generate sound</span>
</button>

View File

@@ -43,13 +43,13 @@
API Traces
</h1>
<p class="hero-subtitle">View logged API requests and responses</p>
<div class="flex flex-wrap justify-center gap-2">
<button type="button" @click="clearTraces()" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-trash text-[10px]"></i>
<div class="flex flex-wrap justify-center gap-3">
<button @click="clearTraces()" class="btn-secondary text-sm py-1.5 px-3">
<i class="fas fa-trash mr-1.5 text-[10px]"></i>
<span>Clear Traces</span>
</button>
<a href="/api/traces" download="traces.json" class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors">
<i class="fas fa-download text-[10px]"></i>
<a href="/api/traces" download="traces.json" class="btn-secondary text-sm py-1.5 px-3">
<i class="fas fa-download mr-1.5 text-[10px]"></i>
<span>Export Traces</span>
</a>
</div>
@@ -93,10 +93,10 @@
<!-- Save Button -->
<div class="flex justify-end pt-2">
<button type="button" @click="saveTracingSettings()"
<button @click="saveTracingSettings()"
:disabled="saving"
class="inline-flex items-center gap-1.5 text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] bg-transparent hover:bg-[var(--color-primary)]/10 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/30 rounded-md py-1.5 px-2.5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:border-[var(--color-border-subtle)]">
<i class="fas fa-save text-[10px]" :class="saving ? 'fa-spin fa-spinner' : ''"></i>
class="btn-primary px-4 py-2 text-sm">
<i class="fas fa-save mr-2" :class="saving ? 'fa-spin fa-spinner' : ''"></i>
<span x-text="saving ? 'Saving...' : 'Save Settings'"></span>
</button>
</div>

11
go.mod
View File

@@ -1,8 +1,6 @@
module github.com/mudler/LocalAI
go 1.24.4
toolchain go1.24.5
go 1.25.0
require (
dario.cat/mergo v1.0.2
@@ -52,7 +50,7 @@ require (
github.com/shirou/gopsutil/v3 v3.24.5
github.com/streamer45/silero-vad-go v0.2.1
github.com/stretchr/testify v1.11.1
github.com/swaggo/echo-swagger v1.4.1
github.com/swaggo/echo-swagger v1.5.0
github.com/swaggo/swag v1.16.6
github.com/testcontainers/testcontainers-go v0.40.0
github.com/tmc/langchaingo v0.1.14
@@ -66,9 +64,11 @@ require (
)
require (
github.com/ghodss/yaml v1.0.0 // indirect
github.com/labstack/echo/v5 v5.0.0 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/sv-tools/openapi v0.2.1 // indirect
github.com/swaggo/files/v2 v2.0.2 // indirect
github.com/swaggo/swag/v2 v2.0.0-rc4 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.2.0 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
@@ -76,6 +76,7 @@ require (
github.com/valyala/fasttemplate v1.2.2 // indirect
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
require (

13
go.sum
View File

@@ -176,7 +176,6 @@ github.com/fyne-io/image v0.1.1 h1:WH0z4H7qfvNUw5l4p3bC1q70sa5+YWVt6HCj7y4VNyA=
github.com/fyne-io/image v0.1.1/go.mod h1:xrfYBh6yspc+KjkgdZU/ifUC9sPA5Iv7WYUBzQKK7JM=
github.com/fyne-io/oksvg v0.2.0 h1:mxcGU2dx6nwjJsSA9PCYZDuoAcsZ/OuJlvg/Q9Njfo8=
github.com/fyne-io/oksvg v0.2.0/go.mod h1:dJ9oEkPiWhnTFNCmRgEze+YNprJF7YRbpjgpWS4kzoI=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
@@ -392,6 +391,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo/v4 v4.15.0 h1:hoRTKWcnR5STXZFe9BmYun9AMTNeSbjHi2vtDuADJ24=
github.com/labstack/echo/v4 v4.15.0/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c=
github.com/labstack/echo/v5 v5.0.0 h1:JHKGrI0cbNsNMyKvranuY0C94O4hSM7yc/HtwcV3Na4=
github.com/labstack/echo/v5 v5.0.0/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
@@ -509,8 +510,6 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mudler/cogito v0.8.2-0.20260214201734-da0d4ceb2b44 h1:joGszpItINnZdoL/0p2077Wz2xnxMGRSRgYN5mS7I4c=
github.com/mudler/cogito v0.8.2-0.20260214201734-da0d4ceb2b44/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4=
github.com/mudler/cogito v0.9.1-0.20260217143801-bb7f986ed2c7 h1:z3AcM7LbaQb+C955JdSXksHB9B0uWGQpdgl05gJM+9Y=
github.com/mudler/cogito v0.9.1-0.20260217143801-bb7f986ed2c7/go.mod h1:6sfja3lcu2nWRzEc0wwqGNu/eCG3EWgij+8s7xyUeQ4=
github.com/mudler/edgevpn v0.31.1 h1:7qegiDWd0kAg6ljhNHxqvp8hbo/6BbzSdbb7/2WZfiY=
@@ -761,12 +760,16 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/swaggo/echo-swagger v1.4.1 h1:Yf0uPaJWp1uRtDloZALyLnvdBeoEL5Kc7DtnjzO/TUk=
github.com/swaggo/echo-swagger v1.4.1/go.mod h1:C8bSi+9yH2FLZsnhqMZLIZddpUxZdBYuNHbtaS1Hljc=
github.com/sv-tools/openapi v0.2.1 h1:ES1tMQMJFGibWndMagvdoo34T1Vllxr1Nlm5wz6b1aA=
github.com/sv-tools/openapi v0.2.1/go.mod h1:k5VuZamTw1HuiS9p2Wl5YIDWzYnHG6/FgPOSFXLAhGg=
github.com/swaggo/echo-swagger v1.5.0 h1:nkHxOaBy0SkbJMtMeXZC64KHSa0mJdZFQhVqwEcMres=
github.com/swaggo/echo-swagger v1.5.0/go.mod h1:TzO363X1ZG/MSbjrG2IX6m65Yd3/zpqh5KM6lPctAhk=
github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU=
github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0=
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
github.com/swaggo/swag/v2 v2.0.0-rc4 h1:SZ8cK68gcV6cslwrJMIOqPkJELRwq4gmjvk77MrvHvY=
github.com/swaggo/swag/v2 v2.0.0-rc4/go.mod h1:Ow7Y8gF16BTCDn8YxZbyKn8FkMLRUHekv1kROJZpbvE=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/testcontainers/testcontainers-go v0.40.0 h1:pSdJYLOVgLE8YdUY2FHQ1Fxu+aMnb6JfVz1mxk7OeMU=
github.com/testcontainers/testcontainers-go v0.40.0/go.mod h1:FSXV5KQtX2HAMlm7U3APNyLkkap35zNLxukw9oBi/MY=

View File

@@ -17,15 +17,11 @@ import (
var forceBackendShutdown bool = os.Getenv("LOCALAI_FORCE_BACKEND_SHUTDOWN") == "true"
var (
modelNotFoundErr = errors.New("model not found")
)
func (ml *ModelLoader) deleteProcess(s string) error {
model, ok := ml.models[s]
if !ok {
xlog.Debug("Model not found", "model", s)
return modelNotFoundErr
return fmt.Errorf("model %s not found", s)
}
retries := 1

View File

@@ -540,7 +540,7 @@ func (wd *WatchDog) evictLRUModel() {
wd.Unlock()
// Shutdown the model
if err := wd.pm.ShutdownModel(lruModel.model); err != nil && err != modelNotFoundErr {
if err := wd.pm.ShutdownModel(lruModel.model); err != nil {
xlog.Error("[WatchDog] error shutting down model during memory reclamation", "error", err, "model", lruModel.model)
} else {
// Untrack the model