mirror of
https://github.com/mudler/LocalAI.git
synced 2026-02-23 10:16:10 -05:00
Compare commits
1 Commits
v3.12.0
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0fe0e0c27 |
@@ -1,5 +1,5 @@
|
||||
|
||||
LLAMA_VERSION?=11c325c6e0666a30590cde390d5746a405e536b9
|
||||
LLAMA_VERSION?=2b089c77580d347767f440205103e4da8ec33d89
|
||||
LLAMA_REPO?=https://github.com/ggerganov/llama.cpp
|
||||
|
||||
CMAKE_ARGS?=
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,6 +5,7 @@ opencv-python
|
||||
transformers
|
||||
accelerate
|
||||
compel
|
||||
git+https://github.com/xhinker/sd_embed
|
||||
peft
|
||||
sentencepiece
|
||||
optimum-quanto
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}}">
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
11
go.mod
@@ -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
13
go.sum
@@ -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=
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user