Spaces:
Running
Running
Update standalone Tater bundle to 0.1.12
Browse files- pyproject.toml +4 -1
- tater_reachy_standalone/__init__.py +1 -1
- tater_reachy_standalone/vendor/tater_app/README.md +23 -19
- tater_reachy_standalone/vendor/tater_app/helpers.py +0 -0
- tater_reachy_standalone/vendor/tater_app/requirements.txt +3 -0
- tater_reachy_standalone/vendor/tater_app/setup_tater.sh +30 -0
- tater_reachy_standalone/vendor/tater_app/tateros_app.py +941 -27
- tater_reachy_standalone/vendor/tater_app/tateros_static/app.js +861 -19
- tater_reachy_standalone/vendor/tater_app/tateros_static/styles.css +325 -1
- tater_voice_sat/__init__.py +1 -2
- tater_voice_sat/sdk_app.py +25 -2
pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
| 4 |
|
| 5 |
[project]
|
| 6 |
name = "tater_reachy_standalone"
|
| 7 |
-
version = "0.1.
|
| 8 |
description = "Full local Tater AI stack and voice satellite for Reachy Mini"
|
| 9 |
readme = "README.md"
|
| 10 |
requires-python = ">=3.11"
|
|
@@ -61,6 +61,9 @@ dependencies = [
|
|
| 61 |
"accelerate",
|
| 62 |
"llama-cpp-python>=0.3.23",
|
| 63 |
"mlx-lm; platform_system == 'Darwin' and platform_machine == 'arm64'",
|
|
|
|
|
|
|
|
|
|
| 64 |
"faster-whisper",
|
| 65 |
"vosk",
|
| 66 |
"pykokoro",
|
|
|
|
| 4 |
|
| 5 |
[project]
|
| 6 |
name = "tater_reachy_standalone"
|
| 7 |
+
version = "0.1.12"
|
| 8 |
description = "Full local Tater AI stack and voice satellite for Reachy Mini"
|
| 9 |
readme = "README.md"
|
| 10 |
requires-python = ">=3.11"
|
|
|
|
| 61 |
"accelerate",
|
| 62 |
"llama-cpp-python>=0.3.23",
|
| 63 |
"mlx-lm; platform_system == 'Darwin' and platform_machine == 'arm64'",
|
| 64 |
+
"mlx-vlm; platform_system == 'Darwin' and platform_machine == 'arm64'",
|
| 65 |
+
"outlines; platform_system == 'Darwin' and platform_machine == 'arm64'",
|
| 66 |
+
"outlines-core==0.1.26; platform_system == 'Darwin' and platform_machine == 'arm64'",
|
| 67 |
"faster-whisper",
|
| 68 |
"vosk",
|
| 69 |
"pykokoro",
|
tater_reachy_standalone/__init__.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
"""Standalone Tater app for Reachy Mini."""
|
| 2 |
|
| 3 |
-
__version__ = "0.1.
|
|
|
|
| 1 |
"""Standalone Tater app for Reachy Mini."""
|
| 2 |
|
| 3 |
+
__version__ = "0.1.12"
|
tater_reachy_standalone/vendor/tater_app/README.md
CHANGED
|
@@ -320,22 +320,26 @@ After Tater is running, open TaterOS and finish the first-run setup:
|
|
| 320 |
- enable `Beast Mode` and set per-head model settings for Chat/Astraeus/Thanatos/Minos/Hermes
|
| 321 |
|
| 322 |
Hydra model settings are saved by TaterOS and used at runtime. Base, Spudex, Beast Mode routing, and Vision can each use the selected built-in local providers or OpenAI-compatible providers.
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 320 |
- enable `Beast Mode` and set per-head model settings for Chat/Astraeus/Thanatos/Minos/Hermes
|
| 321 |
|
| 322 |
Hydra model settings are saved by TaterOS and used at runtime. Base, Spudex, Beast Mode routing, and Vision can each use the selected built-in local providers or OpenAI-compatible providers.
|
| 323 |
+
|
| 324 |
+
### Local Models
|
| 325 |
+
|
| 326 |
+
- Download local Hugging Face Transformers, llama.cpp GGUF, or MLX models from the Hugging Face mini-tab first, then select them from Settings.
|
| 327 |
+
- Model caches live under `agent_lab/models/llm/` by default:
|
| 328 |
+
- `huggingface` for Transformers
|
| 329 |
+
- `llama-cpp` for GGUF models and matching `mmproj*.gguf` vision projectors
|
| 330 |
+
- `mlx` for MLX text and vision models
|
| 331 |
+
- The Hugging Face browser uses the token saved in **Integration Manager -> Hugging Face** for private/gated models and better Hub rate limits.
|
| 332 |
+
- llama.cpp uses GPU offload by default when the installed build supports it. Set `TATER_LLAMA_CPP_N_GPU_LAYERS=0` for CPU-only.
|
| 333 |
+
- MLX is intended for Apple Silicon Macs. Use llama.cpp GGUF on Linux, Raspberry Pi, NVIDIA, AMD/ROCm, Jetson, or other non-Apple-Silicon devices.
|
| 334 |
+
|
| 335 |
+
### Vision
|
| 336 |
+
|
| 337 |
+
- Vision can use an OpenAI-compatible API, the loaded Base model, or a dedicated local vision model.
|
| 338 |
+
- If Base is already loaded and vision-capable, Tater reuses it instead of loading the same model twice.
|
| 339 |
+
- Dedicated vision models are managed separately from Base.
|
| 340 |
+
|
| 341 |
+
### Advanced Notes
|
| 342 |
+
|
| 343 |
+
- Local context length is configured in **Settings -> Models -> LLM / Vision**.
|
| 344 |
+
- Thinking suppression is enabled by default for local providers when supported.
|
| 345 |
+
- `run_ui.sh` starts Uvicorn with `--no-access-log` to suppress per-request log spam.
|
tater_reachy_standalone/vendor/tater_app/helpers.py
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tater_reachy_standalone/vendor/tater_app/requirements.txt
CHANGED
|
@@ -50,6 +50,9 @@ transformers
|
|
| 50 |
accelerate
|
| 51 |
llama-cpp-python>=0.3.23
|
| 52 |
mlx-lm; platform_system == "Darwin" and platform_machine == "arm64"
|
|
|
|
|
|
|
|
|
|
| 53 |
faster-whisper
|
| 54 |
vosk
|
| 55 |
pykokoro
|
|
|
|
| 50 |
accelerate
|
| 51 |
llama-cpp-python>=0.3.23
|
| 52 |
mlx-lm; platform_system == "Darwin" and platform_machine == "arm64"
|
| 53 |
+
mlx-vlm; platform_system == "Darwin" and platform_machine == "arm64"
|
| 54 |
+
outlines; platform_system == "Darwin" and platform_machine == "arm64"
|
| 55 |
+
outlines-core==0.1.26; platform_system == "Darwin" and platform_machine == "arm64"
|
| 56 |
faster-whisper
|
| 57 |
vosk
|
| 58 |
pykokoro
|
tater_reachy_standalone/vendor/tater_app/setup_tater.sh
CHANGED
|
@@ -272,6 +272,33 @@ install_llama_cpp_metal() {
|
|
| 272 |
"${venv_python}" -m pip install --upgrade "${LLAMA_CPP_PYTHON_SPEC}" || warn "Default llama-cpp-python fallback failed."
|
| 273 |
}
|
| 274 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
install_macos() {
|
| 276 |
venv_python="$1"
|
| 277 |
tmp_req="$(mktemp "${TMPDIR:-/tmp}/tater-requirements-macos.XXXXXX")"
|
|
@@ -305,6 +332,9 @@ install_macos() {
|
|
| 305 |
if ! "${venv_python}" -m pip install mlx-whisper kokoro; then
|
| 306 |
warn "Apple-native speech extras failed to install. Tater will still run with Faster Whisper/Kokoro CPU fallbacks."
|
| 307 |
fi
|
|
|
|
|
|
|
|
|
|
| 308 |
rm -f "${tmp_req}"
|
| 309 |
trap - EXIT
|
| 310 |
}
|
|
|
|
| 272 |
"${venv_python}" -m pip install --upgrade "${LLAMA_CPP_PYTHON_SPEC}" || warn "Default llama-cpp-python fallback failed."
|
| 273 |
}
|
| 274 |
|
| 275 |
+
install_mlx_engine_checkout() {
|
| 276 |
+
if [ "${TATER_SETUP_MLX_ENGINE:-1}" = "0" ]; then
|
| 277 |
+
warn "Skipping MLX engine checkout because TATER_SETUP_MLX_ENGINE=0."
|
| 278 |
+
return
|
| 279 |
+
fi
|
| 280 |
+
if ! command -v git >/dev/null 2>&1; then
|
| 281 |
+
warn "git was not found; skipping optional MLX engine checkout."
|
| 282 |
+
return
|
| 283 |
+
fi
|
| 284 |
+
mkdir -p "${RUNTIME_DIR}"
|
| 285 |
+
engine_dir="${RUNTIME_DIR}/mlx-engine"
|
| 286 |
+
if [ -d "${engine_dir}/mlx_engine" ]; then
|
| 287 |
+
ok "Using existing ${engine_dir}"
|
| 288 |
+
if [ -d "${engine_dir}/.git" ]; then
|
| 289 |
+
info "Updating optional MLX engine checkout"
|
| 290 |
+
git -C "${engine_dir}" pull --ff-only || warn "Could not update ${engine_dir}; using the existing checkout."
|
| 291 |
+
fi
|
| 292 |
+
return
|
| 293 |
+
fi
|
| 294 |
+
if [ -e "${engine_dir}" ]; then
|
| 295 |
+
warn "${engine_dir} exists but does not look like an mlx-engine checkout. Set TATER_MLX_ENGINE_PATH or remove it and rerun setup."
|
| 296 |
+
return
|
| 297 |
+
fi
|
| 298 |
+
info "Cloning optional MLX engine runtime"
|
| 299 |
+
git clone --depth 1 https://github.com/lmstudio-ai/mlx-engine.git "${engine_dir}" || warn "Could not clone mlx-engine; Tater will use mlx-lm/mlx-vlm directly."
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
install_macos() {
|
| 303 |
venv_python="$1"
|
| 304 |
tmp_req="$(mktemp "${TMPDIR:-/tmp}/tater-requirements-macos.XXXXXX")"
|
|
|
|
| 332 |
if ! "${venv_python}" -m pip install mlx-whisper kokoro; then
|
| 333 |
warn "Apple-native speech extras failed to install. Tater will still run with Faster Whisper/Kokoro CPU fallbacks."
|
| 334 |
fi
|
| 335 |
+
if [ "${is_apple_silicon}" = "1" ]; then
|
| 336 |
+
install_mlx_engine_checkout
|
| 337 |
+
fi
|
| 338 |
rm -f "${tmp_req}"
|
| 339 |
trap - EXIT
|
| 340 |
}
|
tater_reachy_standalone/vendor/tater_app/tateros_app.py
CHANGED
|
@@ -6,6 +6,7 @@ import hmac
|
|
| 6 |
import importlib
|
| 7 |
import json
|
| 8 |
import logging
|
|
|
|
| 9 |
import os
|
| 10 |
import queue
|
| 11 |
import re
|
|
@@ -63,12 +64,28 @@ from hydra import (
|
|
| 63 |
from emoji_responder import get_emoji_settings as get_core_emoji_settings, save_emoji_settings as save_core_emoji_settings
|
| 64 |
from notify import notifier_destination_catalog
|
| 65 |
from helpers import (
|
|
|
|
| 66 |
DEFAULT_HF_TRANSFORMERS_CONTEXT_TOKENS,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
DEFAULT_LLAMA_CPP_CONTEXT_TOKENS,
|
| 68 |
DEFAULT_LLAMA_CPP_MTP_DRAFT_TOKENS,
|
| 69 |
DEFAULT_LLAMA_CPP_MTP_ENABLED,
|
|
|
|
|
|
|
|
|
|
| 70 |
DEFAULT_LLAMA_CPP_VISION_CONTEXT_TOKENS,
|
|
|
|
|
|
|
|
|
|
| 71 |
HYDRA_HF_TRANSFORMERS_CONTEXT_TOKENS_KEY,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
HYDRA_LLM_BASE_SERVERS_KEY,
|
| 73 |
HYDRA_LLM_PROVIDER_HF_TRANSFORMERS,
|
| 74 |
HYDRA_LLM_PROVIDER_KEY,
|
|
@@ -76,10 +93,20 @@ from helpers import (
|
|
| 76 |
HYDRA_LLM_PROVIDER_MLX_LM,
|
| 77 |
HYDRA_LLM_PROVIDER_OPENAI_COMPATIBLE,
|
| 78 |
HYDRA_LLAMA_CPP_CONTEXT_TOKENS_KEY,
|
|
|
|
| 79 |
HYDRA_LLAMA_CPP_MTP_DRAFT_TOKENS_KEY,
|
| 80 |
HYDRA_LLAMA_CPP_MTP_ENABLED_KEY,
|
|
|
|
|
|
|
|
|
|
| 81 |
HYDRA_LLAMA_CPP_VISION_CONTEXT_TOKENS_KEY,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
HYDRA_MLX_LM_CONTEXT_TOKENS_KEY,
|
|
|
|
|
|
|
| 83 |
HfLlmDownloadCancelled,
|
| 84 |
decrypt_current_redis_snapshot,
|
| 85 |
download_hf_transformers_llm_model,
|
|
@@ -88,11 +115,14 @@ from helpers import (
|
|
| 88 |
encrypt_current_redis_snapshot,
|
| 89 |
ensure_redis_encryption_key,
|
| 90 |
get_llama_cpp_runtime_diagnostics,
|
|
|
|
|
|
|
| 91 |
get_local_llm_loaded_models_snapshot,
|
| 92 |
get_redis_connection_config,
|
| 93 |
get_redis_encryption_status,
|
| 94 |
get_redis_connection_status,
|
| 95 |
get_llm_call_runtime_summary,
|
|
|
|
| 96 |
get_vision_call_runtime_summary,
|
| 97 |
get_llm_client_from_env,
|
| 98 |
preload_hf_transformers_llm_model,
|
|
@@ -108,6 +138,10 @@ from helpers import (
|
|
| 108 |
set_main_loop,
|
| 109 |
shutdown_internal_redis,
|
| 110 |
test_redis_connection_settings,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
unload_local_llm_models,
|
| 112 |
)
|
| 113 |
from runtime_executors import configure_runtime_executors, run_dashboard, run_wake, shutdown_runtime_executors
|
|
@@ -533,6 +567,63 @@ def _read_bool_setting(redis_key: str, env_keys: Tuple[str, ...], default: bool)
|
|
| 533 |
return _as_bool_flag(raw, default=default)
|
| 534 |
|
| 535 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 536 |
def _read_bounded_int_setting(
|
| 537 |
redis_key: str,
|
| 538 |
env_keys: Tuple[str, ...],
|
|
@@ -1604,15 +1695,17 @@ def _hf_browser_models_api_url(
|
|
| 1604 |
task: str = "text-generation",
|
| 1605 |
) -> str:
|
| 1606 |
library = _hf_browser_provider_library(provider)
|
| 1607 |
-
app_filter = _hf_browser_provider_app_filter(provider)
|
|
|
|
| 1608 |
params: Dict[str, Any] = {
|
| 1609 |
"sort": sort,
|
| 1610 |
"direction": "-1",
|
| 1611 |
"limit": str(max(1, min(100, int(limit or 24)))),
|
| 1612 |
"full": "true",
|
| 1613 |
"cardData": "true",
|
| 1614 |
-
"pipeline_tag": _normalize_hf_browser_task(task),
|
| 1615 |
}
|
|
|
|
|
|
|
| 1616 |
if library:
|
| 1617 |
params["library"] = library
|
| 1618 |
if app_filter:
|
|
@@ -1802,11 +1895,299 @@ def _hf_browser_provider_search(provider: str, query: str, task: str = "text-gen
|
|
| 1802 |
return q
|
| 1803 |
|
| 1804 |
|
| 1805 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1806 |
provider_token = _normalize_hydra_llm_provider(provider)
|
|
|
|
| 1807 |
if provider_token == HYDRA_LLM_PROVIDER_LLAMA_CPP:
|
| 1808 |
return "llama.cpp"
|
| 1809 |
if provider_token == HYDRA_LLM_PROVIDER_MLX_LM:
|
|
|
|
|
|
|
| 1810 |
return "mlx-lm"
|
| 1811 |
return ""
|
| 1812 |
|
|
@@ -1818,8 +2199,16 @@ def _hf_browser_provider_library(provider: str) -> str:
|
|
| 1818 |
return "transformers"
|
| 1819 |
|
| 1820 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1821 |
def _hf_browser_text_generation_tokens() -> Tuple[str, ...]:
|
| 1822 |
-
return ("text-generation",)
|
| 1823 |
|
| 1824 |
|
| 1825 |
def _hf_browser_vision_tokens() -> Tuple[str, ...]:
|
|
@@ -1912,6 +2301,8 @@ def _hf_browser_provider_matches(model: Any, provider: str) -> bool:
|
|
| 1912 |
return (
|
| 1913 |
"mlx-lm" in provider_haystack
|
| 1914 |
or "mlx_lm" in provider_haystack
|
|
|
|
|
|
|
| 1915 |
or library == "mlx"
|
| 1916 |
or "mlx" in tags
|
| 1917 |
or "mlx" in files
|
|
@@ -1922,9 +2313,26 @@ def _hf_browser_provider_matches(model: Any, provider: str) -> bool:
|
|
| 1922 |
|
| 1923 |
def _hf_browser_file_quant(path: str) -> str:
|
| 1924 |
name = os.path.basename(str(path or "")).upper()
|
| 1925 |
-
for quant in (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1926 |
if quant in name:
|
| 1927 |
-
return quant
|
| 1928 |
return ""
|
| 1929 |
|
| 1930 |
|
|
@@ -1932,7 +2340,7 @@ def _hf_browser_preferred_gguf(files: List[Dict[str, Any]]) -> str:
|
|
| 1932 |
ggufs = [row for row in files if bool(row.get("is_gguf")) and "mmproj" not in str(row.get("path") or "").lower()]
|
| 1933 |
if not ggufs:
|
| 1934 |
return ""
|
| 1935 |
-
preferred = ("Q4_K_M", "Q5_K_M", "Q4_K_S", "Q5_0", "Q4_0", "Q8_0")
|
| 1936 |
|
| 1937 |
def _score(row: Dict[str, Any]) -> Tuple[int, int, str]:
|
| 1938 |
path = str(row.get("path") or "")
|
|
@@ -1985,6 +2393,8 @@ def _hf_browser_model_summary(model: Any, *, provider: str) -> Dict[str, Any]:
|
|
| 1985 |
lower_files = " ".join(files).lower()
|
| 1986 |
lower_library = str(_hf_browser_object_value(model, "library_name") or "").strip().lower()
|
| 1987 |
provider_haystack = f"{lower_id} {lower_library} {lower_tags} {lower_files}"
|
|
|
|
|
|
|
| 1988 |
supports_vision = _hf_browser_is_vision_model(model) or "mmproj" in lower_files
|
| 1989 |
if provider_token == HYDRA_LLM_PROVIDER_LLAMA_CPP:
|
| 1990 |
compatible = (
|
|
@@ -1996,12 +2406,17 @@ def _hf_browser_model_summary(model: Any, *, provider: str) -> Dict[str, Any]:
|
|
| 1996 |
or ".gguf" in lower_files
|
| 1997 |
)
|
| 1998 |
elif provider_token == HYDRA_LLM_PROVIDER_MLX_LM:
|
| 1999 |
-
compatible =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2000 |
else:
|
| 2001 |
compatible = ".gguf" not in lower_files
|
| 2002 |
-
card_data = _hf_browser_object_value(model, "cardData", None) or _hf_browser_object_value(model, "card_data", None)
|
| 2003 |
model_size_label = _hf_browser_model_size_label(model_id, tags, files, model, card_data)
|
| 2004 |
-
|
| 2005 |
"id": model_id,
|
| 2006 |
"author": str(_hf_browser_object_value(model, "author") or "").strip(),
|
| 2007 |
"model_size": model_size_label,
|
|
@@ -2022,6 +2437,17 @@ def _hf_browser_model_summary(model: Any, *, provider: str) -> Dict[str, Any]:
|
|
| 2022 |
"provider": provider_token,
|
| 2023 |
"provider_label": _hydra_llm_provider_label(provider_token),
|
| 2024 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2025 |
|
| 2026 |
|
| 2027 |
def _hf_browser_file_rows(info: Any, fallback_files: List[str]) -> List[Dict[str, Any]]:
|
|
@@ -2393,7 +2819,10 @@ def _local_llm_provider_cache_rows(provider: str) -> List[Dict[str, Any]]:
|
|
| 2393 |
"downloaded_ts": float(latest_snapshot.stat().st_mtime if latest_snapshot.exists() else time.time()),
|
| 2394 |
"max_context_tokens": max_context,
|
| 2395 |
"context_source": context_source,
|
| 2396 |
-
"supports_vision": bool(
|
|
|
|
|
|
|
|
|
|
| 2397 |
}
|
| 2398 |
)
|
| 2399 |
return rows
|
|
@@ -2435,7 +2864,9 @@ def _normalize_local_llm_model_row(row: Dict[str, Any]) -> Dict[str, Any]:
|
|
| 2435 |
supports_vision = bool(row.get("supports_vision"))
|
| 2436 |
mmproj_filename = str(row.get("mmproj_filename") or "").strip()
|
| 2437 |
mmproj_path = str(row.get("mmproj_path") or "").strip()
|
| 2438 |
-
if provider ==
|
|
|
|
|
|
|
| 2439 |
path_obj = Path(str(row.get("model_path") or "")).expanduser()
|
| 2440 |
if path_obj.exists():
|
| 2441 |
detected_mmproj = _local_llm_mmproj_for_gguf(path_obj)
|
|
@@ -2500,6 +2931,36 @@ def _local_llm_models_payload(provider: str = "") -> Dict[str, Any]:
|
|
| 2500 |
return {"models": models, "by_provider": by_provider, "updated_ts": time.time()}
|
| 2501 |
|
| 2502 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2503 |
def _record_downloaded_local_llm_model(
|
| 2504 |
*,
|
| 2505 |
provider: str,
|
|
@@ -3455,16 +3916,52 @@ def _enforce_user_assistant_alternation(loop_messages: List[Dict[str, Any]]) ->
|
|
| 3455 |
return merged
|
| 3456 |
|
| 3457 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3458 |
def _load_chat_history_tail(count: int) -> List[Dict[str, Any]]:
|
| 3459 |
if count <= 0:
|
| 3460 |
return []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3461 |
raw = redis_client.lrange(CHAT_HISTORY_KEY, -count, -1)
|
| 3462 |
out: List[Dict[str, Any]] = []
|
| 3463 |
-
for line in raw:
|
| 3464 |
try:
|
| 3465 |
parsed = json.loads(line)
|
| 3466 |
if isinstance(parsed, dict):
|
| 3467 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3468 |
except Exception:
|
| 3469 |
continue
|
| 3470 |
return out
|
|
@@ -3473,11 +3970,17 @@ def _load_chat_history_tail(count: int) -> List[Dict[str, Any]]:
|
|
| 3473 |
def _load_chat_history() -> List[Dict[str, Any]]:
|
| 3474 |
raw = redis_client.lrange(CHAT_HISTORY_KEY, 0, -1)
|
| 3475 |
out: List[Dict[str, Any]] = []
|
| 3476 |
-
for line in raw:
|
| 3477 |
try:
|
| 3478 |
parsed = json.loads(line)
|
| 3479 |
if isinstance(parsed, dict):
|
| 3480 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3481 |
except Exception:
|
| 3482 |
continue
|
| 3483 |
return out
|
|
@@ -4287,8 +4790,20 @@ def _normalize_plugin_response_item(item: Any) -> Any:
|
|
| 4287 |
if not isinstance(item, dict):
|
| 4288 |
return item
|
| 4289 |
|
| 4290 |
-
|
| 4291 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4292 |
return item
|
| 4293 |
|
| 4294 |
raw = None
|
|
@@ -4296,15 +4811,62 @@ def _normalize_plugin_response_item(item: Any) -> Any:
|
|
| 4296 |
raw = bytes(item.get("data"))
|
| 4297 |
elif isinstance(item.get("bytes"), (bytes, bytearray)):
|
| 4298 |
raw = bytes(item.get("bytes"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4299 |
|
| 4300 |
if raw is None:
|
| 4301 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4302 |
|
| 4303 |
safe = dict(item)
|
| 4304 |
safe.pop("data", None)
|
| 4305 |
safe.pop("bytes", None)
|
| 4306 |
-
safe
|
| 4307 |
-
safe["
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4308 |
return safe
|
| 4309 |
|
| 4310 |
|
|
@@ -5844,6 +6406,13 @@ class LocalLlmModelDeleteRequest(BaseModel):
|
|
| 5844 |
model: Optional[str] = None
|
| 5845 |
|
| 5846 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5847 |
class HfLlmWarmupCancelRequest(BaseModel):
|
| 5848 |
key: Optional[str] = None
|
| 5849 |
provider: Optional[str] = None
|
|
@@ -5928,11 +6497,26 @@ class AppSettingsRequest(BaseModel):
|
|
| 5928 |
hydra_llm_api_key: Optional[str] = None
|
| 5929 |
hydra_llm_provider: Optional[str] = None
|
| 5930 |
hydra_hf_transformers_context_tokens: Optional[Any] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5931 |
hydra_llama_cpp_context_tokens: Optional[Any] = None
|
| 5932 |
hydra_llama_cpp_vision_context_tokens: Optional[Any] = None
|
| 5933 |
hydra_llama_cpp_mtp_enabled: Optional[bool] = None
|
| 5934 |
hydra_llama_cpp_mtp_draft_tokens: Optional[Any] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5935 |
hydra_mlx_lm_context_tokens: Optional[Any] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5936 |
spudex_llm_provider: Optional[str] = None
|
| 5937 |
spudex_llm_host: Optional[str] = None
|
| 5938 |
spudex_llm_model: Optional[str] = None
|
|
@@ -9902,6 +10486,11 @@ def runtime_breakdown() -> Dict[str, Any]:
|
|
| 9902 |
return {"ok": True, **payload}
|
| 9903 |
|
| 9904 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9905 |
@app.post("/api/runtime/local-llm/unload")
|
| 9906 |
def unload_runtime_local_llm(payload: LocalLlmUnloadRequest) -> Dict[str, Any]:
|
| 9907 |
try:
|
|
@@ -11938,11 +12527,26 @@ def get_settings() -> Dict[str, Any]:
|
|
| 11938 |
"hydra_llm_model": "",
|
| 11939 |
"hydra_llm_api_key": "",
|
| 11940 |
"hydra_hf_transformers_context_tokens": str(DEFAULT_HF_TRANSFORMERS_CONTEXT_TOKENS),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11941 |
"hydra_llama_cpp_context_tokens": str(DEFAULT_LLAMA_CPP_CONTEXT_TOKENS),
|
| 11942 |
"hydra_llama_cpp_vision_context_tokens": str(DEFAULT_LLAMA_CPP_VISION_CONTEXT_TOKENS),
|
| 11943 |
"hydra_llama_cpp_mtp_enabled": bool(DEFAULT_LLAMA_CPP_MTP_ENABLED),
|
| 11944 |
"hydra_llama_cpp_mtp_draft_tokens": str(DEFAULT_LLAMA_CPP_MTP_DRAFT_TOKENS),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11945 |
"hydra_mlx_lm_context_tokens": "",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11946 |
"hydra_beast_mode_enabled": False,
|
| 11947 |
"hydra_max_ledger_items": int(DEFAULT_MAX_LEDGER_ITEMS),
|
| 11948 |
"hydra_astraeus_plan_review_enabled": bool(DEFAULT_ASTRAEUS_PLAN_REVIEW_ENABLED),
|
|
@@ -12049,6 +12653,35 @@ def get_settings() -> Dict[str, Any]:
|
|
| 12049 |
("TATER_HF_TRANSFORMERS_MAX_INPUT_TOKENS",),
|
| 12050 |
DEFAULT_HF_TRANSFORMERS_CONTEXT_TOKENS,
|
| 12051 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12052 |
"hydra_llama_cpp_context_tokens": _read_local_llm_context_setting(
|
| 12053 |
HYDRA_LLAMA_CPP_CONTEXT_TOKENS_KEY,
|
| 12054 |
("TATER_LLAMA_CPP_N_CTX", "LLM_CONTEXT_SIZE"),
|
|
@@ -12071,12 +12704,71 @@ def get_settings() -> Dict[str, Any]:
|
|
| 12071 |
minimum=1,
|
| 12072 |
maximum=16,
|
| 12073 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12074 |
"hydra_mlx_lm_context_tokens": _read_local_llm_context_setting(
|
| 12075 |
HYDRA_MLX_LM_CONTEXT_TOKENS_KEY,
|
| 12076 |
("TATER_MLX_LM_MAX_KV_SIZE",),
|
| 12077 |
None,
|
| 12078 |
minimum=128,
|
| 12079 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12080 |
"spudex_llm_provider": _normalize_hydra_llm_provider(spudex_settings.get("llm_provider") or ""),
|
| 12081 |
"spudex_llm_host": str(spudex_settings.get("llm_host") or ""),
|
| 12082 |
"spudex_llm_model": str(spudex_settings.get("llm_model") or ""),
|
|
@@ -12924,6 +13616,57 @@ def get_llama_cpp_diagnostics() -> Dict[str, Any]:
|
|
| 12924 |
return get_llama_cpp_runtime_diagnostics()
|
| 12925 |
|
| 12926 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12927 |
@app.get("/api/settings/huggingface/models")
|
| 12928 |
def get_huggingface_models(
|
| 12929 |
provider: str = "hf_transformers",
|
|
@@ -12938,6 +13681,25 @@ def get_huggingface_models(
|
|
| 12938 |
provider_token = HYDRA_LLM_PROVIDER_HF_TRANSFORMERS
|
| 12939 |
view_token = str(view or "trending").strip().lower().replace("_", "-")
|
| 12940 |
task_token = _normalize_hf_browser_task(task)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12941 |
if view_token in {"new", "recent", "latest"}:
|
| 12942 |
sort = "lastModified"
|
| 12943 |
response_view = "new"
|
|
@@ -12947,9 +13709,6 @@ def get_huggingface_models(
|
|
| 12947 |
else:
|
| 12948 |
sort = "trendingScore"
|
| 12949 |
response_view = "trending"
|
| 12950 |
-
clean_limit = max(4, min(48, int(limit or 24)))
|
| 12951 |
-
search = _hf_browser_provider_search(provider_token, query, task_token)
|
| 12952 |
-
integration_status = _hf_browser_integration_status()
|
| 12953 |
page_url = _hf_browser_cursor_decode(cursor) if str(cursor or "").strip() else _hf_browser_models_api_url(
|
| 12954 |
provider=provider_token,
|
| 12955 |
search=search,
|
|
@@ -12988,7 +13747,7 @@ def get_huggingface_models(
|
|
| 12988 |
"query": search,
|
| 12989 |
"task": task_token,
|
| 12990 |
"library": _hf_browser_provider_library(provider_token),
|
| 12991 |
-
"app_filter": _hf_browser_provider_app_filter(provider_token),
|
| 12992 |
"integration": integration_status,
|
| 12993 |
"limit": clean_limit,
|
| 12994 |
"has_next": bool(next_cursor),
|
|
@@ -13244,6 +14003,60 @@ def update_settings(payload: AppSettingsRequest, response: Response) -> Dict[str
|
|
| 13244 |
)
|
| 13245 |
redis_client.set(redis_key, str(int(parsed)))
|
| 13246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13247 |
_save_tater_api_settings_from_updates(updates)
|
| 13248 |
|
| 13249 |
local_model_keys_cache: Optional[set[Tuple[str, str]]] = None
|
|
@@ -13308,10 +14121,25 @@ def update_settings(payload: AppSettingsRequest, response: Response) -> Dict[str
|
|
| 13308 |
"hydra_llm_provider",
|
| 13309 |
"hydra_base_servers",
|
| 13310 |
"hydra_hf_transformers_context_tokens",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13311 |
"hydra_llama_cpp_context_tokens",
|
| 13312 |
"hydra_llama_cpp_mtp_enabled",
|
| 13313 |
"hydra_llama_cpp_mtp_draft_tokens",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13314 |
"hydra_mlx_lm_context_tokens",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13315 |
}
|
| 13316 |
spudex_model_keys = {"spudex_llm_provider", "spudex_llm_host", "spudex_llm_model"}
|
| 13317 |
vision_model_keys = {
|
|
@@ -13402,6 +14230,35 @@ def update_settings(payload: AppSettingsRequest, response: Response) -> Dict[str
|
|
| 13402 |
"hydra_hf_transformers_context_tokens",
|
| 13403 |
HYDRA_HF_TRANSFORMERS_CONTEXT_TOKENS_KEY,
|
| 13404 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13405 |
_save_local_llm_context_setting(
|
| 13406 |
"hydra_llama_cpp_context_tokens",
|
| 13407 |
HYDRA_LLAMA_CPP_CONTEXT_TOKENS_KEY,
|
|
@@ -13422,11 +14279,70 @@ def update_settings(payload: AppSettingsRequest, response: Response) -> Dict[str
|
|
| 13422 |
min_value=1,
|
| 13423 |
max_value=16,
|
| 13424 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13425 |
_save_local_llm_context_setting(
|
| 13426 |
"hydra_mlx_lm_context_tokens",
|
| 13427 |
HYDRA_MLX_LM_CONTEXT_TOKENS_KEY,
|
| 13428 |
min_value=128,
|
| 13429 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13430 |
|
| 13431 |
username = updates.get("username")
|
| 13432 |
if isinstance(username, str):
|
|
@@ -13590,8 +14506,6 @@ def update_settings(payload: AppSettingsRequest, response: Response) -> Dict[str
|
|
| 13590 |
updates.get("vision_provider", current_vision.get("provider") or HYDRA_LLM_PROVIDER_OPENAI_COMPATIBLE)
|
| 13591 |
)
|
| 13592 |
vision_model = str(updates.get("vision_model", current_vision.get("model") or "")).strip()
|
| 13593 |
-
if vision_mode == "dedicated" and vision_provider == HYDRA_LLM_PROVIDER_MLX_LM:
|
| 13594 |
-
raise HTTPException(status_code=400, detail="Dedicated local vision currently supports Transformers and llama.cpp GGUF models.")
|
| 13595 |
if vision_mode == "dedicated" and _is_local_hydra_llm_provider(vision_provider):
|
| 13596 |
_require_downloaded_local_model(vision_provider, vision_model, "Vision")
|
| 13597 |
save_shared_vision_settings(
|
|
|
|
| 6 |
import importlib
|
| 7 |
import json
|
| 8 |
import logging
|
| 9 |
+
import mimetypes
|
| 10 |
import os
|
| 11 |
import queue
|
| 12 |
import re
|
|
|
|
| 64 |
from emoji_responder import get_emoji_settings as get_core_emoji_settings, save_emoji_settings as save_core_emoji_settings
|
| 65 |
from notify import notifier_destination_catalog
|
| 66 |
from helpers import (
|
| 67 |
+
DEFAULT_HF_TRANSFORMERS_ATTN_IMPLEMENTATION,
|
| 68 |
DEFAULT_HF_TRANSFORMERS_CONTEXT_TOKENS,
|
| 69 |
+
DEFAULT_HF_TRANSFORMERS_DEVICE,
|
| 70 |
+
DEFAULT_HF_TRANSFORMERS_DEVICE_MAP,
|
| 71 |
+
DEFAULT_HF_TRANSFORMERS_DTYPE,
|
| 72 |
+
DEFAULT_HF_TRANSFORMERS_TRUST_REMOTE_CODE,
|
| 73 |
+
DEFAULT_LLAMA_CPP_FLASH_ATTN,
|
| 74 |
DEFAULT_LLAMA_CPP_CONTEXT_TOKENS,
|
| 75 |
DEFAULT_LLAMA_CPP_MTP_DRAFT_TOKENS,
|
| 76 |
DEFAULT_LLAMA_CPP_MTP_ENABLED,
|
| 77 |
+
DEFAULT_LLAMA_CPP_N_BATCH,
|
| 78 |
+
DEFAULT_LLAMA_CPP_N_UBATCH,
|
| 79 |
+
DEFAULT_LLAMA_CPP_OFFLOAD_KQV,
|
| 80 |
DEFAULT_LLAMA_CPP_VISION_CONTEXT_TOKENS,
|
| 81 |
+
DEFAULT_MLX_LM_LAZY_LOAD,
|
| 82 |
+
DEFAULT_MLX_LM_TRUST_REMOTE_CODE,
|
| 83 |
+
HYDRA_HF_TRANSFORMERS_ATTN_IMPLEMENTATION_KEY,
|
| 84 |
HYDRA_HF_TRANSFORMERS_CONTEXT_TOKENS_KEY,
|
| 85 |
+
HYDRA_HF_TRANSFORMERS_DEVICE_KEY,
|
| 86 |
+
HYDRA_HF_TRANSFORMERS_DEVICE_MAP_KEY,
|
| 87 |
+
HYDRA_HF_TRANSFORMERS_DTYPE_KEY,
|
| 88 |
+
HYDRA_HF_TRANSFORMERS_TRUST_REMOTE_CODE_KEY,
|
| 89 |
HYDRA_LLM_BASE_SERVERS_KEY,
|
| 90 |
HYDRA_LLM_PROVIDER_HF_TRANSFORMERS,
|
| 91 |
HYDRA_LLM_PROVIDER_KEY,
|
|
|
|
| 93 |
HYDRA_LLM_PROVIDER_MLX_LM,
|
| 94 |
HYDRA_LLM_PROVIDER_OPENAI_COMPATIBLE,
|
| 95 |
HYDRA_LLAMA_CPP_CONTEXT_TOKENS_KEY,
|
| 96 |
+
HYDRA_LLAMA_CPP_FLASH_ATTN_KEY,
|
| 97 |
HYDRA_LLAMA_CPP_MTP_DRAFT_TOKENS_KEY,
|
| 98 |
HYDRA_LLAMA_CPP_MTP_ENABLED_KEY,
|
| 99 |
+
HYDRA_LLAMA_CPP_N_BATCH_KEY,
|
| 100 |
+
HYDRA_LLAMA_CPP_N_UBATCH_KEY,
|
| 101 |
+
HYDRA_LLAMA_CPP_OFFLOAD_KQV_KEY,
|
| 102 |
HYDRA_LLAMA_CPP_VISION_CONTEXT_TOKENS_KEY,
|
| 103 |
+
HYDRA_MLX_ENGINE_KV_BITS_KEY,
|
| 104 |
+
HYDRA_MLX_ENGINE_KV_GROUP_SIZE_KEY,
|
| 105 |
+
HYDRA_MLX_ENGINE_PREFILL_STEP_SIZE_KEY,
|
| 106 |
+
HYDRA_MLX_ENGINE_QUANTIZED_KV_START_KEY,
|
| 107 |
HYDRA_MLX_LM_CONTEXT_TOKENS_KEY,
|
| 108 |
+
HYDRA_MLX_LM_LAZY_LOAD_KEY,
|
| 109 |
+
HYDRA_MLX_LM_TRUST_REMOTE_CODE_KEY,
|
| 110 |
HfLlmDownloadCancelled,
|
| 111 |
decrypt_current_redis_snapshot,
|
| 112 |
download_hf_transformers_llm_model,
|
|
|
|
| 115 |
encrypt_current_redis_snapshot,
|
| 116 |
ensure_redis_encryption_key,
|
| 117 |
get_llama_cpp_runtime_diagnostics,
|
| 118 |
+
get_llama_cpp_chat_template_info,
|
| 119 |
+
get_local_llm_chat_template_info,
|
| 120 |
get_local_llm_loaded_models_snapshot,
|
| 121 |
get_redis_connection_config,
|
| 122 |
get_redis_encryption_status,
|
| 123 |
get_redis_connection_status,
|
| 124 |
get_llm_call_runtime_summary,
|
| 125 |
+
get_llm_debug_runtime_snapshot,
|
| 126 |
get_vision_call_runtime_summary,
|
| 127 |
get_llm_client_from_env,
|
| 128 |
preload_hf_transformers_llm_model,
|
|
|
|
| 138 |
set_main_loop,
|
| 139 |
shutdown_internal_redis,
|
| 140 |
test_redis_connection_settings,
|
| 141 |
+
set_llama_cpp_chat_template_override,
|
| 142 |
+
clear_llama_cpp_chat_template_override,
|
| 143 |
+
set_local_llm_chat_template_override,
|
| 144 |
+
clear_local_llm_chat_template_override,
|
| 145 |
unload_local_llm_models,
|
| 146 |
)
|
| 147 |
from runtime_executors import configure_runtime_executors, run_dashboard, run_wake, shutdown_runtime_executors
|
|
|
|
| 567 |
return _as_bool_flag(raw, default=default)
|
| 568 |
|
| 569 |
|
| 570 |
+
def _read_text_choice_setting(
|
| 571 |
+
redis_key: str,
|
| 572 |
+
env_keys: Tuple[str, ...],
|
| 573 |
+
default: str,
|
| 574 |
+
*,
|
| 575 |
+
allowed: Tuple[str, ...],
|
| 576 |
+
) -> str:
|
| 577 |
+
raw = str(redis_client.get(redis_key) or "").strip()
|
| 578 |
+
if not raw:
|
| 579 |
+
for env_key in env_keys:
|
| 580 |
+
raw = str(os.getenv(env_key) or "").strip()
|
| 581 |
+
if raw:
|
| 582 |
+
break
|
| 583 |
+
token = str(raw or default or "").strip().lower()
|
| 584 |
+
aliases = {
|
| 585 |
+
"fp16": "float16",
|
| 586 |
+
"half": "float16",
|
| 587 |
+
"bf16": "bfloat16",
|
| 588 |
+
"fp32": "float32",
|
| 589 |
+
"none": "disabled",
|
| 590 |
+
"off": "disabled",
|
| 591 |
+
"false": "disabled",
|
| 592 |
+
"0": "disabled",
|
| 593 |
+
"default": "default",
|
| 594 |
+
"auto": "auto",
|
| 595 |
+
}
|
| 596 |
+
token = aliases.get(token, token)
|
| 597 |
+
allowed_set = {str(item).strip().lower() for item in allowed}
|
| 598 |
+
default_token = str(default or "").strip().lower()
|
| 599 |
+
return token if token in allowed_set else default_token
|
| 600 |
+
|
| 601 |
+
|
| 602 |
+
def _read_optional_int_setting(
|
| 603 |
+
redis_key: str,
|
| 604 |
+
env_keys: Tuple[str, ...],
|
| 605 |
+
*,
|
| 606 |
+
minimum: int = 0,
|
| 607 |
+
maximum: int = 1_048_576,
|
| 608 |
+
allow_zero: bool = False,
|
| 609 |
+
) -> str:
|
| 610 |
+
raw = str(redis_client.get(redis_key) or "").strip()
|
| 611 |
+
if not raw:
|
| 612 |
+
for env_key in env_keys:
|
| 613 |
+
raw = str(os.getenv(env_key) or "").strip()
|
| 614 |
+
if raw:
|
| 615 |
+
break
|
| 616 |
+
if not raw:
|
| 617 |
+
return ""
|
| 618 |
+
try:
|
| 619 |
+
value = int(float(raw))
|
| 620 |
+
except Exception:
|
| 621 |
+
return ""
|
| 622 |
+
if value < 0 or (value == 0 and not allow_zero):
|
| 623 |
+
return ""
|
| 624 |
+
return str(max(int(minimum), min(int(maximum), int(value))))
|
| 625 |
+
|
| 626 |
+
|
| 627 |
def _read_bounded_int_setting(
|
| 628 |
redis_key: str,
|
| 629 |
env_keys: Tuple[str, ...],
|
|
|
|
| 1695 |
task: str = "text-generation",
|
| 1696 |
) -> str:
|
| 1697 |
library = _hf_browser_provider_library(provider)
|
| 1698 |
+
app_filter = _hf_browser_provider_app_filter(provider, task)
|
| 1699 |
+
pipeline_tag = _hf_browser_provider_pipeline_filter(provider, task)
|
| 1700 |
params: Dict[str, Any] = {
|
| 1701 |
"sort": sort,
|
| 1702 |
"direction": "-1",
|
| 1703 |
"limit": str(max(1, min(100, int(limit or 24)))),
|
| 1704 |
"full": "true",
|
| 1705 |
"cardData": "true",
|
|
|
|
| 1706 |
}
|
| 1707 |
+
if pipeline_tag:
|
| 1708 |
+
params["pipeline_tag"] = pipeline_tag
|
| 1709 |
if library:
|
| 1710 |
params["library"] = library
|
| 1711 |
if app_filter:
|
|
|
|
| 1895 |
return q
|
| 1896 |
|
| 1897 |
|
| 1898 |
+
HF_BROWSER_TATER_PICKS: Tuple[Dict[str, Any], ...] = (
|
| 1899 |
+
{
|
| 1900 |
+
"id": "TaterTotterson/gemma-4-26B-A4B-it-GGUF-Tater-NoThink",
|
| 1901 |
+
"provider": HYDRA_LLM_PROVIDER_LLAMA_CPP,
|
| 1902 |
+
"author": "TaterTotterson",
|
| 1903 |
+
"model_size": "26B",
|
| 1904 |
+
"library_name": "llama.cpp",
|
| 1905 |
+
"pipeline_tag": "image-text-to-text",
|
| 1906 |
+
"license": "apache-2.0",
|
| 1907 |
+
"supports_vision": True,
|
| 1908 |
+
"tasks": ("text-generation", "image-text-to-text"),
|
| 1909 |
+
"tags": (
|
| 1910 |
+
"tater",
|
| 1911 |
+
"nothink",
|
| 1912 |
+
"gguf",
|
| 1913 |
+
"llama.cpp",
|
| 1914 |
+
"gemma4",
|
| 1915 |
+
"vision",
|
| 1916 |
+
"UD-Q4_K_M",
|
| 1917 |
+
),
|
| 1918 |
+
"tater_pick_label": "Tater Pick",
|
| 1919 |
+
"tater_pick_note": "NoThink UD-Q4_K_M with matching mmproj-F16.",
|
| 1920 |
+
"preferred_gguf": "gemma-4-26B-A4B-it-UD-Q4_K_M.gguf",
|
| 1921 |
+
"preferred_mmproj": "mmproj-F16.gguf",
|
| 1922 |
+
},
|
| 1923 |
+
)
|
| 1924 |
+
|
| 1925 |
+
|
| 1926 |
+
def _hf_browser_tater_pick_owner() -> str:
|
| 1927 |
+
return str(os.getenv("TATER_HF_TATER_PICK_OWNER") or "TaterTotterson").strip()
|
| 1928 |
+
|
| 1929 |
+
|
| 1930 |
+
def _hf_browser_tater_pick_tag_tokens() -> Tuple[str, ...]:
|
| 1931 |
+
raw = str(os.getenv("TATER_HF_TATER_PICK_TAGS") or "").strip()
|
| 1932 |
+
if raw:
|
| 1933 |
+
tags = [item.strip().lower() for item in re.split(r"[,;\s]+", raw) if item.strip()]
|
| 1934 |
+
else:
|
| 1935 |
+
tags = ["tater-pick", "tater-recommended", "tater-nothink"]
|
| 1936 |
+
return tuple(dict.fromkeys(tags))
|
| 1937 |
+
|
| 1938 |
+
|
| 1939 |
+
def _hf_browser_card_tags(card_data: Any) -> List[str]:
|
| 1940 |
+
if not card_data:
|
| 1941 |
+
return []
|
| 1942 |
+
if isinstance(card_data, dict):
|
| 1943 |
+
raw = card_data.get("tags") or []
|
| 1944 |
+
else:
|
| 1945 |
+
raw = getattr(card_data, "tags", []) or []
|
| 1946 |
+
if isinstance(raw, str):
|
| 1947 |
+
return [raw]
|
| 1948 |
+
if isinstance(raw, list):
|
| 1949 |
+
return [str(item or "").strip() for item in raw if str(item or "").strip()]
|
| 1950 |
+
return []
|
| 1951 |
+
|
| 1952 |
+
|
| 1953 |
+
def _hf_browser_paths_to_file_rows(paths: List[str]) -> List[Dict[str, Any]]:
|
| 1954 |
+
rows: List[Dict[str, Any]] = []
|
| 1955 |
+
seen: set[str] = set()
|
| 1956 |
+
for raw_path in paths:
|
| 1957 |
+
path = str(raw_path or "").strip()
|
| 1958 |
+
if not path or path in seen:
|
| 1959 |
+
continue
|
| 1960 |
+
seen.add(path)
|
| 1961 |
+
lower = path.lower()
|
| 1962 |
+
rows.append(
|
| 1963 |
+
{
|
| 1964 |
+
"path": path,
|
| 1965 |
+
"size": 0,
|
| 1966 |
+
"is_gguf": lower.endswith(".gguf"),
|
| 1967 |
+
"is_mmproj": lower.endswith(".gguf") and "mmproj" in lower,
|
| 1968 |
+
"is_safetensors": lower.endswith(".safetensors"),
|
| 1969 |
+
"is_config": os.path.basename(lower) in {"config.json", "tokenizer.json", "tokenizer.model"},
|
| 1970 |
+
"quant": _hf_browser_file_quant(path),
|
| 1971 |
+
}
|
| 1972 |
+
)
|
| 1973 |
+
return rows
|
| 1974 |
+
|
| 1975 |
+
|
| 1976 |
+
def _hf_browser_tater_pick_metadata(
|
| 1977 |
+
model_id: Any,
|
| 1978 |
+
*,
|
| 1979 |
+
tags: Optional[List[str]] = None,
|
| 1980 |
+
card_data: Any = None,
|
| 1981 |
+
files: Optional[List[str]] = None,
|
| 1982 |
+
) -> Dict[str, Any]:
|
| 1983 |
+
repo = str(model_id or "").strip()
|
| 1984 |
+
if not repo:
|
| 1985 |
+
return {}
|
| 1986 |
+
|
| 1987 |
+
static_row = _hf_browser_tater_pick_by_id(repo)
|
| 1988 |
+
if static_row:
|
| 1989 |
+
return static_row
|
| 1990 |
+
|
| 1991 |
+
owner = _hf_browser_tater_pick_owner().lower()
|
| 1992 |
+
if not owner or "/" not in repo or repo.split("/", 1)[0].lower() != owner:
|
| 1993 |
+
return {}
|
| 1994 |
+
|
| 1995 |
+
tag_set = {
|
| 1996 |
+
str(item or "").strip().lower()
|
| 1997 |
+
for item in [*(tags or []), *_hf_browser_card_tags(card_data)]
|
| 1998 |
+
if str(item or "").strip()
|
| 1999 |
+
}
|
| 2000 |
+
pick_tags = set(_hf_browser_tater_pick_tag_tokens())
|
| 2001 |
+
has_pick_tag = bool(tag_set.intersection(pick_tags))
|
| 2002 |
+
has_nothink_marker = any(
|
| 2003 |
+
token in tag_set
|
| 2004 |
+
for token in (
|
| 2005 |
+
"nothink",
|
| 2006 |
+
"no-think",
|
| 2007 |
+
"no_think",
|
| 2008 |
+
"thinking-disabled",
|
| 2009 |
+
"disable-thinking",
|
| 2010 |
+
"non-thinking",
|
| 2011 |
+
)
|
| 2012 |
+
)
|
| 2013 |
+
if not has_pick_tag:
|
| 2014 |
+
return {}
|
| 2015 |
+
|
| 2016 |
+
file_rows = _hf_browser_paths_to_file_rows(files or [])
|
| 2017 |
+
preferred_gguf = _hf_browser_preferred_gguf(file_rows) if file_rows else ""
|
| 2018 |
+
preferred_mmproj = _hf_browser_preferred_mmproj(file_rows, preferred_gguf) if file_rows else ""
|
| 2019 |
+
quant = _hf_browser_file_quant(preferred_gguf)
|
| 2020 |
+
note_bits: List[str] = []
|
| 2021 |
+
if has_nothink_marker:
|
| 2022 |
+
note_bits.append("NoThink")
|
| 2023 |
+
if quant:
|
| 2024 |
+
note_bits.append(quant)
|
| 2025 |
+
if preferred_mmproj:
|
| 2026 |
+
note_bits.append("with matching projector")
|
| 2027 |
+
note = " ".join(note_bits).strip()
|
| 2028 |
+
return {
|
| 2029 |
+
"id": repo,
|
| 2030 |
+
"tater_pick_label": "Tater Pick",
|
| 2031 |
+
"tater_pick_note": note or "Curated Tater model.",
|
| 2032 |
+
"preferred_gguf": preferred_gguf,
|
| 2033 |
+
"preferred_mmproj": preferred_mmproj,
|
| 2034 |
+
}
|
| 2035 |
+
|
| 2036 |
+
|
| 2037 |
+
def _hf_browser_tater_pick_by_id(model_id: Any) -> Dict[str, Any]:
|
| 2038 |
+
needle = str(model_id or "").strip().lower()
|
| 2039 |
+
if not needle:
|
| 2040 |
+
return {}
|
| 2041 |
+
for row in HF_BROWSER_TATER_PICKS:
|
| 2042 |
+
if str(row.get("id") or "").strip().lower() == needle:
|
| 2043 |
+
return dict(row)
|
| 2044 |
+
return {}
|
| 2045 |
+
|
| 2046 |
+
|
| 2047 |
+
def _hf_browser_tater_pick_matches_query(row: Dict[str, Any], query: str) -> bool:
|
| 2048 |
+
q = str(query or "").strip().lower()
|
| 2049 |
+
if not q:
|
| 2050 |
+
return True
|
| 2051 |
+
haystack = " ".join(
|
| 2052 |
+
[
|
| 2053 |
+
str(row.get("id") or ""),
|
| 2054 |
+
str(row.get("provider") or ""),
|
| 2055 |
+
str(row.get("library_name") or ""),
|
| 2056 |
+
str(row.get("model_size") or ""),
|
| 2057 |
+
str(row.get("tater_pick_note") or ""),
|
| 2058 |
+
" ".join(str(item or "") for item in (row.get("tags") or [])),
|
| 2059 |
+
]
|
| 2060 |
+
).lower()
|
| 2061 |
+
return all(token in haystack for token in q.split())
|
| 2062 |
+
|
| 2063 |
+
|
| 2064 |
+
def _hf_browser_tater_pick_summary(row: Dict[str, Any]) -> Dict[str, Any]:
|
| 2065 |
+
provider_token = _normalize_hydra_llm_provider(row.get("provider"))
|
| 2066 |
+
tags = [str(item or "").strip() for item in (row.get("tags") or []) if str(item or "").strip()]
|
| 2067 |
+
return {
|
| 2068 |
+
"id": str(row.get("id") or "").strip(),
|
| 2069 |
+
"author": str(row.get("author") or "").strip(),
|
| 2070 |
+
"model_size": str(row.get("model_size") or "").strip(),
|
| 2071 |
+
"downloads": 0,
|
| 2072 |
+
"likes": 0,
|
| 2073 |
+
"last_modified": "",
|
| 2074 |
+
"pipeline_tag": str(row.get("pipeline_tag") or "").strip(),
|
| 2075 |
+
"library_name": str(row.get("library_name") or "").strip(),
|
| 2076 |
+
"tags": tags[:30],
|
| 2077 |
+
"license": str(row.get("license") or "").strip(),
|
| 2078 |
+
"private": False,
|
| 2079 |
+
"gated": "",
|
| 2080 |
+
"compatible": True,
|
| 2081 |
+
"supports_vision": bool(row.get("supports_vision")),
|
| 2082 |
+
"provider": provider_token,
|
| 2083 |
+
"provider_label": _hydra_llm_provider_label(provider_token),
|
| 2084 |
+
"tater_pick": True,
|
| 2085 |
+
"tater_pick_label": str(row.get("tater_pick_label") or "Tater Pick").strip(),
|
| 2086 |
+
"tater_pick_note": str(row.get("tater_pick_note") or "").strip(),
|
| 2087 |
+
"preferred_gguf": str(row.get("preferred_gguf") or "").strip(),
|
| 2088 |
+
"preferred_mmproj": str(row.get("preferred_mmproj") or "").strip(),
|
| 2089 |
+
}
|
| 2090 |
+
|
| 2091 |
+
|
| 2092 |
+
def _hf_browser_provider_for_model(model: Any, fallback: str = HYDRA_LLM_PROVIDER_HF_TRANSFORMERS) -> str:
|
| 2093 |
+
for provider_token in (HYDRA_LLM_PROVIDER_LLAMA_CPP, HYDRA_LLM_PROVIDER_MLX_LM, HYDRA_LLM_PROVIDER_HF_TRANSFORMERS):
|
| 2094 |
+
try:
|
| 2095 |
+
if _hf_browser_provider_matches(model, provider_token):
|
| 2096 |
+
return provider_token
|
| 2097 |
+
except Exception:
|
| 2098 |
+
continue
|
| 2099 |
+
return _normalize_hydra_llm_provider(fallback)
|
| 2100 |
+
|
| 2101 |
+
|
| 2102 |
+
def _hf_browser_tater_pick_models_from_hub(*, query: str, task: str, limit: int, provider: str) -> List[Dict[str, Any]]:
|
| 2103 |
+
owner = _hf_browser_tater_pick_owner()
|
| 2104 |
+
if not owner:
|
| 2105 |
+
return []
|
| 2106 |
+
try:
|
| 2107 |
+
from huggingface_hub import HfApi # type: ignore
|
| 2108 |
+
except Exception:
|
| 2109 |
+
return []
|
| 2110 |
+
|
| 2111 |
+
api = HfApi(token=_hf_browser_token())
|
| 2112 |
+
try:
|
| 2113 |
+
iterator = _hf_hub_call_compat(
|
| 2114 |
+
api.list_models,
|
| 2115 |
+
author=owner,
|
| 2116 |
+
sort="lastModified",
|
| 2117 |
+
direction=-1,
|
| 2118 |
+
limit=max(50, min(200, int(limit or 24) * 4)),
|
| 2119 |
+
full=True,
|
| 2120 |
+
cardData=True,
|
| 2121 |
+
fetch_config=True,
|
| 2122 |
+
)
|
| 2123 |
+
raw_models = list(iterator or [])
|
| 2124 |
+
except Exception as exc:
|
| 2125 |
+
logger.warning("[huggingface-browser] Tater Picks live lookup failed: %s", exc)
|
| 2126 |
+
return []
|
| 2127 |
+
|
| 2128 |
+
task_token = _normalize_hf_browser_task(task)
|
| 2129 |
+
provider_filter = _normalize_hydra_llm_provider(provider)
|
| 2130 |
+
rows: List[Dict[str, Any]] = []
|
| 2131 |
+
seen: set[str] = set()
|
| 2132 |
+
for model in raw_models:
|
| 2133 |
+
model_id = str(_hf_browser_object_value(model, "modelId") or _hf_browser_object_value(model, "id") or "").strip()
|
| 2134 |
+
if not model_id or model_id.lower() in seen:
|
| 2135 |
+
continue
|
| 2136 |
+
seen.add(model_id.lower())
|
| 2137 |
+
tags = [
|
| 2138 |
+
str(item or "").strip()
|
| 2139 |
+
for item in (_hf_browser_object_value(model, "tags", []) or [])
|
| 2140 |
+
if str(item or "").strip()
|
| 2141 |
+
]
|
| 2142 |
+
siblings = _hf_browser_object_value(model, "siblings", []) or []
|
| 2143 |
+
files = [
|
| 2144 |
+
str(_hf_browser_object_value(item, "rfilename") or _hf_browser_object_value(item, "path") or "").strip()
|
| 2145 |
+
for item in siblings
|
| 2146 |
+
if str(_hf_browser_object_value(item, "rfilename") or _hf_browser_object_value(item, "path") or "").strip()
|
| 2147 |
+
]
|
| 2148 |
+
card_data = _hf_browser_object_value(model, "cardData", None) or _hf_browser_object_value(model, "card_data", None)
|
| 2149 |
+
if not _hf_browser_tater_pick_metadata(model_id, tags=tags, card_data=card_data, files=files):
|
| 2150 |
+
continue
|
| 2151 |
+
provider_token = _hf_browser_provider_for_model(model, fallback=HYDRA_LLM_PROVIDER_LLAMA_CPP)
|
| 2152 |
+
if provider_token != provider_filter:
|
| 2153 |
+
continue
|
| 2154 |
+
summary = _hf_browser_model_summary(model, provider=provider_token)
|
| 2155 |
+
if task_token == "image-text-to-text" and not bool(summary.get("supports_vision")):
|
| 2156 |
+
continue
|
| 2157 |
+
if not _hf_browser_tater_pick_matches_query(summary, query):
|
| 2158 |
+
continue
|
| 2159 |
+
rows.append(summary)
|
| 2160 |
+
return rows[: max(1, min(100, int(limit or 24)))]
|
| 2161 |
+
|
| 2162 |
+
|
| 2163 |
+
def _hf_browser_tater_pick_models(*, query: str, task: str, limit: int, provider: str) -> List[Dict[str, Any]]:
|
| 2164 |
+
task_token = _normalize_hf_browser_task(task)
|
| 2165 |
+
provider_filter = _normalize_hydra_llm_provider(provider)
|
| 2166 |
+
live_rows = _hf_browser_tater_pick_models_from_hub(query=query, task=task_token, limit=limit, provider=provider_filter)
|
| 2167 |
+
rows: List[Dict[str, Any]] = list(live_rows)
|
| 2168 |
+
seen = {str(row.get("id") or "").strip().lower() for row in rows if str(row.get("id") or "").strip()}
|
| 2169 |
+
for row in HF_BROWSER_TATER_PICKS:
|
| 2170 |
+
if str(row.get("id") or "").strip().lower() in seen:
|
| 2171 |
+
continue
|
| 2172 |
+
if _normalize_hydra_llm_provider(row.get("provider")) != provider_filter:
|
| 2173 |
+
continue
|
| 2174 |
+
tasks = {str(item or "").strip() for item in (row.get("tasks") or []) if str(item or "").strip()}
|
| 2175 |
+
if tasks and task_token not in tasks:
|
| 2176 |
+
continue
|
| 2177 |
+
if not _hf_browser_tater_pick_matches_query(row, query):
|
| 2178 |
+
continue
|
| 2179 |
+
rows.append(_hf_browser_tater_pick_summary(row))
|
| 2180 |
+
return rows[: max(1, min(100, int(limit or 24)))]
|
| 2181 |
+
|
| 2182 |
+
|
| 2183 |
+
def _hf_browser_provider_app_filter(provider: str, task: str = "text-generation") -> str:
|
| 2184 |
provider_token = _normalize_hydra_llm_provider(provider)
|
| 2185 |
+
task_token = _normalize_hf_browser_task(task)
|
| 2186 |
if provider_token == HYDRA_LLM_PROVIDER_LLAMA_CPP:
|
| 2187 |
return "llama.cpp"
|
| 2188 |
if provider_token == HYDRA_LLM_PROVIDER_MLX_LM:
|
| 2189 |
+
if task_token == "image-text-to-text":
|
| 2190 |
+
return ""
|
| 2191 |
return "mlx-lm"
|
| 2192 |
return ""
|
| 2193 |
|
|
|
|
| 2199 |
return "transformers"
|
| 2200 |
|
| 2201 |
|
| 2202 |
+
def _hf_browser_provider_pipeline_filter(provider: str, task: str = "text-generation") -> str:
|
| 2203 |
+
provider_token = _normalize_hydra_llm_provider(provider)
|
| 2204 |
+
task_token = _normalize_hf_browser_task(task)
|
| 2205 |
+
if task_token == "text-generation" and provider_token in {HYDRA_LLM_PROVIDER_LLAMA_CPP, HYDRA_LLM_PROVIDER_MLX_LM}:
|
| 2206 |
+
return ""
|
| 2207 |
+
return task_token
|
| 2208 |
+
|
| 2209 |
+
|
| 2210 |
def _hf_browser_text_generation_tokens() -> Tuple[str, ...]:
|
| 2211 |
+
return ("text-generation", "text2text-generation", "conversational", "image-text-to-text")
|
| 2212 |
|
| 2213 |
|
| 2214 |
def _hf_browser_vision_tokens() -> Tuple[str, ...]:
|
|
|
|
| 2301 |
return (
|
| 2302 |
"mlx-lm" in provider_haystack
|
| 2303 |
or "mlx_lm" in provider_haystack
|
| 2304 |
+
or "mlx-vlm" in provider_haystack
|
| 2305 |
+
or "mlx_vlm" in provider_haystack
|
| 2306 |
or library == "mlx"
|
| 2307 |
or "mlx" in tags
|
| 2308 |
or "mlx" in files
|
|
|
|
| 2313 |
|
| 2314 |
def _hf_browser_file_quant(path: str) -> str:
|
| 2315 |
name = os.path.basename(str(path or "")).upper()
|
| 2316 |
+
for quant in (
|
| 2317 |
+
"UD-Q4_K_M",
|
| 2318 |
+
"UD_Q4_K_M",
|
| 2319 |
+
"UD-Q4_K_S",
|
| 2320 |
+
"UD_Q4_K_S",
|
| 2321 |
+
"UD-Q5_K_M",
|
| 2322 |
+
"UD_Q5_K_M",
|
| 2323 |
+
"Q2_K",
|
| 2324 |
+
"Q3_K_M",
|
| 2325 |
+
"Q4_K_M",
|
| 2326 |
+
"Q4_K_S",
|
| 2327 |
+
"Q5_K_M",
|
| 2328 |
+
"Q5_K_S",
|
| 2329 |
+
"Q6_K",
|
| 2330 |
+
"Q8_0",
|
| 2331 |
+
"F16",
|
| 2332 |
+
"BF16",
|
| 2333 |
+
):
|
| 2334 |
if quant in name:
|
| 2335 |
+
return quant.replace("_Q", "-Q", 1) if quant.startswith("UD_") else quant
|
| 2336 |
return ""
|
| 2337 |
|
| 2338 |
|
|
|
|
| 2340 |
ggufs = [row for row in files if bool(row.get("is_gguf")) and "mmproj" not in str(row.get("path") or "").lower()]
|
| 2341 |
if not ggufs:
|
| 2342 |
return ""
|
| 2343 |
+
preferred = ("UD-Q4_K_M", "Q4_K_M", "UD-Q5_K_M", "Q5_K_M", "UD-Q4_K_S", "Q4_K_S", "Q5_0", "Q4_0", "Q8_0")
|
| 2344 |
|
| 2345 |
def _score(row: Dict[str, Any]) -> Tuple[int, int, str]:
|
| 2346 |
path = str(row.get("path") or "")
|
|
|
|
| 2393 |
lower_files = " ".join(files).lower()
|
| 2394 |
lower_library = str(_hf_browser_object_value(model, "library_name") or "").strip().lower()
|
| 2395 |
provider_haystack = f"{lower_id} {lower_library} {lower_tags} {lower_files}"
|
| 2396 |
+
card_data = _hf_browser_object_value(model, "cardData", None) or _hf_browser_object_value(model, "card_data", None)
|
| 2397 |
+
tater_pick = _hf_browser_tater_pick_metadata(model_id, tags=tags, card_data=card_data, files=files)
|
| 2398 |
supports_vision = _hf_browser_is_vision_model(model) or "mmproj" in lower_files
|
| 2399 |
if provider_token == HYDRA_LLM_PROVIDER_LLAMA_CPP:
|
| 2400 |
compatible = (
|
|
|
|
| 2406 |
or ".gguf" in lower_files
|
| 2407 |
)
|
| 2408 |
elif provider_token == HYDRA_LLM_PROVIDER_MLX_LM:
|
| 2409 |
+
compatible = (
|
| 2410 |
+
"mlx-lm" in provider_haystack
|
| 2411 |
+
or "mlx_lm" in provider_haystack
|
| 2412 |
+
or "mlx-vlm" in provider_haystack
|
| 2413 |
+
or "mlx_vlm" in provider_haystack
|
| 2414 |
+
or "mlx" in provider_haystack
|
| 2415 |
+
)
|
| 2416 |
else:
|
| 2417 |
compatible = ".gguf" not in lower_files
|
|
|
|
| 2418 |
model_size_label = _hf_browser_model_size_label(model_id, tags, files, model, card_data)
|
| 2419 |
+
summary = {
|
| 2420 |
"id": model_id,
|
| 2421 |
"author": str(_hf_browser_object_value(model, "author") or "").strip(),
|
| 2422 |
"model_size": model_size_label,
|
|
|
|
| 2437 |
"provider": provider_token,
|
| 2438 |
"provider_label": _hydra_llm_provider_label(provider_token),
|
| 2439 |
}
|
| 2440 |
+
if tater_pick:
|
| 2441 |
+
summary.update(
|
| 2442 |
+
{
|
| 2443 |
+
"tater_pick": True,
|
| 2444 |
+
"tater_pick_label": str(tater_pick.get("tater_pick_label") or "Tater Pick").strip(),
|
| 2445 |
+
"tater_pick_note": str(tater_pick.get("tater_pick_note") or "").strip(),
|
| 2446 |
+
"preferred_gguf": str(tater_pick.get("preferred_gguf") or "").strip(),
|
| 2447 |
+
"preferred_mmproj": str(tater_pick.get("preferred_mmproj") or "").strip(),
|
| 2448 |
+
}
|
| 2449 |
+
)
|
| 2450 |
+
return summary
|
| 2451 |
|
| 2452 |
|
| 2453 |
def _hf_browser_file_rows(info: Any, fallback_files: List[str]) -> List[Dict[str, Any]]:
|
|
|
|
| 2819 |
"downloaded_ts": float(latest_snapshot.stat().st_mtime if latest_snapshot.exists() else time.time()),
|
| 2820 |
"max_context_tokens": max_context,
|
| 2821 |
"context_source": context_source,
|
| 2822 |
+
"supports_vision": bool(
|
| 2823 |
+
provider_token != HYDRA_LLM_PROVIDER_MLX_LM
|
| 2824 |
+
and _local_llm_json_files_support_vision(latest_snapshot)
|
| 2825 |
+
),
|
| 2826 |
}
|
| 2827 |
)
|
| 2828 |
return rows
|
|
|
|
| 2864 |
supports_vision = bool(row.get("supports_vision"))
|
| 2865 |
mmproj_filename = str(row.get("mmproj_filename") or "").strip()
|
| 2866 |
mmproj_path = str(row.get("mmproj_path") or "").strip()
|
| 2867 |
+
if provider == HYDRA_LLM_PROVIDER_MLX_LM:
|
| 2868 |
+
supports_vision = False
|
| 2869 |
+
elif provider == HYDRA_LLM_PROVIDER_LLAMA_CPP:
|
| 2870 |
path_obj = Path(str(row.get("model_path") or "")).expanduser()
|
| 2871 |
if path_obj.exists():
|
| 2872 |
detected_mmproj = _local_llm_mmproj_for_gguf(path_obj)
|
|
|
|
| 2931 |
return {"models": models, "by_provider": by_provider, "updated_ts": time.time()}
|
| 2932 |
|
| 2933 |
|
| 2934 |
+
def _local_llm_chat_template_model_info(provider: str, model: str) -> Dict[str, Any]:
|
| 2935 |
+
provider_token = _normalize_hydra_llm_provider(provider)
|
| 2936 |
+
if not _is_local_hydra_llm_provider(provider_token):
|
| 2937 |
+
raise HTTPException(status_code=400, detail="Local LLM provider is required.")
|
| 2938 |
+
model_token = str(model or "").strip()
|
| 2939 |
+
if not model_token:
|
| 2940 |
+
raise HTTPException(status_code=400, detail="Local model is required.")
|
| 2941 |
+
payload = _local_llm_models_payload(provider=provider_token)
|
| 2942 |
+
row = next(
|
| 2943 |
+
(
|
| 2944 |
+
dict(item)
|
| 2945 |
+
for item in payload.get("models", [])
|
| 2946 |
+
if _normalize_hydra_llm_provider(item.get("provider")) == provider_token
|
| 2947 |
+
and str(item.get("model") or "").strip() == model_token
|
| 2948 |
+
),
|
| 2949 |
+
{},
|
| 2950 |
+
)
|
| 2951 |
+
model_path = str(row.get("model_path") or "").strip()
|
| 2952 |
+
info = get_local_llm_chat_template_info(provider_token, model_token, model_path=model_path)
|
| 2953 |
+
info["installed"] = bool(row)
|
| 2954 |
+
info["provider"] = provider_token
|
| 2955 |
+
info["provider_label"] = _hydra_llm_provider_label(provider_token)
|
| 2956 |
+
info["model_path"] = model_path or str(info.get("model_path") or "")
|
| 2957 |
+
return info
|
| 2958 |
+
|
| 2959 |
+
|
| 2960 |
+
def _llama_cpp_chat_template_model_info(model: str) -> Dict[str, Any]:
|
| 2961 |
+
return _local_llm_chat_template_model_info(HYDRA_LLM_PROVIDER_LLAMA_CPP, model)
|
| 2962 |
+
|
| 2963 |
+
|
| 2964 |
def _record_downloaded_local_llm_model(
|
| 2965 |
*,
|
| 2966 |
provider: str,
|
|
|
|
| 3916 |
return merged
|
| 3917 |
|
| 3918 |
|
| 3919 |
+
def _compact_chat_history_row(row: Dict[str, Any]) -> Tuple[Dict[str, Any], bool]:
|
| 3920 |
+
if not isinstance(row, dict):
|
| 3921 |
+
return row, False
|
| 3922 |
+
content = row.get("content")
|
| 3923 |
+
if not isinstance(content, dict):
|
| 3924 |
+
return row, False
|
| 3925 |
+
content_type = str(content.get("type") or "").strip().lower()
|
| 3926 |
+
mimetype_value = str(content.get("mimetype") or content.get("mime_type") or "").strip().lower()
|
| 3927 |
+
if content_type not in {"image", "audio", "video", "file"} and not mimetype_value.startswith(("image/", "audio/", "video/")):
|
| 3928 |
+
return row, False
|
| 3929 |
+
has_inline_payload = any(
|
| 3930 |
+
isinstance(content.get(key), (bytes, bytearray)) or (isinstance(content.get(key), str) and content.get(key).strip())
|
| 3931 |
+
for key in ("data", "bytes", "data_b64")
|
| 3932 |
+
)
|
| 3933 |
+
has_materializable_ref = any(str(content.get(key) or "").strip() for key in ("blob_key", "path", "file_path", "artifact_path"))
|
| 3934 |
+
if not has_inline_payload and not has_materializable_ref:
|
| 3935 |
+
return row, False
|
| 3936 |
+
normalized = _normalize_plugin_response_item(content)
|
| 3937 |
+
if not isinstance(normalized, dict) or normalized == content:
|
| 3938 |
+
return row, False
|
| 3939 |
+
compacted = dict(row)
|
| 3940 |
+
compacted["content"] = normalized
|
| 3941 |
+
return compacted, True
|
| 3942 |
+
|
| 3943 |
+
|
| 3944 |
def _load_chat_history_tail(count: int) -> List[Dict[str, Any]]:
|
| 3945 |
if count <= 0:
|
| 3946 |
return []
|
| 3947 |
+
try:
|
| 3948 |
+
list_len = int(redis_client.llen(CHAT_HISTORY_KEY) or 0)
|
| 3949 |
+
except Exception:
|
| 3950 |
+
list_len = 0
|
| 3951 |
+
start_index = max(0, list_len - int(count))
|
| 3952 |
raw = redis_client.lrange(CHAT_HISTORY_KEY, -count, -1)
|
| 3953 |
out: List[Dict[str, Any]] = []
|
| 3954 |
+
for offset, line in enumerate(raw):
|
| 3955 |
try:
|
| 3956 |
parsed = json.loads(line)
|
| 3957 |
if isinstance(parsed, dict):
|
| 3958 |
+
compacted, changed = _compact_chat_history_row(parsed)
|
| 3959 |
+
out.append(compacted)
|
| 3960 |
+
if changed and list_len > 0:
|
| 3961 |
+
try:
|
| 3962 |
+
redis_client.lset(CHAT_HISTORY_KEY, start_index + offset, json.dumps(compacted))
|
| 3963 |
+
except Exception:
|
| 3964 |
+
pass
|
| 3965 |
except Exception:
|
| 3966 |
continue
|
| 3967 |
return out
|
|
|
|
| 3970 |
def _load_chat_history() -> List[Dict[str, Any]]:
|
| 3971 |
raw = redis_client.lrange(CHAT_HISTORY_KEY, 0, -1)
|
| 3972 |
out: List[Dict[str, Any]] = []
|
| 3973 |
+
for idx, line in enumerate(raw):
|
| 3974 |
try:
|
| 3975 |
parsed = json.loads(line)
|
| 3976 |
if isinstance(parsed, dict):
|
| 3977 |
+
compacted, changed = _compact_chat_history_row(parsed)
|
| 3978 |
+
out.append(compacted)
|
| 3979 |
+
if changed:
|
| 3980 |
+
try:
|
| 3981 |
+
redis_client.lset(CHAT_HISTORY_KEY, idx, json.dumps(compacted))
|
| 3982 |
+
except Exception:
|
| 3983 |
+
pass
|
| 3984 |
except Exception:
|
| 3985 |
continue
|
| 3986 |
return out
|
|
|
|
| 4790 |
if not isinstance(item, dict):
|
| 4791 |
return item
|
| 4792 |
|
| 4793 |
+
raw_type = str(item.get("type") or "").strip().lower()
|
| 4794 |
+
name = str(item.get("name") or item.get("filename") or "attachment").strip() or "attachment"
|
| 4795 |
+
mimetype_value = str(item.get("mimetype") or item.get("mime_type") or "").strip().lower()
|
| 4796 |
+
if not mimetype_value:
|
| 4797 |
+
mimetype_value = str(mimetypes.guess_type(name)[0] or "").strip().lower() or "application/octet-stream"
|
| 4798 |
+
|
| 4799 |
+
inferred_type = _media_type_from_mimetype(mimetype_value)
|
| 4800 |
+
if raw_type in {"image", "audio", "video"}:
|
| 4801 |
+
media_type = raw_type
|
| 4802 |
+
elif raw_type == "file":
|
| 4803 |
+
media_type = inferred_type if inferred_type != "file" else "file"
|
| 4804 |
+
elif inferred_type in {"image", "audio", "video"}:
|
| 4805 |
+
media_type = inferred_type
|
| 4806 |
+
else:
|
| 4807 |
return item
|
| 4808 |
|
| 4809 |
raw = None
|
|
|
|
| 4811 |
raw = bytes(item.get("data"))
|
| 4812 |
elif isinstance(item.get("bytes"), (bytes, bytearray)):
|
| 4813 |
raw = bytes(item.get("bytes"))
|
| 4814 |
+
elif isinstance(item.get("data_b64"), str):
|
| 4815 |
+
try:
|
| 4816 |
+
raw = _decode_attachment_data(item.get("data_b64"))
|
| 4817 |
+
except Exception:
|
| 4818 |
+
raw = None
|
| 4819 |
+
elif isinstance(item.get("data"), str):
|
| 4820 |
+
try:
|
| 4821 |
+
raw = _decode_attachment_data(item.get("data"))
|
| 4822 |
+
except Exception:
|
| 4823 |
+
raw = None
|
| 4824 |
|
| 4825 |
if raw is None:
|
| 4826 |
+
blob_key = str(item.get("blob_key") or "").strip()
|
| 4827 |
+
if blob_key:
|
| 4828 |
+
try:
|
| 4829 |
+
blob = redis_blob_client.get(blob_key)
|
| 4830 |
+
if isinstance(blob, (bytes, bytearray)):
|
| 4831 |
+
raw = bytes(blob)
|
| 4832 |
+
except Exception:
|
| 4833 |
+
raw = None
|
| 4834 |
+
|
| 4835 |
+
if raw is None:
|
| 4836 |
+
path_value = str(item.get("path") or item.get("file_path") or item.get("artifact_path") or "").strip()
|
| 4837 |
+
if path_value:
|
| 4838 |
+
try:
|
| 4839 |
+
path = Path(path_value).expanduser()
|
| 4840 |
+
if path.is_file():
|
| 4841 |
+
raw = path.read_bytes()
|
| 4842 |
+
if name == "attachment":
|
| 4843 |
+
name = path.name or name
|
| 4844 |
+
if mimetype_value == "application/octet-stream":
|
| 4845 |
+
mimetype_value = str(mimetypes.guess_type(name)[0] or mimetype_value).strip().lower()
|
| 4846 |
+
inferred_type = _media_type_from_mimetype(mimetype_value)
|
| 4847 |
+
if raw_type in {"", "file"} and inferred_type != "file":
|
| 4848 |
+
media_type = inferred_type
|
| 4849 |
+
except Exception:
|
| 4850 |
+
raw = None
|
| 4851 |
|
| 4852 |
safe = dict(item)
|
| 4853 |
safe.pop("data", None)
|
| 4854 |
safe.pop("bytes", None)
|
| 4855 |
+
safe.pop("data_b64", None)
|
| 4856 |
+
safe["type"] = media_type
|
| 4857 |
+
safe["name"] = name
|
| 4858 |
+
safe["mimetype"] = mimetype_value
|
| 4859 |
+
if raw is not None:
|
| 4860 |
+
file_id = str(uuid.uuid4())
|
| 4861 |
+
_store_file_blob_in_redis(file_id, raw)
|
| 4862 |
+
safe["id"] = file_id
|
| 4863 |
+
safe["size"] = len(raw)
|
| 4864 |
+
safe.pop("blob_key", None)
|
| 4865 |
+
safe.pop("path", None)
|
| 4866 |
+
safe.pop("file_path", None)
|
| 4867 |
+
safe.pop("artifact_path", None)
|
| 4868 |
+
elif str(safe.get("file_id") or "").strip() and not str(safe.get("id") or "").strip():
|
| 4869 |
+
safe["id"] = str(safe.get("file_id") or "").strip()
|
| 4870 |
return safe
|
| 4871 |
|
| 4872 |
|
|
|
|
| 6406 |
model: Optional[str] = None
|
| 6407 |
|
| 6408 |
|
| 6409 |
+
class LlamaCppChatTemplateRequest(BaseModel):
|
| 6410 |
+
provider: Optional[str] = None
|
| 6411 |
+
model: Optional[str] = None
|
| 6412 |
+
template: Optional[str] = None
|
| 6413 |
+
reset: Optional[bool] = None
|
| 6414 |
+
|
| 6415 |
+
|
| 6416 |
class HfLlmWarmupCancelRequest(BaseModel):
|
| 6417 |
key: Optional[str] = None
|
| 6418 |
provider: Optional[str] = None
|
|
|
|
| 6497 |
hydra_llm_api_key: Optional[str] = None
|
| 6498 |
hydra_llm_provider: Optional[str] = None
|
| 6499 |
hydra_hf_transformers_context_tokens: Optional[Any] = None
|
| 6500 |
+
hydra_hf_transformers_device: Optional[str] = None
|
| 6501 |
+
hydra_hf_transformers_dtype: Optional[str] = None
|
| 6502 |
+
hydra_hf_transformers_device_map: Optional[str] = None
|
| 6503 |
+
hydra_hf_transformers_attn_implementation: Optional[str] = None
|
| 6504 |
+
hydra_hf_transformers_trust_remote_code: Optional[bool] = None
|
| 6505 |
hydra_llama_cpp_context_tokens: Optional[Any] = None
|
| 6506 |
hydra_llama_cpp_vision_context_tokens: Optional[Any] = None
|
| 6507 |
hydra_llama_cpp_mtp_enabled: Optional[bool] = None
|
| 6508 |
hydra_llama_cpp_mtp_draft_tokens: Optional[Any] = None
|
| 6509 |
+
hydra_llama_cpp_n_batch: Optional[Any] = None
|
| 6510 |
+
hydra_llama_cpp_n_ubatch: Optional[Any] = None
|
| 6511 |
+
hydra_llama_cpp_flash_attn: Optional[bool] = None
|
| 6512 |
+
hydra_llama_cpp_offload_kqv: Optional[bool] = None
|
| 6513 |
hydra_mlx_lm_context_tokens: Optional[Any] = None
|
| 6514 |
+
hydra_mlx_lm_trust_remote_code: Optional[bool] = None
|
| 6515 |
+
hydra_mlx_lm_lazy_load: Optional[bool] = None
|
| 6516 |
+
hydra_mlx_engine_prefill_step_size: Optional[Any] = None
|
| 6517 |
+
hydra_mlx_engine_kv_bits: Optional[Any] = None
|
| 6518 |
+
hydra_mlx_engine_kv_group_size: Optional[Any] = None
|
| 6519 |
+
hydra_mlx_engine_quantized_kv_start: Optional[Any] = None
|
| 6520 |
spudex_llm_provider: Optional[str] = None
|
| 6521 |
spudex_llm_host: Optional[str] = None
|
| 6522 |
spudex_llm_model: Optional[str] = None
|
|
|
|
| 10486 |
return {"ok": True, **payload}
|
| 10487 |
|
| 10488 |
|
| 10489 |
+
@app.get("/api/runtime/llm/debug")
|
| 10490 |
+
def runtime_llm_debug(since_id: int = 0, limit: int = 200) -> Dict[str, Any]:
|
| 10491 |
+
return get_llm_debug_runtime_snapshot(since_id=since_id, limit=limit)
|
| 10492 |
+
|
| 10493 |
+
|
| 10494 |
@app.post("/api/runtime/local-llm/unload")
|
| 10495 |
def unload_runtime_local_llm(payload: LocalLlmUnloadRequest) -> Dict[str, Any]:
|
| 10496 |
try:
|
|
|
|
| 12527 |
"hydra_llm_model": "",
|
| 12528 |
"hydra_llm_api_key": "",
|
| 12529 |
"hydra_hf_transformers_context_tokens": str(DEFAULT_HF_TRANSFORMERS_CONTEXT_TOKENS),
|
| 12530 |
+
"hydra_hf_transformers_device": DEFAULT_HF_TRANSFORMERS_DEVICE,
|
| 12531 |
+
"hydra_hf_transformers_dtype": DEFAULT_HF_TRANSFORMERS_DTYPE,
|
| 12532 |
+
"hydra_hf_transformers_device_map": DEFAULT_HF_TRANSFORMERS_DEVICE_MAP,
|
| 12533 |
+
"hydra_hf_transformers_attn_implementation": DEFAULT_HF_TRANSFORMERS_ATTN_IMPLEMENTATION,
|
| 12534 |
+
"hydra_hf_transformers_trust_remote_code": bool(DEFAULT_HF_TRANSFORMERS_TRUST_REMOTE_CODE),
|
| 12535 |
"hydra_llama_cpp_context_tokens": str(DEFAULT_LLAMA_CPP_CONTEXT_TOKENS),
|
| 12536 |
"hydra_llama_cpp_vision_context_tokens": str(DEFAULT_LLAMA_CPP_VISION_CONTEXT_TOKENS),
|
| 12537 |
"hydra_llama_cpp_mtp_enabled": bool(DEFAULT_LLAMA_CPP_MTP_ENABLED),
|
| 12538 |
"hydra_llama_cpp_mtp_draft_tokens": str(DEFAULT_LLAMA_CPP_MTP_DRAFT_TOKENS),
|
| 12539 |
+
"hydra_llama_cpp_n_batch": str(DEFAULT_LLAMA_CPP_N_BATCH),
|
| 12540 |
+
"hydra_llama_cpp_n_ubatch": "",
|
| 12541 |
+
"hydra_llama_cpp_flash_attn": bool(DEFAULT_LLAMA_CPP_FLASH_ATTN),
|
| 12542 |
+
"hydra_llama_cpp_offload_kqv": bool(DEFAULT_LLAMA_CPP_OFFLOAD_KQV),
|
| 12543 |
"hydra_mlx_lm_context_tokens": "",
|
| 12544 |
+
"hydra_mlx_lm_trust_remote_code": bool(DEFAULT_MLX_LM_TRUST_REMOTE_CODE),
|
| 12545 |
+
"hydra_mlx_lm_lazy_load": bool(DEFAULT_MLX_LM_LAZY_LOAD),
|
| 12546 |
+
"hydra_mlx_engine_prefill_step_size": "",
|
| 12547 |
+
"hydra_mlx_engine_kv_bits": "",
|
| 12548 |
+
"hydra_mlx_engine_kv_group_size": "",
|
| 12549 |
+
"hydra_mlx_engine_quantized_kv_start": "",
|
| 12550 |
"hydra_beast_mode_enabled": False,
|
| 12551 |
"hydra_max_ledger_items": int(DEFAULT_MAX_LEDGER_ITEMS),
|
| 12552 |
"hydra_astraeus_plan_review_enabled": bool(DEFAULT_ASTRAEUS_PLAN_REVIEW_ENABLED),
|
|
|
|
| 12653 |
("TATER_HF_TRANSFORMERS_MAX_INPUT_TOKENS",),
|
| 12654 |
DEFAULT_HF_TRANSFORMERS_CONTEXT_TOKENS,
|
| 12655 |
),
|
| 12656 |
+
"hydra_hf_transformers_device": _read_text_choice_setting(
|
| 12657 |
+
HYDRA_HF_TRANSFORMERS_DEVICE_KEY,
|
| 12658 |
+
("TATER_HF_TRANSFORMERS_DEVICE",),
|
| 12659 |
+
DEFAULT_HF_TRANSFORMERS_DEVICE,
|
| 12660 |
+
allowed=("auto", "cuda", "mps", "cpu"),
|
| 12661 |
+
),
|
| 12662 |
+
"hydra_hf_transformers_dtype": _read_text_choice_setting(
|
| 12663 |
+
HYDRA_HF_TRANSFORMERS_DTYPE_KEY,
|
| 12664 |
+
("TATER_HF_TRANSFORMERS_DTYPE",),
|
| 12665 |
+
DEFAULT_HF_TRANSFORMERS_DTYPE,
|
| 12666 |
+
allowed=("auto", "float16", "bfloat16", "float32"),
|
| 12667 |
+
),
|
| 12668 |
+
"hydra_hf_transformers_device_map": _read_text_choice_setting(
|
| 12669 |
+
HYDRA_HF_TRANSFORMERS_DEVICE_MAP_KEY,
|
| 12670 |
+
("TATER_HF_TRANSFORMERS_DEVICE_MAP",),
|
| 12671 |
+
DEFAULT_HF_TRANSFORMERS_DEVICE_MAP,
|
| 12672 |
+
allowed=("default", "disabled", "auto", "balanced"),
|
| 12673 |
+
),
|
| 12674 |
+
"hydra_hf_transformers_attn_implementation": _read_text_choice_setting(
|
| 12675 |
+
HYDRA_HF_TRANSFORMERS_ATTN_IMPLEMENTATION_KEY,
|
| 12676 |
+
("TATER_HF_TRANSFORMERS_ATTN_IMPLEMENTATION",),
|
| 12677 |
+
DEFAULT_HF_TRANSFORMERS_ATTN_IMPLEMENTATION,
|
| 12678 |
+
allowed=("", "auto", "sdpa", "flash_attention_2", "eager"),
|
| 12679 |
+
),
|
| 12680 |
+
"hydra_hf_transformers_trust_remote_code": _read_bool_setting(
|
| 12681 |
+
HYDRA_HF_TRANSFORMERS_TRUST_REMOTE_CODE_KEY,
|
| 12682 |
+
("TATER_HF_TRANSFORMERS_TRUST_REMOTE_CODE",),
|
| 12683 |
+
DEFAULT_HF_TRANSFORMERS_TRUST_REMOTE_CODE,
|
| 12684 |
+
),
|
| 12685 |
"hydra_llama_cpp_context_tokens": _read_local_llm_context_setting(
|
| 12686 |
HYDRA_LLAMA_CPP_CONTEXT_TOKENS_KEY,
|
| 12687 |
("TATER_LLAMA_CPP_N_CTX", "LLM_CONTEXT_SIZE"),
|
|
|
|
| 12704 |
minimum=1,
|
| 12705 |
maximum=16,
|
| 12706 |
),
|
| 12707 |
+
"hydra_llama_cpp_n_batch": _read_bounded_int_setting(
|
| 12708 |
+
HYDRA_LLAMA_CPP_N_BATCH_KEY,
|
| 12709 |
+
("TATER_LLAMA_CPP_N_BATCH",),
|
| 12710 |
+
DEFAULT_LLAMA_CPP_N_BATCH,
|
| 12711 |
+
minimum=32,
|
| 12712 |
+
maximum=8192,
|
| 12713 |
+
),
|
| 12714 |
+
"hydra_llama_cpp_n_ubatch": _read_bounded_int_setting(
|
| 12715 |
+
HYDRA_LLAMA_CPP_N_UBATCH_KEY,
|
| 12716 |
+
("TATER_LLAMA_CPP_N_UBATCH",),
|
| 12717 |
+
DEFAULT_LLAMA_CPP_N_UBATCH,
|
| 12718 |
+
minimum=0,
|
| 12719 |
+
maximum=8192,
|
| 12720 |
+
),
|
| 12721 |
+
"hydra_llama_cpp_flash_attn": _read_bool_setting(
|
| 12722 |
+
HYDRA_LLAMA_CPP_FLASH_ATTN_KEY,
|
| 12723 |
+
("TATER_LLAMA_CPP_FLASH_ATTN",),
|
| 12724 |
+
DEFAULT_LLAMA_CPP_FLASH_ATTN,
|
| 12725 |
+
),
|
| 12726 |
+
"hydra_llama_cpp_offload_kqv": _read_bool_setting(
|
| 12727 |
+
HYDRA_LLAMA_CPP_OFFLOAD_KQV_KEY,
|
| 12728 |
+
("TATER_LLAMA_CPP_OFFLOAD_KQV",),
|
| 12729 |
+
DEFAULT_LLAMA_CPP_OFFLOAD_KQV,
|
| 12730 |
+
),
|
| 12731 |
"hydra_mlx_lm_context_tokens": _read_local_llm_context_setting(
|
| 12732 |
HYDRA_MLX_LM_CONTEXT_TOKENS_KEY,
|
| 12733 |
("TATER_MLX_LM_MAX_KV_SIZE",),
|
| 12734 |
None,
|
| 12735 |
minimum=128,
|
| 12736 |
),
|
| 12737 |
+
"hydra_mlx_lm_trust_remote_code": _read_bool_setting(
|
| 12738 |
+
HYDRA_MLX_LM_TRUST_REMOTE_CODE_KEY,
|
| 12739 |
+
("TATER_MLX_LM_TRUST_REMOTE_CODE",),
|
| 12740 |
+
DEFAULT_MLX_LM_TRUST_REMOTE_CODE,
|
| 12741 |
+
),
|
| 12742 |
+
"hydra_mlx_lm_lazy_load": _read_bool_setting(
|
| 12743 |
+
HYDRA_MLX_LM_LAZY_LOAD_KEY,
|
| 12744 |
+
("TATER_MLX_LM_LAZY",),
|
| 12745 |
+
DEFAULT_MLX_LM_LAZY_LOAD,
|
| 12746 |
+
),
|
| 12747 |
+
"hydra_mlx_engine_prefill_step_size": _read_optional_int_setting(
|
| 12748 |
+
HYDRA_MLX_ENGINE_PREFILL_STEP_SIZE_KEY,
|
| 12749 |
+
("TATER_MLX_ENGINE_PREFILL_STEP_SIZE",),
|
| 12750 |
+
minimum=1,
|
| 12751 |
+
maximum=32768,
|
| 12752 |
+
),
|
| 12753 |
+
"hydra_mlx_engine_kv_bits": _read_text_choice_setting(
|
| 12754 |
+
HYDRA_MLX_ENGINE_KV_BITS_KEY,
|
| 12755 |
+
("TATER_MLX_ENGINE_KV_BITS",),
|
| 12756 |
+
"",
|
| 12757 |
+
allowed=("", "2", "3", "4", "6", "8"),
|
| 12758 |
+
),
|
| 12759 |
+
"hydra_mlx_engine_kv_group_size": _read_text_choice_setting(
|
| 12760 |
+
HYDRA_MLX_ENGINE_KV_GROUP_SIZE_KEY,
|
| 12761 |
+
("TATER_MLX_ENGINE_KV_GROUP_SIZE",),
|
| 12762 |
+
"",
|
| 12763 |
+
allowed=("", "32", "64", "128"),
|
| 12764 |
+
),
|
| 12765 |
+
"hydra_mlx_engine_quantized_kv_start": _read_optional_int_setting(
|
| 12766 |
+
HYDRA_MLX_ENGINE_QUANTIZED_KV_START_KEY,
|
| 12767 |
+
("TATER_MLX_ENGINE_QUANTIZED_KV_START",),
|
| 12768 |
+
minimum=0,
|
| 12769 |
+
maximum=1_048_576,
|
| 12770 |
+
allow_zero=True,
|
| 12771 |
+
),
|
| 12772 |
"spudex_llm_provider": _normalize_hydra_llm_provider(spudex_settings.get("llm_provider") or ""),
|
| 12773 |
"spudex_llm_host": str(spudex_settings.get("llm_host") or ""),
|
| 12774 |
"spudex_llm_model": str(spudex_settings.get("llm_model") or ""),
|
|
|
|
| 13616 |
return get_llama_cpp_runtime_diagnostics()
|
| 13617 |
|
| 13618 |
|
| 13619 |
+
@app.get("/api/settings/llama-cpp/chat-template")
|
| 13620 |
+
def get_llama_cpp_chat_template(model: str) -> Dict[str, Any]:
|
| 13621 |
+
return _llama_cpp_chat_template_model_info(model)
|
| 13622 |
+
|
| 13623 |
+
|
| 13624 |
+
@app.post("/api/settings/llama-cpp/chat-template")
|
| 13625 |
+
def save_llama_cpp_chat_template(request: LlamaCppChatTemplateRequest) -> Dict[str, Any]:
|
| 13626 |
+
model = str(request.model or "").strip()
|
| 13627 |
+
if not model:
|
| 13628 |
+
raise HTTPException(status_code=400, detail="llama.cpp model is required.")
|
| 13629 |
+
try:
|
| 13630 |
+
if request.reset:
|
| 13631 |
+
clear_llama_cpp_chat_template_override(model)
|
| 13632 |
+
else:
|
| 13633 |
+
set_llama_cpp_chat_template_override(model, request.template or "")
|
| 13634 |
+
except ValueError as exc:
|
| 13635 |
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
| 13636 |
+
unload_result = unload_local_llm_models(provider=HYDRA_LLM_PROVIDER_LLAMA_CPP, model=model)
|
| 13637 |
+
info = _llama_cpp_chat_template_model_info(model)
|
| 13638 |
+
info["ok"] = True
|
| 13639 |
+
info["unload"] = unload_result
|
| 13640 |
+
return info
|
| 13641 |
+
|
| 13642 |
+
|
| 13643 |
+
@app.get("/api/settings/local-llm/chat-template")
|
| 13644 |
+
def get_local_llm_chat_template(provider: str = "", model: str = "") -> Dict[str, Any]:
|
| 13645 |
+
return _local_llm_chat_template_model_info(provider, model)
|
| 13646 |
+
|
| 13647 |
+
|
| 13648 |
+
@app.post("/api/settings/local-llm/chat-template")
|
| 13649 |
+
def save_local_llm_chat_template(request: LlamaCppChatTemplateRequest) -> Dict[str, Any]:
|
| 13650 |
+
provider = _normalize_hydra_llm_provider(request.provider)
|
| 13651 |
+
if not _is_local_hydra_llm_provider(provider):
|
| 13652 |
+
raise HTTPException(status_code=400, detail="Local LLM provider is required.")
|
| 13653 |
+
model = str(request.model or "").strip()
|
| 13654 |
+
if not model:
|
| 13655 |
+
raise HTTPException(status_code=400, detail="Local model is required.")
|
| 13656 |
+
try:
|
| 13657 |
+
if request.reset:
|
| 13658 |
+
clear_local_llm_chat_template_override(provider, model)
|
| 13659 |
+
else:
|
| 13660 |
+
set_local_llm_chat_template_override(provider, model, request.template or "")
|
| 13661 |
+
except ValueError as exc:
|
| 13662 |
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
| 13663 |
+
unload_result = unload_local_llm_models(provider=provider, model=model)
|
| 13664 |
+
info = _local_llm_chat_template_model_info(provider, model)
|
| 13665 |
+
info["ok"] = True
|
| 13666 |
+
info["unload"] = unload_result
|
| 13667 |
+
return info
|
| 13668 |
+
|
| 13669 |
+
|
| 13670 |
@app.get("/api/settings/huggingface/models")
|
| 13671 |
def get_huggingface_models(
|
| 13672 |
provider: str = "hf_transformers",
|
|
|
|
| 13681 |
provider_token = HYDRA_LLM_PROVIDER_HF_TRANSFORMERS
|
| 13682 |
view_token = str(view or "trending").strip().lower().replace("_", "-")
|
| 13683 |
task_token = _normalize_hf_browser_task(task)
|
| 13684 |
+
clean_limit = max(4, min(48, int(limit or 24)))
|
| 13685 |
+
search = _hf_browser_provider_search(provider_token, query, task_token)
|
| 13686 |
+
integration_status = _hf_browser_integration_status()
|
| 13687 |
+
if view_token in {"picks", "tater", "tater-picks", "recommended"}:
|
| 13688 |
+
models = _hf_browser_tater_pick_models(query=search, task=task_token, limit=clean_limit, provider=provider_token)
|
| 13689 |
+
return {
|
| 13690 |
+
"provider": provider_token,
|
| 13691 |
+
"provider_label": _hydra_llm_provider_label(provider_token),
|
| 13692 |
+
"view": "picks",
|
| 13693 |
+
"query": search,
|
| 13694 |
+
"task": task_token,
|
| 13695 |
+
"library": "",
|
| 13696 |
+
"app_filter": "",
|
| 13697 |
+
"integration": integration_status,
|
| 13698 |
+
"limit": clean_limit,
|
| 13699 |
+
"has_next": False,
|
| 13700 |
+
"next_cursor": "",
|
| 13701 |
+
"models": models,
|
| 13702 |
+
}
|
| 13703 |
if view_token in {"new", "recent", "latest"}:
|
| 13704 |
sort = "lastModified"
|
| 13705 |
response_view = "new"
|
|
|
|
| 13709 |
else:
|
| 13710 |
sort = "trendingScore"
|
| 13711 |
response_view = "trending"
|
|
|
|
|
|
|
|
|
|
| 13712 |
page_url = _hf_browser_cursor_decode(cursor) if str(cursor or "").strip() else _hf_browser_models_api_url(
|
| 13713 |
provider=provider_token,
|
| 13714 |
search=search,
|
|
|
|
| 13747 |
"query": search,
|
| 13748 |
"task": task_token,
|
| 13749 |
"library": _hf_browser_provider_library(provider_token),
|
| 13750 |
+
"app_filter": _hf_browser_provider_app_filter(provider_token, task_token),
|
| 13751 |
"integration": integration_status,
|
| 13752 |
"limit": clean_limit,
|
| 13753 |
"has_next": bool(next_cursor),
|
|
|
|
| 14003 |
)
|
| 14004 |
redis_client.set(redis_key, str(int(parsed)))
|
| 14005 |
|
| 14006 |
+
def _save_text_choice_setting(
|
| 14007 |
+
payload_key: str,
|
| 14008 |
+
redis_key: str,
|
| 14009 |
+
*,
|
| 14010 |
+
allowed: Tuple[str, ...],
|
| 14011 |
+
default: str = "",
|
| 14012 |
+
) -> None:
|
| 14013 |
+
if payload_key not in updates:
|
| 14014 |
+
return
|
| 14015 |
+
token = str(updates.get(payload_key) or default or "").strip().lower()
|
| 14016 |
+
aliases = {
|
| 14017 |
+
"fp16": "float16",
|
| 14018 |
+
"half": "float16",
|
| 14019 |
+
"bf16": "bfloat16",
|
| 14020 |
+
"fp32": "float32",
|
| 14021 |
+
"none": "disabled",
|
| 14022 |
+
"off": "disabled",
|
| 14023 |
+
"false": "disabled",
|
| 14024 |
+
"0": "disabled",
|
| 14025 |
+
"default": "default",
|
| 14026 |
+
"auto": "auto",
|
| 14027 |
+
}
|
| 14028 |
+
token = aliases.get(token, token)
|
| 14029 |
+
allowed_set = {str(item).strip().lower() for item in allowed}
|
| 14030 |
+
if token not in allowed_set:
|
| 14031 |
+
raise HTTPException(status_code=400, detail=f"{payload_key} has an unsupported value.")
|
| 14032 |
+
redis_client.set(redis_key, token)
|
| 14033 |
+
|
| 14034 |
+
def _save_optional_int_setting(
|
| 14035 |
+
payload_key: str,
|
| 14036 |
+
redis_key: str,
|
| 14037 |
+
*,
|
| 14038 |
+
min_value: int = 0,
|
| 14039 |
+
max_value: int = 1_048_576,
|
| 14040 |
+
allow_zero: bool = False,
|
| 14041 |
+
) -> None:
|
| 14042 |
+
if payload_key not in updates:
|
| 14043 |
+
return
|
| 14044 |
+
raw = str(updates.get(payload_key) or "").strip()
|
| 14045 |
+
if not raw:
|
| 14046 |
+
redis_client.delete(redis_key)
|
| 14047 |
+
return
|
| 14048 |
+
try:
|
| 14049 |
+
parsed = int(float(raw))
|
| 14050 |
+
except Exception as exc:
|
| 14051 |
+
raise HTTPException(status_code=400, detail=f"{payload_key} must be a whole number.") from exc
|
| 14052 |
+
if parsed < min_value or parsed > max_value or (parsed == 0 and not allow_zero):
|
| 14053 |
+
lower = min_value if allow_zero or min_value > 0 else 1
|
| 14054 |
+
raise HTTPException(
|
| 14055 |
+
status_code=400,
|
| 14056 |
+
detail=f"{payload_key} must be between {lower} and {max_value}.",
|
| 14057 |
+
)
|
| 14058 |
+
redis_client.set(redis_key, str(int(parsed)))
|
| 14059 |
+
|
| 14060 |
_save_tater_api_settings_from_updates(updates)
|
| 14061 |
|
| 14062 |
local_model_keys_cache: Optional[set[Tuple[str, str]]] = None
|
|
|
|
| 14121 |
"hydra_llm_provider",
|
| 14122 |
"hydra_base_servers",
|
| 14123 |
"hydra_hf_transformers_context_tokens",
|
| 14124 |
+
"hydra_hf_transformers_device",
|
| 14125 |
+
"hydra_hf_transformers_dtype",
|
| 14126 |
+
"hydra_hf_transformers_device_map",
|
| 14127 |
+
"hydra_hf_transformers_attn_implementation",
|
| 14128 |
+
"hydra_hf_transformers_trust_remote_code",
|
| 14129 |
"hydra_llama_cpp_context_tokens",
|
| 14130 |
"hydra_llama_cpp_mtp_enabled",
|
| 14131 |
"hydra_llama_cpp_mtp_draft_tokens",
|
| 14132 |
+
"hydra_llama_cpp_n_batch",
|
| 14133 |
+
"hydra_llama_cpp_n_ubatch",
|
| 14134 |
+
"hydra_llama_cpp_flash_attn",
|
| 14135 |
+
"hydra_llama_cpp_offload_kqv",
|
| 14136 |
"hydra_mlx_lm_context_tokens",
|
| 14137 |
+
"hydra_mlx_lm_trust_remote_code",
|
| 14138 |
+
"hydra_mlx_lm_lazy_load",
|
| 14139 |
+
"hydra_mlx_engine_prefill_step_size",
|
| 14140 |
+
"hydra_mlx_engine_kv_bits",
|
| 14141 |
+
"hydra_mlx_engine_kv_group_size",
|
| 14142 |
+
"hydra_mlx_engine_quantized_kv_start",
|
| 14143 |
}
|
| 14144 |
spudex_model_keys = {"spudex_llm_provider", "spudex_llm_host", "spudex_llm_model"}
|
| 14145 |
vision_model_keys = {
|
|
|
|
| 14230 |
"hydra_hf_transformers_context_tokens",
|
| 14231 |
HYDRA_HF_TRANSFORMERS_CONTEXT_TOKENS_KEY,
|
| 14232 |
)
|
| 14233 |
+
_save_text_choice_setting(
|
| 14234 |
+
"hydra_hf_transformers_device",
|
| 14235 |
+
HYDRA_HF_TRANSFORMERS_DEVICE_KEY,
|
| 14236 |
+
allowed=("auto", "cuda", "mps", "cpu"),
|
| 14237 |
+
default=DEFAULT_HF_TRANSFORMERS_DEVICE,
|
| 14238 |
+
)
|
| 14239 |
+
_save_text_choice_setting(
|
| 14240 |
+
"hydra_hf_transformers_dtype",
|
| 14241 |
+
HYDRA_HF_TRANSFORMERS_DTYPE_KEY,
|
| 14242 |
+
allowed=("auto", "float16", "bfloat16", "float32"),
|
| 14243 |
+
default=DEFAULT_HF_TRANSFORMERS_DTYPE,
|
| 14244 |
+
)
|
| 14245 |
+
_save_text_choice_setting(
|
| 14246 |
+
"hydra_hf_transformers_device_map",
|
| 14247 |
+
HYDRA_HF_TRANSFORMERS_DEVICE_MAP_KEY,
|
| 14248 |
+
allowed=("default", "disabled", "auto", "balanced"),
|
| 14249 |
+
default=DEFAULT_HF_TRANSFORMERS_DEVICE_MAP,
|
| 14250 |
+
)
|
| 14251 |
+
_save_text_choice_setting(
|
| 14252 |
+
"hydra_hf_transformers_attn_implementation",
|
| 14253 |
+
HYDRA_HF_TRANSFORMERS_ATTN_IMPLEMENTATION_KEY,
|
| 14254 |
+
allowed=("", "auto", "sdpa", "flash_attention_2", "eager"),
|
| 14255 |
+
default=DEFAULT_HF_TRANSFORMERS_ATTN_IMPLEMENTATION,
|
| 14256 |
+
)
|
| 14257 |
+
_save_bool_setting(
|
| 14258 |
+
"hydra_hf_transformers_trust_remote_code",
|
| 14259 |
+
HYDRA_HF_TRANSFORMERS_TRUST_REMOTE_CODE_KEY,
|
| 14260 |
+
default=DEFAULT_HF_TRANSFORMERS_TRUST_REMOTE_CODE,
|
| 14261 |
+
)
|
| 14262 |
_save_local_llm_context_setting(
|
| 14263 |
"hydra_llama_cpp_context_tokens",
|
| 14264 |
HYDRA_LLAMA_CPP_CONTEXT_TOKENS_KEY,
|
|
|
|
| 14279 |
min_value=1,
|
| 14280 |
max_value=16,
|
| 14281 |
)
|
| 14282 |
+
_save_bounded_int_setting(
|
| 14283 |
+
"hydra_llama_cpp_n_batch",
|
| 14284 |
+
HYDRA_LLAMA_CPP_N_BATCH_KEY,
|
| 14285 |
+
default=DEFAULT_LLAMA_CPP_N_BATCH,
|
| 14286 |
+
min_value=32,
|
| 14287 |
+
max_value=8192,
|
| 14288 |
+
)
|
| 14289 |
+
_save_bounded_int_setting(
|
| 14290 |
+
"hydra_llama_cpp_n_ubatch",
|
| 14291 |
+
HYDRA_LLAMA_CPP_N_UBATCH_KEY,
|
| 14292 |
+
default=DEFAULT_LLAMA_CPP_N_UBATCH,
|
| 14293 |
+
min_value=0,
|
| 14294 |
+
max_value=8192,
|
| 14295 |
+
)
|
| 14296 |
+
_save_bool_setting(
|
| 14297 |
+
"hydra_llama_cpp_flash_attn",
|
| 14298 |
+
HYDRA_LLAMA_CPP_FLASH_ATTN_KEY,
|
| 14299 |
+
default=DEFAULT_LLAMA_CPP_FLASH_ATTN,
|
| 14300 |
+
)
|
| 14301 |
+
_save_bool_setting(
|
| 14302 |
+
"hydra_llama_cpp_offload_kqv",
|
| 14303 |
+
HYDRA_LLAMA_CPP_OFFLOAD_KQV_KEY,
|
| 14304 |
+
default=DEFAULT_LLAMA_CPP_OFFLOAD_KQV,
|
| 14305 |
+
)
|
| 14306 |
_save_local_llm_context_setting(
|
| 14307 |
"hydra_mlx_lm_context_tokens",
|
| 14308 |
HYDRA_MLX_LM_CONTEXT_TOKENS_KEY,
|
| 14309 |
min_value=128,
|
| 14310 |
)
|
| 14311 |
+
_save_bool_setting(
|
| 14312 |
+
"hydra_mlx_lm_trust_remote_code",
|
| 14313 |
+
HYDRA_MLX_LM_TRUST_REMOTE_CODE_KEY,
|
| 14314 |
+
default=DEFAULT_MLX_LM_TRUST_REMOTE_CODE,
|
| 14315 |
+
)
|
| 14316 |
+
_save_bool_setting(
|
| 14317 |
+
"hydra_mlx_lm_lazy_load",
|
| 14318 |
+
HYDRA_MLX_LM_LAZY_LOAD_KEY,
|
| 14319 |
+
default=DEFAULT_MLX_LM_LAZY_LOAD,
|
| 14320 |
+
)
|
| 14321 |
+
_save_optional_int_setting(
|
| 14322 |
+
"hydra_mlx_engine_prefill_step_size",
|
| 14323 |
+
HYDRA_MLX_ENGINE_PREFILL_STEP_SIZE_KEY,
|
| 14324 |
+
min_value=1,
|
| 14325 |
+
max_value=32768,
|
| 14326 |
+
)
|
| 14327 |
+
_save_text_choice_setting(
|
| 14328 |
+
"hydra_mlx_engine_kv_bits",
|
| 14329 |
+
HYDRA_MLX_ENGINE_KV_BITS_KEY,
|
| 14330 |
+
allowed=("", "2", "3", "4", "6", "8"),
|
| 14331 |
+
default="",
|
| 14332 |
+
)
|
| 14333 |
+
_save_text_choice_setting(
|
| 14334 |
+
"hydra_mlx_engine_kv_group_size",
|
| 14335 |
+
HYDRA_MLX_ENGINE_KV_GROUP_SIZE_KEY,
|
| 14336 |
+
allowed=("", "32", "64", "128"),
|
| 14337 |
+
default="",
|
| 14338 |
+
)
|
| 14339 |
+
_save_optional_int_setting(
|
| 14340 |
+
"hydra_mlx_engine_quantized_kv_start",
|
| 14341 |
+
HYDRA_MLX_ENGINE_QUANTIZED_KV_START_KEY,
|
| 14342 |
+
min_value=0,
|
| 14343 |
+
max_value=1_048_576,
|
| 14344 |
+
allow_zero=True,
|
| 14345 |
+
)
|
| 14346 |
|
| 14347 |
username = updates.get("username")
|
| 14348 |
if isinstance(username, str):
|
|
|
|
| 14506 |
updates.get("vision_provider", current_vision.get("provider") or HYDRA_LLM_PROVIDER_OPENAI_COMPATIBLE)
|
| 14507 |
)
|
| 14508 |
vision_model = str(updates.get("vision_model", current_vision.get("model") or "")).strip()
|
|
|
|
|
|
|
| 14509 |
if vision_mode == "dedicated" and _is_local_hydra_llm_provider(vision_provider):
|
| 14510 |
_require_downloaded_local_model(vision_provider, vision_model, "Vision")
|
| 14511 |
save_shared_vision_settings(
|
tater_reachy_standalone/vendor/tater_app/tateros_static/app.js
CHANGED
|
@@ -127,6 +127,12 @@ const state = {
|
|
| 127 |
dashboardRefreshStatus: "",
|
| 128 |
hfLlmWarmupPollTimer: 0,
|
| 129 |
hfLlmWarmupLastSnapshot: null,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
runtimeSettingsSaveHandler: null,
|
| 131 |
runtimeSettingsOpenHandler: null,
|
| 132 |
runtimeSettingsCloseHandler: null,
|
|
@@ -178,6 +184,14 @@ const state = {
|
|
| 178 |
|
| 179 |
safeStorageSet("tater_tateros_session_id", state.sessionId);
|
| 180 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
const APP_BASE_PATH = (() => {
|
| 182 |
const rawPath = String(window.location.pathname || "/").trim();
|
| 183 |
const normalized = rawPath.replace(/\/+$/, "");
|
|
@@ -1016,6 +1030,35 @@ function finishActionProgress(tone = "success", statusText = "Completed.") {
|
|
| 1016 |
setActionProgress(100, statusText, tone);
|
| 1017 |
}
|
| 1018 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1019 |
function closeActionProgressModal() {
|
| 1020 |
const modal = document.getElementById("action-progress-modal");
|
| 1021 |
if (!modal) {
|
|
@@ -1058,6 +1101,220 @@ async function runActionWithProgress(meta, actionFn) {
|
|
| 1058 |
}
|
| 1059 |
}
|
| 1060 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1061 |
async function api(path, options = {}) {
|
| 1062 |
const requestOptions = options && typeof options === "object" ? options : {};
|
| 1063 |
const skipRedisRecovery = Boolean(requestOptions._skipRedisRecovery);
|
|
@@ -4248,6 +4505,14 @@ function _sanitizeMarkdownHref(rawHref) {
|
|
| 4248 |
return "";
|
| 4249 |
}
|
| 4250 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4251 |
function _renderMarkdownInline(rawText) {
|
| 4252 |
const source = String(rawText ?? "");
|
| 4253 |
if (!source) {
|
|
@@ -4447,17 +4712,19 @@ function renderChatMessage(message) {
|
|
| 4447 |
`;
|
| 4448 |
} else {
|
| 4449 |
const contentType = String(content.type || "").toLowerCase();
|
| 4450 |
-
const fileId = String(content.id || "").trim();
|
| 4451 |
const fileName = String(content.name || "attachment").trim() || "attachment";
|
| 4452 |
const mimetype = String(content.mimetype || "").trim() || "application/octet-stream";
|
| 4453 |
const fileUrl = _chatFileUrl(fileId, mimetype);
|
|
|
|
|
|
|
| 4454 |
const hasDataB64 = typeof content.data_b64 === "string" && content.data_b64.trim().length > 0;
|
| 4455 |
-
const
|
| 4456 |
if (contentType === "image" && hasDataB64) {
|
| 4457 |
const imageUrl = `data:${escapeHtml(content.mimetype || "image/png")};base64,${content.data_b64}`;
|
| 4458 |
bodyHtml = `<img class="chat-media-image" src="${imageUrl}" alt="${escapeHtml(content.name || "image")}" />`;
|
| 4459 |
-
} else if (contentType === "image" &&
|
| 4460 |
-
bodyHtml = `<img class="chat-media-image" src="${
|
| 4461 |
} else if (contentType === "audio" && hasDataB64) {
|
| 4462 |
const mimetype = String(content.mimetype || "audio/mpeg");
|
| 4463 |
const audioUrl = `data:${escapeHtml(mimetype)};base64,${content.data_b64}`;
|
|
@@ -4468,12 +4735,12 @@ function renderChatMessage(message) {
|
|
| 4468 |
<a class="inline-btn" href="${audioUrl}" download="${escapeHtml(fileName)}">Download Audio</a>
|
| 4469 |
</div>
|
| 4470 |
`;
|
| 4471 |
-
} else if (contentType === "audio" &&
|
| 4472 |
bodyHtml = `
|
| 4473 |
<div class="chat-media-wrap">
|
| 4474 |
-
<audio controls preload="metadata" src="${
|
| 4475 |
<div class="chat-file-meta">${escapeHtml(fileName)}</div>
|
| 4476 |
-
<a class="inline-btn" href="${
|
| 4477 |
</div>
|
| 4478 |
`;
|
| 4479 |
} else if (contentType === "video" && hasDataB64) {
|
|
@@ -4486,12 +4753,12 @@ function renderChatMessage(message) {
|
|
| 4486 |
<a class="inline-btn" href="${videoUrl}" download="${escapeHtml(fileName)}">Download Video</a>
|
| 4487 |
</div>
|
| 4488 |
`;
|
| 4489 |
-
} else if (contentType === "video" &&
|
| 4490 |
bodyHtml = `
|
| 4491 |
<div class="chat-media-wrap">
|
| 4492 |
-
<video controls preload="metadata" src="${
|
| 4493 |
<div class="chat-file-meta">${escapeHtml(fileName)}</div>
|
| 4494 |
-
<a class="inline-btn" href="${
|
| 4495 |
</div>
|
| 4496 |
`;
|
| 4497 |
} else if (contentType === "file" && hasDataB64) {
|
|
@@ -4504,12 +4771,12 @@ function renderChatMessage(message) {
|
|
| 4504 |
)}">Download File</a>
|
| 4505 |
</div>
|
| 4506 |
`;
|
| 4507 |
-
} else if (contentType === "file" &&
|
| 4508 |
const sizeLabel = _formatBytes(content.size);
|
| 4509 |
bodyHtml = `
|
| 4510 |
<div class="chat-file-card">
|
| 4511 |
<div class="chat-file-meta">${escapeHtml(fileName)}${sizeLabel ? ` (${escapeHtml(sizeLabel)})` : ""}</div>
|
| 4512 |
-
<a class="inline-btn" href="${
|
| 4513 |
</div>
|
| 4514 |
`;
|
| 4515 |
} else {
|
|
@@ -8193,6 +8460,9 @@ function bindModelSettingsTabs(root = document) {
|
|
| 8193 |
if (load && ["speakerid", "emotionid"].includes(normalized)) {
|
| 8194 |
void ensureEspHomeRuntimeLoaded({ force: true, panel: normalized });
|
| 8195 |
}
|
|
|
|
|
|
|
|
|
|
| 8196 |
};
|
| 8197 |
buttons.forEach((button) => {
|
| 8198 |
if (button.dataset.modelsTabBound === "1") {
|
|
@@ -15885,6 +16155,16 @@ async function loadSettingsView() {
|
|
| 15885 |
.map((option) => `<option value="${escapeHtml(option.value)}"${option.value === selected ? " selected" : ""}>${escapeHtml(option.label)}</option>`)
|
| 15886 |
.join("");
|
| 15887 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15888 |
const renderHydraLocalProviderOptions = (currentValue) => {
|
| 15889 |
const selected = normalizeHydraBaseProvider(currentValue);
|
| 15890 |
return hydraProviderOptions
|
|
@@ -16537,6 +16817,7 @@ async function loadSettingsView() {
|
|
| 16537 |
<button type="button" class="settings-subtab-btn active" data-llm-vision-tab="settings">Settings</button>
|
| 16538 |
<button type="button" class="settings-subtab-btn" data-llm-vision-tab="huggingface">Hugging Face</button>
|
| 16539 |
<button type="button" class="settings-subtab-btn" data-llm-vision-tab="manage">Manage</button>
|
|
|
|
| 16540 |
</div>
|
| 16541 |
<div id="settings-hf-model-browser" class="hf-model-browser" hidden>
|
| 16542 |
<div class="hf-model-browser-hero">
|
|
@@ -16560,7 +16841,8 @@ async function loadSettingsView() {
|
|
| 16560 |
<option value="image-text-to-text">Vision</option>
|
| 16561 |
</select>
|
| 16562 |
<div class="hf-model-browser-view-switch" role="group" aria-label="Model list">
|
| 16563 |
-
<button type="button" class="active" data-hf-model-view="
|
|
|
|
| 16564 |
<button type="button" data-hf-model-view="new">New</button>
|
| 16565 |
<button type="button" data-hf-model-view="downloads">Most Downloaded</button>
|
| 16566 |
</div>
|
|
@@ -16611,6 +16893,27 @@ async function loadSettingsView() {
|
|
| 16611 |
<div id="local-model-manager-status" class="small hf-model-browser-status"></div>
|
| 16612 |
<div id="local-model-manager-list" class="local-model-manager-list"></div>
|
| 16613 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16614 |
<div id="settings-hydra-base-fields" class="hydra-model-panel is-active llm-vision-settings-block">
|
| 16615 |
<div class="hydra-model-panel-title">Base Model</div>
|
| 16616 |
<div class="small hydra-model-panel-note">Used for regular AI calls. Multiple base servers rotate in round-robin order.</div>
|
|
@@ -16636,6 +16939,10 @@ async function loadSettingsView() {
|
|
| 16636 |
<select id="set_hydra_llm_model_select"></select>
|
| 16637 |
<div id="hydra-local-model-status" class="small"></div>
|
| 16638 |
</label>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16639 |
<label class="hydra-context-field" data-hydra-provider-field="hf_transformers">Context Length
|
| 16640 |
<div class="hydra-context-control" data-hydra-context-control="hf_transformers">
|
| 16641 |
<input id="set_hydra_hf_transformers_context_tokens_range" type="range" min="256" max="262144" step="256" value="${escapeHtml(
|
|
@@ -16648,6 +16955,71 @@ async function loadSettingsView() {
|
|
| 16648 |
<div id="hydra-hf-context-hint" class="small hydra-context-hint"></div>
|
| 16649 |
<div id="hydra-hf-context-estimate" class="hydra-context-estimate" data-hydra-context-estimate="hf_transformers"></div>
|
| 16650 |
</label>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16651 |
<label class="hydra-context-field" data-hydra-provider-field="llama_cpp">Context Length
|
| 16652 |
<div class="hydra-context-control" data-hydra-context-control="llama_cpp">
|
| 16653 |
<input id="set_hydra_llama_cpp_context_tokens_range" type="range" min="256" max="262144" step="256" value="${escapeHtml(
|
|
@@ -16660,6 +17032,46 @@ async function loadSettingsView() {
|
|
| 16660 |
<div id="hydra-llama-context-hint" class="small hydra-context-hint"></div>
|
| 16661 |
<div id="hydra-llama-context-estimate" class="hydra-context-estimate" data-hydra-context-estimate="llama_cpp"></div>
|
| 16662 |
</label>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16663 |
<label class="hydra-context-field" data-hydra-provider-field="llama_cpp">Multi-Token Prediction
|
| 16664 |
${renderToggleRow(
|
| 16665 |
`<input id="set_hydra_llama_cpp_mtp_enabled" class="toggle-input" type="checkbox" ${
|
|
@@ -16691,6 +17103,66 @@ async function loadSettingsView() {
|
|
| 16691 |
<div id="hydra-mlx-context-hint" class="small hydra-context-hint"></div>
|
| 16692 |
<div id="hydra-mlx-context-estimate" class="hydra-context-estimate" data-hydra-context-estimate="mlx_lm"></div>
|
| 16693 |
</label>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16694 |
<label id="hydra-base-api-key-wrap" data-hydra-provider-field="openai_compatible" style="grid-column: 1 / -1;">API Key (optional)
|
| 16695 |
<input id="set_hydra_llm_api_key" type="password" autocomplete="new-password" value="${escapeHtml(
|
| 16696 |
hydraPrimaryBaseRow.api_key || ""
|
|
@@ -17661,6 +18133,8 @@ async function loadSettingsView() {
|
|
| 17661 |
if (["speakerid", "emotionid"].includes(activeModelsPanel)) {
|
| 17662 |
void ensureEspHomeRuntimeLoaded({ force: true, panel: activeModelsPanel });
|
| 17663 |
}
|
|
|
|
|
|
|
| 17664 |
}
|
| 17665 |
};
|
| 17666 |
|
|
@@ -17819,6 +18293,8 @@ async function loadSettingsView() {
|
|
| 17819 |
const hydraBaseModelSelectEl = document.getElementById("set_hydra_llm_model_select");
|
| 17820 |
const hydraBaseModelLabelEl = document.getElementById("hydra-base-model-label");
|
| 17821 |
const hydraLocalModelStatusEl = document.getElementById("hydra-local-model-status");
|
|
|
|
|
|
|
| 17822 |
const contextControlConfig = {
|
| 17823 |
hf_transformers: {
|
| 17824 |
min: 256,
|
|
@@ -18034,6 +18510,42 @@ async function loadSettingsView() {
|
|
| 18034 |
llamaCppMtpDraftNumberEl?.addEventListener("input", () => syncLlamaCppMtpDraftControl(llamaCppMtpDraftNumberEl));
|
| 18035 |
llamaCppMtpDraftNumberEl?.addEventListener("blur", () => syncLlamaCppMtpDraftControl(llamaCppMtpDraftNumberEl));
|
| 18036 |
syncLlamaCppMtpDraftControl();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18037 |
const normalizeHydraBaseRowInput = (row) => ({
|
| 18038 |
provider: normalizeHydraBaseProvider(row?.provider || ""),
|
| 18039 |
host: String(row?.host || "").trim(),
|
|
@@ -18133,6 +18645,22 @@ async function loadSettingsView() {
|
|
| 18133 |
}
|
| 18134 |
return String(hydraBaseModelEl?.value || "").trim();
|
| 18135 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18136 |
const refreshLocalLlmModels = async ({ selectModel = "", provider = "" } = {}) => {
|
| 18137 |
try {
|
| 18138 |
localLlmModelsPayload = await api("/api/settings/local-llm/models", { _timeoutMs: HEALTH_REQUEST_TIMEOUT_MS });
|
|
@@ -18195,6 +18723,7 @@ async function loadSettingsView() {
|
|
| 18195 |
syncHydraProviderScopedFields(hydraBaseFieldsEl, provider);
|
| 18196 |
syncHydraModelInputForProvider(hydraBaseModelEl, hydraBaseModelLabelEl, provider);
|
| 18197 |
syncHydraPrimaryModelControl(provider);
|
|
|
|
| 18198 |
};
|
| 18199 |
const hydraRouteControls = [];
|
| 18200 |
const syncHydraRouteControl = (control, preferredModel = "") => {
|
|
@@ -18492,6 +19021,17 @@ async function loadSettingsView() {
|
|
| 18492 |
syncActiveHydraContextControl();
|
| 18493 |
renderHydraContextEstimateCards();
|
| 18494 |
syncVisionLlamaContextControl();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18495 |
});
|
| 18496 |
syncHydraPrimaryProviderFields();
|
| 18497 |
if (state.hydraContextEstimateRuntimeListener) {
|
|
@@ -18545,11 +19085,18 @@ async function loadSettingsView() {
|
|
| 18545 |
const localModelManagerStatusEl = document.getElementById("local-model-manager-status");
|
| 18546 |
const localModelManagerListEl = document.getElementById("local-model-manager-list");
|
| 18547 |
const localModelManagerRefreshEl = document.getElementById("local-model-manager-refresh");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18548 |
const hfModelBrowserViewButtons = Array.from(root.querySelectorAll("[data-hf-model-view]"));
|
| 18549 |
const hfModelBrowserState = {
|
| 18550 |
provider: isHydraLocalProvider(hydraPrimaryBaseRow.provider) ? hydraPrimaryBaseRow.provider : "hf_transformers",
|
| 18551 |
task: "text-generation",
|
| 18552 |
-
view: "
|
| 18553 |
query: "",
|
| 18554 |
loaded: false,
|
| 18555 |
loadingSeq: 0,
|
|
@@ -18575,6 +19122,9 @@ async function loadSettingsView() {
|
|
| 18575 |
const hfModelTaskLabel = (task) => normalizeHfModelTask(task) === "image-text-to-text" ? "vision" : "text";
|
| 18576 |
const normalizeHfModelView = (value) => {
|
| 18577 |
const token = String(value || "trending").trim().toLowerCase().replace(/_/g, "-");
|
|
|
|
|
|
|
|
|
|
| 18578 |
if (token === "new" || token === "recent" || token === "latest") {
|
| 18579 |
return "new";
|
| 18580 |
}
|
|
@@ -18585,6 +19135,9 @@ async function loadSettingsView() {
|
|
| 18585 |
};
|
| 18586 |
const hfModelViewLabel = (value) => {
|
| 18587 |
const view = normalizeHfModelView(value);
|
|
|
|
|
|
|
|
|
|
| 18588 |
if (view === "new") {
|
| 18589 |
return "new";
|
| 18590 |
}
|
|
@@ -18681,6 +19234,7 @@ async function loadSettingsView() {
|
|
| 18681 |
</div>
|
| 18682 |
${pathLabel ? `<div class="local-model-path">${escapeHtml(pathLabel)}</div>` : ""}
|
| 18683 |
<div class="local-model-actions">
|
|
|
|
| 18684 |
<button type="button" class="inline-btn danger" data-local-model-delete data-provider="${escapeHtml(row.provider)}" data-model="${escapeHtml(row.model)}">Delete</button>
|
| 18685 |
</div>
|
| 18686 |
</article>
|
|
@@ -18801,20 +19355,24 @@ async function loadSettingsView() {
|
|
| 18801 |
const library = String(model?.library_name || model?.pipeline_tag || "").trim();
|
| 18802 |
const compatible = Boolean(model?.compatible);
|
| 18803 |
const supportsVision = Boolean(model?.supports_vision) || normalizeHfModelTask(model?.task) === "image-text-to-text" || normalizeHfModelTask(hfModelBrowserState.task) === "image-text-to-text";
|
|
|
|
|
|
|
|
|
|
| 18804 |
const isMlxProvider = provider === "mlx_lm";
|
| 18805 |
const downloadLabel = isMlxProvider ? "Download Repo" : "Download";
|
| 18806 |
return `
|
| 18807 |
-
<article class="hf-model-card ${compatible ? "" : "is-uncertain"}" role="button" tabindex="0" aria-label="Show files for ${escapeHtml(id)}" data-hf-model-card data-provider="${escapeHtml(provider)}" data-repo-id="${escapeHtml(id)}">
|
| 18808 |
<div class="hf-model-card-head">
|
| 18809 |
<div>
|
| 18810 |
<strong>${escapeHtml(id)}</strong>
|
| 18811 |
<span>${escapeHtml(library || hfModelProviderLabel(provider))}</span>
|
| 18812 |
</div>
|
| 18813 |
-
<span class="hf-model-provider-pill">${escapeHtml(hfModelProviderLabel(provider))}</span>
|
| 18814 |
</div>
|
| 18815 |
<div class="hf-model-card-meta">
|
| 18816 |
${modelSize ? `<span class="hf-model-size-pill">${escapeHtml(modelSize)}</span>` : ""}
|
| 18817 |
${supportsVision ? `<span class="hf-model-vision-pill" title="Supports vision models">Vision</span>` : ""}
|
|
|
|
| 18818 |
<span>${escapeHtml(downloads)} downloads</span>
|
| 18819 |
<span>${escapeHtml(likes)} likes</span>
|
| 18820 |
${updated ? `<span>${escapeHtml(updated)}</span>` : ""}
|
|
@@ -18922,7 +19480,13 @@ async function loadSettingsView() {
|
|
| 18922 |
hfModelBrowserState.provider = provider;
|
| 18923 |
hfModelBrowserState.task = task;
|
| 18924 |
hfModelBrowserState.query = query;
|
| 18925 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18926 |
if (hfModelBrowserResultsEl) {
|
| 18927 |
hfModelBrowserResultsEl.innerHTML = `<div class="hf-model-browser-empty">Loading models...</div>`;
|
| 18928 |
}
|
|
@@ -18949,7 +19513,12 @@ async function loadSettingsView() {
|
|
| 18949 |
}
|
| 18950 |
renderHfModelCards(payload?.models || []);
|
| 18951 |
renderHfModelBrowserPagination();
|
| 18952 |
-
setHfModelBrowserStatus(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18953 |
} catch (error) {
|
| 18954 |
if (seq !== hfModelBrowserState.loadingSeq) {
|
| 18955 |
return;
|
|
@@ -19096,15 +19665,210 @@ async function loadSettingsView() {
|
|
| 19096 |
showToast(`Model delete failed: ${error.message}`, "error", 3600);
|
| 19097 |
}
|
| 19098 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19099 |
const activateLlmVisionTab = (tab) => {
|
| 19100 |
const normalized = String(tab || "settings").trim() || "settings";
|
| 19101 |
const browserActive = normalized === "huggingface";
|
| 19102 |
const manageActive = normalized === "manage";
|
|
|
|
| 19103 |
llmVisionTabButtons.forEach((button) => {
|
| 19104 |
button.classList.toggle("active", String(button.dataset.llmVisionTab || "") === normalized);
|
| 19105 |
});
|
| 19106 |
root.querySelectorAll(".llm-vision-settings-block").forEach((block) => {
|
| 19107 |
-
block.classList.toggle("llm-vision-hidden", browserActive || manageActive);
|
| 19108 |
});
|
| 19109 |
if (hfModelBrowserEl) {
|
| 19110 |
hfModelBrowserEl.hidden = !browserActive;
|
|
@@ -19114,6 +19878,13 @@ async function loadSettingsView() {
|
|
| 19114 |
localModelManagerEl.hidden = !manageActive;
|
| 19115 |
localModelManagerEl.classList.toggle("active", manageActive);
|
| 19116 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19117 |
if (browserActive) {
|
| 19118 |
renderHfDownloadSummary(state.hfLlmWarmupLastSnapshot || {});
|
| 19119 |
scheduleHfLlmWarmupPoll(150);
|
|
@@ -19122,6 +19893,12 @@ async function loadSettingsView() {
|
|
| 19122 |
renderLocalModelManager();
|
| 19123 |
void refreshLocalLlmModels();
|
| 19124 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19125 |
if (browserActive && !hfModelBrowserState.loaded) {
|
| 19126 |
if (hfModelBrowserProviderEl) {
|
| 19127 |
hfModelBrowserProviderEl.value = isHydraLocalProvider(hydraBaseProviderEl?.value || "")
|
|
@@ -19134,6 +19911,23 @@ async function loadSettingsView() {
|
|
| 19134 |
llmVisionTabButtons.forEach((button) => {
|
| 19135 |
button.addEventListener("click", () => activateLlmVisionTab(button.dataset.llmVisionTab || "settings"));
|
| 19136 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19137 |
hfModelBrowserViewButtons.forEach((button) => {
|
| 19138 |
button.addEventListener("click", () => {
|
| 19139 |
hfModelBrowserState.view = normalizeHfModelView(button.dataset.hfModelView || "trending");
|
|
@@ -19270,6 +20064,20 @@ async function loadSettingsView() {
|
|
| 19270 |
void refreshLocalLlmModels();
|
| 19271 |
});
|
| 19272 |
localModelManagerListEl?.addEventListener("click", (event) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19273 |
const button = event.target instanceof Element ? event.target.closest("[data-local-model-delete]") : null;
|
| 19274 |
if (!button) {
|
| 19275 |
return;
|
|
@@ -20682,10 +21490,25 @@ async function loadSettingsView() {
|
|
| 20682 |
const baseModel = getHydraBaseModelValue();
|
| 20683 |
const baseApiKey = String(document.getElementById("set_hydra_llm_api_key")?.value || "").trim();
|
| 20684 |
const hfTransformersContextTokens = String(document.getElementById("set_hydra_hf_transformers_context_tokens")?.value || "").trim();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20685 |
const llamaCppContextTokens = String(document.getElementById("set_hydra_llama_cpp_context_tokens")?.value || "").trim();
|
| 20686 |
const llamaCppMtpEnabled = Boolean(document.getElementById("set_hydra_llama_cpp_mtp_enabled")?.checked);
|
| 20687 |
const llamaCppMtpDraftTokens = String(document.getElementById("set_hydra_llama_cpp_mtp_draft_tokens")?.value || "3").trim();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20688 |
const mlxLmContextTokens = String(document.getElementById("set_hydra_mlx_lm_context_tokens")?.value || "").trim();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20689 |
const additionalBaseRows = readHydraAdditionalBaseRows();
|
| 20690 |
const hydraBaseServersPayload = [
|
| 20691 |
normalizeHydraBaseRowInput({ provider: baseProvider, host: baseHost, port: basePort, model: baseModel, api_key: baseApiKey }),
|
|
@@ -20703,10 +21526,25 @@ async function loadSettingsView() {
|
|
| 20703 |
hydra_llm_model: baseModel,
|
| 20704 |
hydra_llm_api_key: baseApiKey,
|
| 20705 |
hydra_hf_transformers_context_tokens: hfTransformersContextTokens,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20706 |
hydra_llama_cpp_context_tokens: llamaCppContextTokens,
|
| 20707 |
hydra_llama_cpp_mtp_enabled: llamaCppMtpEnabled,
|
| 20708 |
hydra_llama_cpp_mtp_draft_tokens: llamaCppMtpDraftTokens,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20709 |
hydra_mlx_lm_context_tokens: mlxLmContextTokens,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20710 |
hydra_base_servers: hydraBaseServersPayload,
|
| 20711 |
},
|
| 20712 |
};
|
|
@@ -23371,6 +24209,9 @@ async function loadView(viewName) {
|
|
| 23371 |
if (state.view !== "spudex") {
|
| 23372 |
clearSpudexPollTimer();
|
| 23373 |
}
|
|
|
|
|
|
|
|
|
|
| 23374 |
document.body.dataset.view = String(viewName || "").trim().toLowerCase();
|
| 23375 |
setActiveNav(viewName);
|
| 23376 |
updateHeader();
|
|
@@ -23459,6 +24300,7 @@ window.addEventListener("beforeunload", () => {
|
|
| 23459 |
}
|
| 23460 |
clearDashboardRefreshTimer();
|
| 23461 |
clearSpudexPollTimer();
|
|
|
|
| 23462 |
closeChatEventSource();
|
| 23463 |
stopAllChatJobPolling();
|
| 23464 |
stopRuntimeBreakdownPolling();
|
|
|
|
| 127 |
dashboardRefreshStatus: "",
|
| 128 |
hfLlmWarmupPollTimer: 0,
|
| 129 |
hfLlmWarmupLastSnapshot: null,
|
| 130 |
+
llmDebugPollTimer: 0,
|
| 131 |
+
llmDebugInFlight: false,
|
| 132 |
+
llmDebugNextId: 0,
|
| 133 |
+
llmDebugEvents: [],
|
| 134 |
+
llmDebugLastSnapshot: null,
|
| 135 |
+
llmDebugAutoScroll: String(safeStorageGet("tater_llm_debug_auto_scroll", "true")).trim().toLowerCase() !== "false",
|
| 136 |
runtimeSettingsSaveHandler: null,
|
| 137 |
runtimeSettingsOpenHandler: null,
|
| 138 |
runtimeSettingsCloseHandler: null,
|
|
|
|
| 184 |
|
| 185 |
safeStorageSet("tater_tateros_session_id", state.sessionId);
|
| 186 |
|
| 187 |
+
function clearLlmDebugPollTimer() {
|
| 188 |
+
if (state.llmDebugPollTimer) {
|
| 189 |
+
window.clearTimeout(state.llmDebugPollTimer);
|
| 190 |
+
state.llmDebugPollTimer = 0;
|
| 191 |
+
}
|
| 192 |
+
state.llmDebugInFlight = false;
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
const APP_BASE_PATH = (() => {
|
| 196 |
const rawPath = String(window.location.pathname || "/").trim();
|
| 197 |
const normalized = rawPath.replace(/\/+$/, "");
|
|
|
|
| 1030 |
setActionProgress(100, statusText, tone);
|
| 1031 |
}
|
| 1032 |
|
| 1033 |
+
function normalizeLocalLlmProviderToken(value) {
|
| 1034 |
+
const token = String(value || "").trim().toLowerCase().replace(/[\s.-]+/g, "_");
|
| 1035 |
+
if (["llama", "llamacpp", "llama_cpp", "gguf", "llama_cpp_python"].includes(token)) {
|
| 1036 |
+
return "llama_cpp";
|
| 1037 |
+
}
|
| 1038 |
+
if (["mlx", "mlx_lm", "apple_mlx", "apple_silicon", "mlxlm"].includes(token)) {
|
| 1039 |
+
return "mlx_lm";
|
| 1040 |
+
}
|
| 1041 |
+
if (["hf", "huggingface", "hugging_face", "transformers", "hf_transformers", "local_transformers"].includes(token)) {
|
| 1042 |
+
return "hf_transformers";
|
| 1043 |
+
}
|
| 1044 |
+
return token || "hf_transformers";
|
| 1045 |
+
}
|
| 1046 |
+
|
| 1047 |
+
function isLocalLlmProviderToken(value) {
|
| 1048 |
+
return ["hf_transformers", "llama_cpp", "mlx_lm"].includes(normalizeLocalLlmProviderToken(value));
|
| 1049 |
+
}
|
| 1050 |
+
|
| 1051 |
+
function localLlmProviderLabel(value) {
|
| 1052 |
+
const provider = normalizeLocalLlmProviderToken(value);
|
| 1053 |
+
if (provider === "llama_cpp") {
|
| 1054 |
+
return "llama.cpp";
|
| 1055 |
+
}
|
| 1056 |
+
if (provider === "mlx_lm") {
|
| 1057 |
+
return "MLX";
|
| 1058 |
+
}
|
| 1059 |
+
return "Transformers";
|
| 1060 |
+
}
|
| 1061 |
+
|
| 1062 |
function closeActionProgressModal() {
|
| 1063 |
const modal = document.getElementById("action-progress-modal");
|
| 1064 |
if (!modal) {
|
|
|
|
| 1101 |
}
|
| 1102 |
}
|
| 1103 |
|
| 1104 |
+
function localLlmChatTemplateCapabilityBadges(capabilities = {}) {
|
| 1105 |
+
const items = [
|
| 1106 |
+
["enable_thinking", "enable_thinking"],
|
| 1107 |
+
["reasoning_budget", "reasoning budget"],
|
| 1108 |
+
["no_think_marker", "/no_think"],
|
| 1109 |
+
["think_tags", "think tags"],
|
| 1110 |
+
];
|
| 1111 |
+
return items
|
| 1112 |
+
.map(([key, label]) => {
|
| 1113 |
+
const active = Boolean(capabilities?.[key]);
|
| 1114 |
+
return `<span class="llama-template-badge${active ? " active" : ""}">${escapeHtml(label)} ${active ? "found" : "missing"}</span>`;
|
| 1115 |
+
})
|
| 1116 |
+
.join("");
|
| 1117 |
+
}
|
| 1118 |
+
|
| 1119 |
+
function ensureLocalLlmChatTemplateModal() {
|
| 1120 |
+
let modal = document.getElementById("llama-chat-template-modal");
|
| 1121 |
+
if (modal) {
|
| 1122 |
+
return modal;
|
| 1123 |
+
}
|
| 1124 |
+
document.body.insertAdjacentHTML(
|
| 1125 |
+
"beforeend",
|
| 1126 |
+
`
|
| 1127 |
+
<div id="llama-chat-template-modal" class="cerb-modal" aria-hidden="true">
|
| 1128 |
+
<div class="cerb-modal-dialog card llama-chat-template-dialog" role="dialog" aria-modal="true" aria-label="Local Model Chat Template">
|
| 1129 |
+
<div class="card-head runtime-breakdown-modal-head">
|
| 1130 |
+
<span class="runtime-breakdown-modal-badge" aria-hidden="true">CT</span>
|
| 1131 |
+
<div>
|
| 1132 |
+
<h3 id="llama-chat-template-title" class="card-title">Chat Template</h3>
|
| 1133 |
+
<div id="llama-chat-template-model" class="small"></div>
|
| 1134 |
+
</div>
|
| 1135 |
+
<button type="button" class="inline-btn" id="llama-chat-template-close">Close</button>
|
| 1136 |
+
</div>
|
| 1137 |
+
<div class="cerb-modal-body llama-chat-template-body">
|
| 1138 |
+
<div id="llama-chat-template-summary" class="llama-chat-template-summary"></div>
|
| 1139 |
+
<textarea id="llama-chat-template-editor" class="llama-chat-template-editor" spellcheck="false"></textarea>
|
| 1140 |
+
<div class="llama-chat-template-actions">
|
| 1141 |
+
<button type="button" class="inline-btn" id="llama-chat-template-use-embedded">Use Embedded</button>
|
| 1142 |
+
<button type="button" class="inline-btn" id="llama-chat-template-reload">Reload</button>
|
| 1143 |
+
<button type="button" class="inline-btn" id="llama-chat-template-reset">Reset Override</button>
|
| 1144 |
+
<button type="button" class="inline-btn primary" id="llama-chat-template-save">Save Override</button>
|
| 1145 |
+
</div>
|
| 1146 |
+
<div id="llama-chat-template-status" class="small llama-chat-template-status"></div>
|
| 1147 |
+
</div>
|
| 1148 |
+
</div>
|
| 1149 |
+
</div>
|
| 1150 |
+
`
|
| 1151 |
+
);
|
| 1152 |
+
modal = document.getElementById("llama-chat-template-modal");
|
| 1153 |
+
const closeModal = () => closePopupModal(modal);
|
| 1154 |
+
document.getElementById("llama-chat-template-close")?.addEventListener("click", closeModal);
|
| 1155 |
+
modal?.addEventListener("click", (event) => {
|
| 1156 |
+
if (event.target === modal) {
|
| 1157 |
+
closeModal();
|
| 1158 |
+
}
|
| 1159 |
+
});
|
| 1160 |
+
document.addEventListener("keydown", (event) => {
|
| 1161 |
+
if (event.key === "Escape" && modal?.classList.contains("active")) {
|
| 1162 |
+
closeModal();
|
| 1163 |
+
}
|
| 1164 |
+
});
|
| 1165 |
+
return modal;
|
| 1166 |
+
}
|
| 1167 |
+
|
| 1168 |
+
async function openLocalLlmChatTemplateModal(provider, model, options = {}) {
|
| 1169 |
+
const providerToken = normalizeLocalLlmProviderToken(provider);
|
| 1170 |
+
const modelId = String(model || "").trim();
|
| 1171 |
+
if (!isLocalLlmProviderToken(providerToken) || !modelId) {
|
| 1172 |
+
showToast("Select a local model first.", "error", 2600);
|
| 1173 |
+
return null;
|
| 1174 |
+
}
|
| 1175 |
+
const providerLabel = localLlmProviderLabel(providerToken);
|
| 1176 |
+
const modal = ensureLocalLlmChatTemplateModal();
|
| 1177 |
+
const titleEl = document.getElementById("llama-chat-template-title");
|
| 1178 |
+
const modelEl = document.getElementById("llama-chat-template-model");
|
| 1179 |
+
const summaryEl = document.getElementById("llama-chat-template-summary");
|
| 1180 |
+
const editorEl = document.getElementById("llama-chat-template-editor");
|
| 1181 |
+
const statusEl = document.getElementById("llama-chat-template-status");
|
| 1182 |
+
const saveBtn = document.getElementById("llama-chat-template-save");
|
| 1183 |
+
const resetBtn = document.getElementById("llama-chat-template-reset");
|
| 1184 |
+
const reloadBtn = document.getElementById("llama-chat-template-reload");
|
| 1185 |
+
const embeddedBtn = document.getElementById("llama-chat-template-use-embedded");
|
| 1186 |
+
let latestPayload = null;
|
| 1187 |
+
|
| 1188 |
+
const setStatus = (text, tone = "") => {
|
| 1189 |
+
if (!statusEl) {
|
| 1190 |
+
return;
|
| 1191 |
+
}
|
| 1192 |
+
statusEl.textContent = String(text || "").trim();
|
| 1193 |
+
statusEl.classList.toggle("success", tone === "success");
|
| 1194 |
+
statusEl.classList.toggle("error", tone === "error");
|
| 1195 |
+
};
|
| 1196 |
+
const setBusy = (busy) => {
|
| 1197 |
+
[
|
| 1198 |
+
document.getElementById("llama-chat-template-save"),
|
| 1199 |
+
document.getElementById("llama-chat-template-reset"),
|
| 1200 |
+
document.getElementById("llama-chat-template-reload"),
|
| 1201 |
+
document.getElementById("llama-chat-template-use-embedded"),
|
| 1202 |
+
].forEach((button) => {
|
| 1203 |
+
if (button) {
|
| 1204 |
+
button.disabled = Boolean(busy);
|
| 1205 |
+
}
|
| 1206 |
+
});
|
| 1207 |
+
};
|
| 1208 |
+
const renderPayload = (payload) => {
|
| 1209 |
+
latestPayload = payload && typeof payload === "object" ? payload : {};
|
| 1210 |
+
const effective = String(latestPayload.effective_template || latestPayload.override_template || latestPayload.embedded_template || "");
|
| 1211 |
+
if (titleEl) {
|
| 1212 |
+
titleEl.textContent = latestPayload.override_active ? "Chat Template Override" : "Chat Template";
|
| 1213 |
+
}
|
| 1214 |
+
if (modelEl) {
|
| 1215 |
+
modelEl.textContent = `${providerLabel} • ${modelId}`;
|
| 1216 |
+
}
|
| 1217 |
+
if (editorEl) {
|
| 1218 |
+
editorEl.value = effective;
|
| 1219 |
+
}
|
| 1220 |
+
const templateNames = Array.isArray(latestPayload.template_names) ? latestPayload.template_names : [];
|
| 1221 |
+
const source = String(latestPayload.source || "none").trim();
|
| 1222 |
+
const sourceLabel = source === "override" ? "Override active" : source === "gguf" || source === "embedded" ? "Using embedded model template" : "No embedded template found";
|
| 1223 |
+
const embeddedChars = Number(latestPayload.embedded_template_chars || 0);
|
| 1224 |
+
const overrideChars = Number(latestPayload.override_template_chars || 0);
|
| 1225 |
+
const maxChars = Number(latestPayload.max_chars || 0);
|
| 1226 |
+
if (summaryEl) {
|
| 1227 |
+
summaryEl.innerHTML = `
|
| 1228 |
+
<div class="llama-chat-template-summary-row">
|
| 1229 |
+
<strong>${escapeHtml(sourceLabel)}</strong>
|
| 1230 |
+
<span>${escapeHtml(templateNames.length ? templateNames.join(", ") : "default template")}</span>
|
| 1231 |
+
</div>
|
| 1232 |
+
<div class="llama-chat-template-badges">
|
| 1233 |
+
${localLlmChatTemplateCapabilityBadges(latestPayload.capabilities || {})}
|
| 1234 |
+
</div>
|
| 1235 |
+
<div class="llama-chat-template-meta">
|
| 1236 |
+
<span>Embedded ${embeddedChars.toLocaleString()} chars</span>
|
| 1237 |
+
<span>Override ${overrideChars.toLocaleString()} chars</span>
|
| 1238 |
+
${maxChars > 0 ? `<span>Limit ${maxChars.toLocaleString()} chars</span>` : ""}
|
| 1239 |
+
</div>
|
| 1240 |
+
`;
|
| 1241 |
+
}
|
| 1242 |
+
const embeddedButton = document.getElementById("llama-chat-template-use-embedded");
|
| 1243 |
+
if (embeddedButton) {
|
| 1244 |
+
embeddedButton.disabled = embeddedChars <= 0;
|
| 1245 |
+
}
|
| 1246 |
+
setStatus(source === "none" || source === "fallback" ? "No embedded template was found for this model." : "Template loaded.");
|
| 1247 |
+
};
|
| 1248 |
+
const load = async () => {
|
| 1249 |
+
setBusy(true);
|
| 1250 |
+
setStatus("Loading chat template...");
|
| 1251 |
+
try {
|
| 1252 |
+
const payload = await api(`/api/settings/local-llm/chat-template?provider=${encodeURIComponent(providerToken)}&model=${encodeURIComponent(modelId)}`, {
|
| 1253 |
+
_timeoutMs: HEALTH_REQUEST_TIMEOUT_MS,
|
| 1254 |
+
});
|
| 1255 |
+
renderPayload(payload);
|
| 1256 |
+
} catch (error) {
|
| 1257 |
+
setStatus(`Load failed: ${error.message}`, "error");
|
| 1258 |
+
if (summaryEl) {
|
| 1259 |
+
summaryEl.innerHTML = `<div class="hf-model-browser-empty">Template metadata could not be loaded.</div>`;
|
| 1260 |
+
}
|
| 1261 |
+
} finally {
|
| 1262 |
+
setBusy(false);
|
| 1263 |
+
}
|
| 1264 |
+
};
|
| 1265 |
+
const save = async ({ reset = false } = {}) => {
|
| 1266 |
+
setBusy(true);
|
| 1267 |
+
setStatus(reset ? "Resetting override..." : "Saving override...");
|
| 1268 |
+
try {
|
| 1269 |
+
const payload = await api("/api/settings/local-llm/chat-template", {
|
| 1270 |
+
method: "POST",
|
| 1271 |
+
body: JSON.stringify({
|
| 1272 |
+
provider: providerToken,
|
| 1273 |
+
model: modelId,
|
| 1274 |
+
template: reset ? "" : String(editorEl?.value || ""),
|
| 1275 |
+
reset,
|
| 1276 |
+
}),
|
| 1277 |
+
_timeoutMs: HEALTH_REQUEST_TIMEOUT_MS,
|
| 1278 |
+
});
|
| 1279 |
+
renderPayload(payload);
|
| 1280 |
+
setStatus(reset ? "Override reset. The next model call will use the embedded template." : "Override saved. The matching loaded model was unloaded for a clean reload.", "success");
|
| 1281 |
+
if (typeof options.onSaved === "function") {
|
| 1282 |
+
options.onSaved(payload);
|
| 1283 |
+
}
|
| 1284 |
+
} catch (error) {
|
| 1285 |
+
setStatus(`${reset ? "Reset" : "Save"} failed: ${error.message}`, "error");
|
| 1286 |
+
} finally {
|
| 1287 |
+
setBusy(false);
|
| 1288 |
+
}
|
| 1289 |
+
};
|
| 1290 |
+
|
| 1291 |
+
saveBtn?.replaceWith(saveBtn.cloneNode(true));
|
| 1292 |
+
resetBtn?.replaceWith(resetBtn.cloneNode(true));
|
| 1293 |
+
reloadBtn?.replaceWith(reloadBtn.cloneNode(true));
|
| 1294 |
+
embeddedBtn?.replaceWith(embeddedBtn.cloneNode(true));
|
| 1295 |
+
document.getElementById("llama-chat-template-save")?.addEventListener("click", () => {
|
| 1296 |
+
void save();
|
| 1297 |
+
});
|
| 1298 |
+
document.getElementById("llama-chat-template-reset")?.addEventListener("click", () => {
|
| 1299 |
+
if (window.confirm(`Reset chat template override for ${modelId}?`)) {
|
| 1300 |
+
void save({ reset: true });
|
| 1301 |
+
}
|
| 1302 |
+
});
|
| 1303 |
+
document.getElementById("llama-chat-template-reload")?.addEventListener("click", () => {
|
| 1304 |
+
void load();
|
| 1305 |
+
});
|
| 1306 |
+
document.getElementById("llama-chat-template-use-embedded")?.addEventListener("click", () => {
|
| 1307 |
+
if (editorEl && latestPayload) {
|
| 1308 |
+
editorEl.value = String(latestPayload.embedded_template || "");
|
| 1309 |
+
setStatus("Embedded template copied into the editor.");
|
| 1310 |
+
}
|
| 1311 |
+
});
|
| 1312 |
+
|
| 1313 |
+
openPopupModal(modal);
|
| 1314 |
+
await load();
|
| 1315 |
+
return modal;
|
| 1316 |
+
}
|
| 1317 |
+
|
| 1318 |
async function api(path, options = {}) {
|
| 1319 |
const requestOptions = options && typeof options === "object" ? options : {};
|
| 1320 |
const skipRedisRecovery = Boolean(requestOptions._skipRedisRecovery);
|
|
|
|
| 4505 |
return "";
|
| 4506 |
}
|
| 4507 |
|
| 4508 |
+
function _chatDirectMediaUrl(rawUrl) {
|
| 4509 |
+
const safeHref = _sanitizeMarkdownHref(rawUrl);
|
| 4510 |
+
if (!safeHref) {
|
| 4511 |
+
return "";
|
| 4512 |
+
}
|
| 4513 |
+
return safeHref.startsWith("/") ? withBasePath(safeHref) : safeHref;
|
| 4514 |
+
}
|
| 4515 |
+
|
| 4516 |
function _renderMarkdownInline(rawText) {
|
| 4517 |
const source = String(rawText ?? "");
|
| 4518 |
if (!source) {
|
|
|
|
| 4712 |
`;
|
| 4713 |
} else {
|
| 4714 |
const contentType = String(content.type || "").toLowerCase();
|
| 4715 |
+
const fileId = String(content.id || content.file_id || "").trim();
|
| 4716 |
const fileName = String(content.name || "attachment").trim() || "attachment";
|
| 4717 |
const mimetype = String(content.mimetype || "").trim() || "application/octet-stream";
|
| 4718 |
const fileUrl = _chatFileUrl(fileId, mimetype);
|
| 4719 |
+
const directMediaUrl = _chatDirectMediaUrl(content.url || content.src || content.href);
|
| 4720 |
+
const mediaUrl = directMediaUrl || fileUrl;
|
| 4721 |
const hasDataB64 = typeof content.data_b64 === "string" && content.data_b64.trim().length > 0;
|
| 4722 |
+
const hasMediaUrl = Boolean(mediaUrl);
|
| 4723 |
if (contentType === "image" && hasDataB64) {
|
| 4724 |
const imageUrl = `data:${escapeHtml(content.mimetype || "image/png")};base64,${content.data_b64}`;
|
| 4725 |
bodyHtml = `<img class="chat-media-image" src="${imageUrl}" alt="${escapeHtml(content.name || "image")}" />`;
|
| 4726 |
+
} else if (contentType === "image" && hasMediaUrl) {
|
| 4727 |
+
bodyHtml = `<img class="chat-media-image" src="${escapeHtml(mediaUrl)}" alt="${escapeHtml(fileName)}" />`;
|
| 4728 |
} else if (contentType === "audio" && hasDataB64) {
|
| 4729 |
const mimetype = String(content.mimetype || "audio/mpeg");
|
| 4730 |
const audioUrl = `data:${escapeHtml(mimetype)};base64,${content.data_b64}`;
|
|
|
|
| 4735 |
<a class="inline-btn" href="${audioUrl}" download="${escapeHtml(fileName)}">Download Audio</a>
|
| 4736 |
</div>
|
| 4737 |
`;
|
| 4738 |
+
} else if (contentType === "audio" && hasMediaUrl) {
|
| 4739 |
bodyHtml = `
|
| 4740 |
<div class="chat-media-wrap">
|
| 4741 |
+
<audio controls preload="metadata" src="${escapeHtml(mediaUrl)}"></audio>
|
| 4742 |
<div class="chat-file-meta">${escapeHtml(fileName)}</div>
|
| 4743 |
+
<a class="inline-btn" href="${escapeHtml(mediaUrl)}" download="${escapeHtml(fileName)}">Download Audio</a>
|
| 4744 |
</div>
|
| 4745 |
`;
|
| 4746 |
} else if (contentType === "video" && hasDataB64) {
|
|
|
|
| 4753 |
<a class="inline-btn" href="${videoUrl}" download="${escapeHtml(fileName)}">Download Video</a>
|
| 4754 |
</div>
|
| 4755 |
`;
|
| 4756 |
+
} else if (contentType === "video" && hasMediaUrl) {
|
| 4757 |
bodyHtml = `
|
| 4758 |
<div class="chat-media-wrap">
|
| 4759 |
+
<video controls preload="metadata" src="${escapeHtml(mediaUrl)}" class="chat-media-video"></video>
|
| 4760 |
<div class="chat-file-meta">${escapeHtml(fileName)}</div>
|
| 4761 |
+
<a class="inline-btn" href="${escapeHtml(mediaUrl)}" download="${escapeHtml(fileName)}">Download Video</a>
|
| 4762 |
</div>
|
| 4763 |
`;
|
| 4764 |
} else if (contentType === "file" && hasDataB64) {
|
|
|
|
| 4771 |
)}">Download File</a>
|
| 4772 |
</div>
|
| 4773 |
`;
|
| 4774 |
+
} else if (contentType === "file" && hasMediaUrl) {
|
| 4775 |
const sizeLabel = _formatBytes(content.size);
|
| 4776 |
bodyHtml = `
|
| 4777 |
<div class="chat-file-card">
|
| 4778 |
<div class="chat-file-meta">${escapeHtml(fileName)}${sizeLabel ? ` (${escapeHtml(sizeLabel)})` : ""}</div>
|
| 4779 |
+
<a class="inline-btn" href="${escapeHtml(mediaUrl)}" download="${escapeHtml(fileName)}">Download File</a>
|
| 4780 |
</div>
|
| 4781 |
`;
|
| 4782 |
} else {
|
|
|
|
| 8460 |
if (load && ["speakerid", "emotionid"].includes(normalized)) {
|
| 8461 |
void ensureEspHomeRuntimeLoaded({ force: true, panel: normalized });
|
| 8462 |
}
|
| 8463 |
+
if (normalized !== "routing") {
|
| 8464 |
+
clearLlmDebugPollTimer();
|
| 8465 |
+
}
|
| 8466 |
};
|
| 8467 |
buttons.forEach((button) => {
|
| 8468 |
if (button.dataset.modelsTabBound === "1") {
|
|
|
|
| 16155 |
.map((option) => `<option value="${escapeHtml(option.value)}"${option.value === selected ? " selected" : ""}>${escapeHtml(option.label)}</option>`)
|
| 16156 |
.join("");
|
| 16157 |
};
|
| 16158 |
+
const renderChoiceOptions = (options, currentValue) => {
|
| 16159 |
+
const selected = String(currentValue ?? "").trim().toLowerCase();
|
| 16160 |
+
return (Array.isArray(options) ? options : [])
|
| 16161 |
+
.map((option) => {
|
| 16162 |
+
const value = String(option?.value ?? "").trim();
|
| 16163 |
+
const label = String(option?.label ?? value).trim();
|
| 16164 |
+
return `<option value="${escapeHtml(value)}"${value.toLowerCase() === selected ? " selected" : ""}>${escapeHtml(label)}</option>`;
|
| 16165 |
+
})
|
| 16166 |
+
.join("");
|
| 16167 |
+
};
|
| 16168 |
const renderHydraLocalProviderOptions = (currentValue) => {
|
| 16169 |
const selected = normalizeHydraBaseProvider(currentValue);
|
| 16170 |
return hydraProviderOptions
|
|
|
|
| 16817 |
<button type="button" class="settings-subtab-btn active" data-llm-vision-tab="settings">Settings</button>
|
| 16818 |
<button type="button" class="settings-subtab-btn" data-llm-vision-tab="huggingface">Hugging Face</button>
|
| 16819 |
<button type="button" class="settings-subtab-btn" data-llm-vision-tab="manage">Manage</button>
|
| 16820 |
+
<button type="button" class="settings-subtab-btn" data-llm-vision-tab="debug">Debug</button>
|
| 16821 |
</div>
|
| 16822 |
<div id="settings-hf-model-browser" class="hf-model-browser" hidden>
|
| 16823 |
<div class="hf-model-browser-hero">
|
|
|
|
| 16841 |
<option value="image-text-to-text">Vision</option>
|
| 16842 |
</select>
|
| 16843 |
<div class="hf-model-browser-view-switch" role="group" aria-label="Model list">
|
| 16844 |
+
<button type="button" class="active" data-hf-model-view="picks">Tater Picks</button>
|
| 16845 |
+
<button type="button" data-hf-model-view="trending">Trending</button>
|
| 16846 |
<button type="button" data-hf-model-view="new">New</button>
|
| 16847 |
<button type="button" data-hf-model-view="downloads">Most Downloaded</button>
|
| 16848 |
</div>
|
|
|
|
| 16893 |
<div id="local-model-manager-status" class="small hf-model-browser-status"></div>
|
| 16894 |
<div id="local-model-manager-list" class="local-model-manager-list"></div>
|
| 16895 |
</div>
|
| 16896 |
+
<div id="settings-llm-debug-console" class="hf-model-browser llm-debug-console-shell" hidden>
|
| 16897 |
+
<div class="hf-model-browser-hero llm-debug-hero">
|
| 16898 |
+
<div class="hf-model-browser-spud" aria-hidden="true">
|
| 16899 |
+
<span class="hf-model-browser-spud-eye left"></span>
|
| 16900 |
+
<span class="hf-model-browser-spud-eye right"></span>
|
| 16901 |
+
<span class="hf-model-browser-spud-spark"></span>
|
| 16902 |
+
</div>
|
| 16903 |
+
<div class="hf-model-browser-title">
|
| 16904 |
+
<strong>Live LLM Console</strong>
|
| 16905 |
+
<span>Runtime events, active calls, prompt timing, output previews, and backend errors.</span>
|
| 16906 |
+
</div>
|
| 16907 |
+
<div class="llm-debug-actions">
|
| 16908 |
+
<button type="button" id="llm-debug-refresh" class="inline-btn">Refresh</button>
|
| 16909 |
+
<button type="button" id="llm-debug-autoscroll" class="inline-btn">Auto-scroll On</button>
|
| 16910 |
+
<button type="button" id="llm-debug-clear" class="inline-btn danger">Clear View</button>
|
| 16911 |
+
</div>
|
| 16912 |
+
</div>
|
| 16913 |
+
<div id="llm-debug-status" class="small hf-model-browser-status">Open Debug to start the live console.</div>
|
| 16914 |
+
<div id="llm-debug-active" class="llm-debug-active"></div>
|
| 16915 |
+
<div id="llm-debug-events" class="llm-debug-events" role="log" aria-live="polite"></div>
|
| 16916 |
+
</div>
|
| 16917 |
<div id="settings-hydra-base-fields" class="hydra-model-panel is-active llm-vision-settings-block">
|
| 16918 |
<div class="hydra-model-panel-title">Base Model</div>
|
| 16919 |
<div class="small hydra-model-panel-note">Used for regular AI calls. Multiple base servers rotate in round-robin order.</div>
|
|
|
|
| 16939 |
<select id="set_hydra_llm_model_select"></select>
|
| 16940 |
<div id="hydra-local-model-status" class="small"></div>
|
| 16941 |
</label>
|
| 16942 |
+
<div id="settings-llama-chat-template-tools" class="hydra-chat-template-tools" data-hydra-provider-field="local">
|
| 16943 |
+
<button type="button" id="settings-llama-chat-template-edit" class="inline-btn">Edit Chat Template</button>
|
| 16944 |
+
<span id="settings-llama-chat-template-status" class="small">Per-model template override.</span>
|
| 16945 |
+
</div>
|
| 16946 |
<label class="hydra-context-field" data-hydra-provider-field="hf_transformers">Context Length
|
| 16947 |
<div class="hydra-context-control" data-hydra-context-control="hf_transformers">
|
| 16948 |
<input id="set_hydra_hf_transformers_context_tokens_range" type="range" min="256" max="262144" step="256" value="${escapeHtml(
|
|
|
|
| 16955 |
<div id="hydra-hf-context-hint" class="small hydra-context-hint"></div>
|
| 16956 |
<div id="hydra-hf-context-estimate" class="hydra-context-estimate" data-hydra-context-estimate="hf_transformers"></div>
|
| 16957 |
</label>
|
| 16958 |
+
<label class="hydra-context-field" data-hydra-provider-field="hf_transformers">Device
|
| 16959 |
+
<select id="set_hydra_hf_transformers_device">
|
| 16960 |
+
${renderChoiceOptions(
|
| 16961 |
+
[
|
| 16962 |
+
{ value: "auto", label: "Auto" },
|
| 16963 |
+
{ value: "cuda", label: "CUDA" },
|
| 16964 |
+
{ value: "mps", label: "Apple MPS" },
|
| 16965 |
+
{ value: "cpu", label: "CPU" },
|
| 16966 |
+
],
|
| 16967 |
+
settings.hydra_hf_transformers_device || "auto"
|
| 16968 |
+
)}
|
| 16969 |
+
</select>
|
| 16970 |
+
<div class="small hydra-context-hint">Auto chooses CUDA, then Apple MPS, then CPU.</div>
|
| 16971 |
+
</label>
|
| 16972 |
+
<label class="hydra-context-field" data-hydra-provider-field="hf_transformers">Precision
|
| 16973 |
+
<select id="set_hydra_hf_transformers_dtype">
|
| 16974 |
+
${renderChoiceOptions(
|
| 16975 |
+
[
|
| 16976 |
+
{ value: "auto", label: "Auto" },
|
| 16977 |
+
{ value: "float16", label: "Float16" },
|
| 16978 |
+
{ value: "bfloat16", label: "BFloat16" },
|
| 16979 |
+
{ value: "float32", label: "Float32" },
|
| 16980 |
+
],
|
| 16981 |
+
settings.hydra_hf_transformers_dtype || "auto"
|
| 16982 |
+
)}
|
| 16983 |
+
</select>
|
| 16984 |
+
<div class="small hydra-context-hint">Auto lets Transformers choose the model dtype.</div>
|
| 16985 |
+
</label>
|
| 16986 |
+
<label class="hydra-context-field" data-hydra-provider-field="hf_transformers">Device Map
|
| 16987 |
+
<select id="set_hydra_hf_transformers_device_map">
|
| 16988 |
+
${renderChoiceOptions(
|
| 16989 |
+
[
|
| 16990 |
+
{ value: "default", label: "Default" },
|
| 16991 |
+
{ value: "disabled", label: "Disabled" },
|
| 16992 |
+
{ value: "auto", label: "Auto" },
|
| 16993 |
+
{ value: "balanced", label: "Balanced" },
|
| 16994 |
+
],
|
| 16995 |
+
settings.hydra_hf_transformers_device_map || "default"
|
| 16996 |
+
)}
|
| 16997 |
+
</select>
|
| 16998 |
+
<div class="small hydra-context-hint">Default uses automatic mapping on CUDA and direct device load elsewhere.</div>
|
| 16999 |
+
</label>
|
| 17000 |
+
<label class="hydra-context-field" data-hydra-provider-field="hf_transformers">Attention
|
| 17001 |
+
<select id="set_hydra_hf_transformers_attn_implementation">
|
| 17002 |
+
${renderChoiceOptions(
|
| 17003 |
+
[
|
| 17004 |
+
{ value: "auto", label: "Auto" },
|
| 17005 |
+
{ value: "sdpa", label: "SDPA" },
|
| 17006 |
+
{ value: "flash_attention_2", label: "Flash Attention 2" },
|
| 17007 |
+
{ value: "eager", label: "Eager" },
|
| 17008 |
+
],
|
| 17009 |
+
settings.hydra_hf_transformers_attn_implementation || "auto"
|
| 17010 |
+
)}
|
| 17011 |
+
</select>
|
| 17012 |
+
<div class="small hydra-context-hint">Uses Transformers attention implementation support when available.</div>
|
| 17013 |
+
</label>
|
| 17014 |
+
<label class="hydra-context-field" data-hydra-provider-field="hf_transformers">Trust Remote Code
|
| 17015 |
+
${renderToggleRow(
|
| 17016 |
+
`<input id="set_hydra_hf_transformers_trust_remote_code" class="toggle-input" type="checkbox" ${
|
| 17017 |
+
settings.hydra_hf_transformers_trust_remote_code ? "checked" : ""
|
| 17018 |
+
} />`,
|
| 17019 |
+
"Enable"
|
| 17020 |
+
)}
|
| 17021 |
+
<div class="small hydra-context-hint">Allows custom model code from the selected repository.</div>
|
| 17022 |
+
</label>
|
| 17023 |
<label class="hydra-context-field" data-hydra-provider-field="llama_cpp">Context Length
|
| 17024 |
<div class="hydra-context-control" data-hydra-context-control="llama_cpp">
|
| 17025 |
<input id="set_hydra_llama_cpp_context_tokens_range" type="range" min="256" max="262144" step="256" value="${escapeHtml(
|
|
|
|
| 17032 |
<div id="hydra-llama-context-hint" class="small hydra-context-hint"></div>
|
| 17033 |
<div id="hydra-llama-context-estimate" class="hydra-context-estimate" data-hydra-context-estimate="llama_cpp"></div>
|
| 17034 |
</label>
|
| 17035 |
+
<label class="hydra-context-field" data-hydra-provider-field="llama_cpp">Eval Batch Size
|
| 17036 |
+
<div class="hydra-context-control hydra-llama-perf-control">
|
| 17037 |
+
<input id="set_hydra_llama_cpp_n_batch_range" type="range" min="32" max="8192" step="32" value="${escapeHtml(
|
| 17038 |
+
settings.hydra_llama_cpp_n_batch || "512"
|
| 17039 |
+
)}" />
|
| 17040 |
+
<input id="set_hydra_llama_cpp_n_batch" type="number" min="32" max="8192" step="32" value="${escapeHtml(
|
| 17041 |
+
settings.hydra_llama_cpp_n_batch || "512"
|
| 17042 |
+
)}" />
|
| 17043 |
+
</div>
|
| 17044 |
+
<div class="small hydra-context-hint">Higher values can improve prompt processing when memory allows.</div>
|
| 17045 |
+
</label>
|
| 17046 |
+
<label class="hydra-context-field" data-hydra-provider-field="llama_cpp">Micro-Batch Size
|
| 17047 |
+
<div class="hydra-context-control hydra-llama-perf-control">
|
| 17048 |
+
<input id="set_hydra_llama_cpp_n_ubatch_range" type="range" min="0" max="8192" step="32" value="${escapeHtml(
|
| 17049 |
+
settings.hydra_llama_cpp_n_ubatch || "0"
|
| 17050 |
+
)}" />
|
| 17051 |
+
<input id="set_hydra_llama_cpp_n_ubatch" type="number" min="0" max="8192" step="32" value="${escapeHtml(
|
| 17052 |
+
settings.hydra_llama_cpp_n_ubatch || "0"
|
| 17053 |
+
)}" />
|
| 17054 |
+
</div>
|
| 17055 |
+
<div class="small hydra-context-hint">0 lets llama.cpp use its default, usually matching the eval batch size.</div>
|
| 17056 |
+
</label>
|
| 17057 |
+
<label class="hydra-context-field" data-hydra-provider-field="llama_cpp">Flash Attention
|
| 17058 |
+
${renderToggleRow(
|
| 17059 |
+
`<input id="set_hydra_llama_cpp_flash_attn" class="toggle-input" type="checkbox" ${
|
| 17060 |
+
settings.hydra_llama_cpp_flash_attn ? "checked" : ""
|
| 17061 |
+
} />`,
|
| 17062 |
+
"Enable"
|
| 17063 |
+
)}
|
| 17064 |
+
<div class="small hydra-context-hint">Optimizes attention computation when supported by the backend and model.</div>
|
| 17065 |
+
</label>
|
| 17066 |
+
<label class="hydra-context-field" data-hydra-provider-field="llama_cpp">GPU KV Offload
|
| 17067 |
+
${renderToggleRow(
|
| 17068 |
+
`<input id="set_hydra_llama_cpp_offload_kqv" class="toggle-input" type="checkbox" ${
|
| 17069 |
+
settings.hydra_llama_cpp_offload_kqv !== false ? "checked" : ""
|
| 17070 |
+
} />`,
|
| 17071 |
+
"Enable"
|
| 17072 |
+
)}
|
| 17073 |
+
<div class="small hydra-context-hint">Keeps attention/KV work on the GPU when llama.cpp supports it.</div>
|
| 17074 |
+
</label>
|
| 17075 |
<label class="hydra-context-field" data-hydra-provider-field="llama_cpp">Multi-Token Prediction
|
| 17076 |
${renderToggleRow(
|
| 17077 |
`<input id="set_hydra_llama_cpp_mtp_enabled" class="toggle-input" type="checkbox" ${
|
|
|
|
| 17103 |
<div id="hydra-mlx-context-hint" class="small hydra-context-hint"></div>
|
| 17104 |
<div id="hydra-mlx-context-estimate" class="hydra-context-estimate" data-hydra-context-estimate="mlx_lm"></div>
|
| 17105 |
</label>
|
| 17106 |
+
<label class="hydra-context-field" data-hydra-provider-field="mlx_lm">Lazy Load
|
| 17107 |
+
${renderToggleRow(
|
| 17108 |
+
`<input id="set_hydra_mlx_lm_lazy_load" class="toggle-input" type="checkbox" ${
|
| 17109 |
+
settings.hydra_mlx_lm_lazy_load ? "checked" : ""
|
| 17110 |
+
} />`,
|
| 17111 |
+
"Enable"
|
| 17112 |
+
)}
|
| 17113 |
+
<div class="small hydra-context-hint">Defers some MLX weight materialization during model load.</div>
|
| 17114 |
+
</label>
|
| 17115 |
+
<label class="hydra-context-field" data-hydra-provider-field="mlx_lm">Trust Remote Code
|
| 17116 |
+
${renderToggleRow(
|
| 17117 |
+
`<input id="set_hydra_mlx_lm_trust_remote_code" class="toggle-input" type="checkbox" ${
|
| 17118 |
+
settings.hydra_mlx_lm_trust_remote_code ? "checked" : ""
|
| 17119 |
+
} />`,
|
| 17120 |
+
"Enable"
|
| 17121 |
+
)}
|
| 17122 |
+
<div class="small hydra-context-hint">Allows custom tokenizer/config code from the selected repository.</div>
|
| 17123 |
+
</label>
|
| 17124 |
+
<label class="hydra-context-field" data-hydra-provider-field="mlx_lm">Prefill Step Size
|
| 17125 |
+
<input id="set_hydra_mlx_engine_prefill_step_size" type="number" min="1" max="32768" step="1" placeholder="Auto" value="${escapeHtml(
|
| 17126 |
+
settings.hydra_mlx_engine_prefill_step_size || ""
|
| 17127 |
+
)}" />
|
| 17128 |
+
<div class="small hydra-context-hint">Blank lets the MLX runtime choose the prompt prefill chunk size.</div>
|
| 17129 |
+
</label>
|
| 17130 |
+
<label class="hydra-context-field" data-hydra-provider-field="mlx_lm">Quantized KV Bits
|
| 17131 |
+
<select id="set_hydra_mlx_engine_kv_bits">
|
| 17132 |
+
${renderChoiceOptions(
|
| 17133 |
+
[
|
| 17134 |
+
{ value: "", label: "Auto" },
|
| 17135 |
+
{ value: "2", label: "2-bit" },
|
| 17136 |
+
{ value: "3", label: "3-bit" },
|
| 17137 |
+
{ value: "4", label: "4-bit" },
|
| 17138 |
+
{ value: "6", label: "6-bit" },
|
| 17139 |
+
{ value: "8", label: "8-bit" },
|
| 17140 |
+
],
|
| 17141 |
+
settings.hydra_mlx_engine_kv_bits || ""
|
| 17142 |
+
)}
|
| 17143 |
+
</select>
|
| 17144 |
+
<div class="small hydra-context-hint">Optional KV cache quantization for lower memory use.</div>
|
| 17145 |
+
</label>
|
| 17146 |
+
<label class="hydra-context-field" data-hydra-provider-field="mlx_lm">KV Group Size
|
| 17147 |
+
<select id="set_hydra_mlx_engine_kv_group_size">
|
| 17148 |
+
${renderChoiceOptions(
|
| 17149 |
+
[
|
| 17150 |
+
{ value: "", label: "Auto" },
|
| 17151 |
+
{ value: "32", label: "32" },
|
| 17152 |
+
{ value: "64", label: "64" },
|
| 17153 |
+
{ value: "128", label: "128" },
|
| 17154 |
+
],
|
| 17155 |
+
settings.hydra_mlx_engine_kv_group_size || ""
|
| 17156 |
+
)}
|
| 17157 |
+
</select>
|
| 17158 |
+
<div class="small hydra-context-hint">Blank uses the MLX runtime default.</div>
|
| 17159 |
+
</label>
|
| 17160 |
+
<label class="hydra-context-field" data-hydra-provider-field="mlx_lm">Quantized KV Start
|
| 17161 |
+
<input id="set_hydra_mlx_engine_quantized_kv_start" type="number" min="0" max="1048576" step="1" placeholder="Auto" value="${escapeHtml(
|
| 17162 |
+
settings.hydra_mlx_engine_quantized_kv_start || ""
|
| 17163 |
+
)}" />
|
| 17164 |
+
<div class="small hydra-context-hint">Token index where quantized KV begins. Blank uses the runtime default.</div>
|
| 17165 |
+
</label>
|
| 17166 |
<label id="hydra-base-api-key-wrap" data-hydra-provider-field="openai_compatible" style="grid-column: 1 / -1;">API Key (optional)
|
| 17167 |
<input id="set_hydra_llm_api_key" type="password" autocomplete="new-password" value="${escapeHtml(
|
| 17168 |
hydraPrimaryBaseRow.api_key || ""
|
|
|
|
| 18133 |
if (["speakerid", "emotionid"].includes(activeModelsPanel)) {
|
| 18134 |
void ensureEspHomeRuntimeLoaded({ force: true, panel: activeModelsPanel });
|
| 18135 |
}
|
| 18136 |
+
} else {
|
| 18137 |
+
clearLlmDebugPollTimer();
|
| 18138 |
}
|
| 18139 |
};
|
| 18140 |
|
|
|
|
| 18293 |
const hydraBaseModelSelectEl = document.getElementById("set_hydra_llm_model_select");
|
| 18294 |
const hydraBaseModelLabelEl = document.getElementById("hydra-base-model-label");
|
| 18295 |
const hydraLocalModelStatusEl = document.getElementById("hydra-local-model-status");
|
| 18296 |
+
const llamaChatTemplateEditEl = document.getElementById("settings-llama-chat-template-edit");
|
| 18297 |
+
const llamaChatTemplateStatusEl = document.getElementById("settings-llama-chat-template-status");
|
| 18298 |
const contextControlConfig = {
|
| 18299 |
hf_transformers: {
|
| 18300 |
min: 256,
|
|
|
|
| 18510 |
llamaCppMtpDraftNumberEl?.addEventListener("input", () => syncLlamaCppMtpDraftControl(llamaCppMtpDraftNumberEl));
|
| 18511 |
llamaCppMtpDraftNumberEl?.addEventListener("blur", () => syncLlamaCppMtpDraftControl(llamaCppMtpDraftNumberEl));
|
| 18512 |
syncLlamaCppMtpDraftControl();
|
| 18513 |
+
const syncPairedIntegerControl = ({ rangeEl, numberEl, fallback = 0, min = 0, max = 8192, step = 1 }) => {
|
| 18514 |
+
if (!rangeEl || !numberEl) {
|
| 18515 |
+
return;
|
| 18516 |
+
}
|
| 18517 |
+
const applyValue = (sourceEl = null) => {
|
| 18518 |
+
const raw = Number(sourceEl?.value || numberEl.value || rangeEl.value || fallback);
|
| 18519 |
+
let value = Number.isFinite(raw) ? Math.round(raw) : fallback;
|
| 18520 |
+
value = Math.max(min, Math.min(max, value));
|
| 18521 |
+
if (step > 1 && value > min) {
|
| 18522 |
+
value = Math.round(value / step) * step;
|
| 18523 |
+
value = Math.max(min, Math.min(max, value));
|
| 18524 |
+
}
|
| 18525 |
+
rangeEl.value = String(value);
|
| 18526 |
+
numberEl.value = String(value);
|
| 18527 |
+
};
|
| 18528 |
+
rangeEl.addEventListener("input", () => applyValue(rangeEl));
|
| 18529 |
+
numberEl.addEventListener("input", () => applyValue(numberEl));
|
| 18530 |
+
numberEl.addEventListener("blur", () => applyValue(numberEl));
|
| 18531 |
+
applyValue();
|
| 18532 |
+
};
|
| 18533 |
+
syncPairedIntegerControl({
|
| 18534 |
+
rangeEl: document.getElementById("set_hydra_llama_cpp_n_batch_range"),
|
| 18535 |
+
numberEl: document.getElementById("set_hydra_llama_cpp_n_batch"),
|
| 18536 |
+
fallback: 512,
|
| 18537 |
+
min: 32,
|
| 18538 |
+
max: 8192,
|
| 18539 |
+
step: 32,
|
| 18540 |
+
});
|
| 18541 |
+
syncPairedIntegerControl({
|
| 18542 |
+
rangeEl: document.getElementById("set_hydra_llama_cpp_n_ubatch_range"),
|
| 18543 |
+
numberEl: document.getElementById("set_hydra_llama_cpp_n_ubatch"),
|
| 18544 |
+
fallback: 0,
|
| 18545 |
+
min: 0,
|
| 18546 |
+
max: 8192,
|
| 18547 |
+
step: 32,
|
| 18548 |
+
});
|
| 18549 |
const normalizeHydraBaseRowInput = (row) => ({
|
| 18550 |
provider: normalizeHydraBaseProvider(row?.provider || ""),
|
| 18551 |
host: String(row?.host || "").trim(),
|
|
|
|
| 18645 |
}
|
| 18646 |
return String(hydraBaseModelEl?.value || "").trim();
|
| 18647 |
};
|
| 18648 |
+
const syncLlamaChatTemplateTools = () => {
|
| 18649 |
+
const provider = normalizeHydraBaseProvider(hydraBaseProviderEl?.value || "");
|
| 18650 |
+
const local = isHydraLocalProvider(provider);
|
| 18651 |
+
const model = local ? getHydraBaseModelValue() : "";
|
| 18652 |
+
if (llamaChatTemplateEditEl) {
|
| 18653 |
+
llamaChatTemplateEditEl.disabled = !model;
|
| 18654 |
+
}
|
| 18655 |
+
if (llamaChatTemplateStatusEl) {
|
| 18656 |
+
const providerLabel = provider === "llama_cpp" ? "GGUF" : provider === "mlx_lm" ? "MLX" : "Transformers";
|
| 18657 |
+
llamaChatTemplateStatusEl.textContent = !local
|
| 18658 |
+
? ""
|
| 18659 |
+
: model
|
| 18660 |
+
? `Per-model ${providerLabel} template override.`
|
| 18661 |
+
: `Select a downloaded ${providerLabel} model first.`;
|
| 18662 |
+
}
|
| 18663 |
+
};
|
| 18664 |
const refreshLocalLlmModels = async ({ selectModel = "", provider = "" } = {}) => {
|
| 18665 |
try {
|
| 18666 |
localLlmModelsPayload = await api("/api/settings/local-llm/models", { _timeoutMs: HEALTH_REQUEST_TIMEOUT_MS });
|
|
|
|
| 18723 |
syncHydraProviderScopedFields(hydraBaseFieldsEl, provider);
|
| 18724 |
syncHydraModelInputForProvider(hydraBaseModelEl, hydraBaseModelLabelEl, provider);
|
| 18725 |
syncHydraPrimaryModelControl(provider);
|
| 18726 |
+
syncLlamaChatTemplateTools();
|
| 18727 |
};
|
| 18728 |
const hydraRouteControls = [];
|
| 18729 |
const syncHydraRouteControl = (control, preferredModel = "") => {
|
|
|
|
| 19021 |
syncActiveHydraContextControl();
|
| 19022 |
renderHydraContextEstimateCards();
|
| 19023 |
syncVisionLlamaContextControl();
|
| 19024 |
+
syncLlamaChatTemplateTools();
|
| 19025 |
+
});
|
| 19026 |
+
llamaChatTemplateEditEl?.addEventListener("click", () => {
|
| 19027 |
+
const provider = normalizeHydraBaseProvider(hydraBaseProviderEl?.value || "");
|
| 19028 |
+
const model = getHydraBaseModelValue();
|
| 19029 |
+
void openLocalLlmChatTemplateModal(provider, model, {
|
| 19030 |
+
onSaved: () => {
|
| 19031 |
+
void refreshLocalLlmModels({ provider, selectModel: model });
|
| 19032 |
+
syncLlamaChatTemplateTools();
|
| 19033 |
+
},
|
| 19034 |
+
});
|
| 19035 |
});
|
| 19036 |
syncHydraPrimaryProviderFields();
|
| 19037 |
if (state.hydraContextEstimateRuntimeListener) {
|
|
|
|
| 19085 |
const localModelManagerStatusEl = document.getElementById("local-model-manager-status");
|
| 19086 |
const localModelManagerListEl = document.getElementById("local-model-manager-list");
|
| 19087 |
const localModelManagerRefreshEl = document.getElementById("local-model-manager-refresh");
|
| 19088 |
+
const llmDebugConsoleEl = document.getElementById("settings-llm-debug-console");
|
| 19089 |
+
const llmDebugStatusEl = document.getElementById("llm-debug-status");
|
| 19090 |
+
const llmDebugActiveEl = document.getElementById("llm-debug-active");
|
| 19091 |
+
const llmDebugEventsEl = document.getElementById("llm-debug-events");
|
| 19092 |
+
const llmDebugRefreshEl = document.getElementById("llm-debug-refresh");
|
| 19093 |
+
const llmDebugAutoscrollEl = document.getElementById("llm-debug-autoscroll");
|
| 19094 |
+
const llmDebugClearEl = document.getElementById("llm-debug-clear");
|
| 19095 |
const hfModelBrowserViewButtons = Array.from(root.querySelectorAll("[data-hf-model-view]"));
|
| 19096 |
const hfModelBrowserState = {
|
| 19097 |
provider: isHydraLocalProvider(hydraPrimaryBaseRow.provider) ? hydraPrimaryBaseRow.provider : "hf_transformers",
|
| 19098 |
task: "text-generation",
|
| 19099 |
+
view: "picks",
|
| 19100 |
query: "",
|
| 19101 |
loaded: false,
|
| 19102 |
loadingSeq: 0,
|
|
|
|
| 19122 |
const hfModelTaskLabel = (task) => normalizeHfModelTask(task) === "image-text-to-text" ? "vision" : "text";
|
| 19123 |
const normalizeHfModelView = (value) => {
|
| 19124 |
const token = String(value || "trending").trim().toLowerCase().replace(/_/g, "-");
|
| 19125 |
+
if (["picks", "tater", "tater-picks", "recommended"].includes(token)) {
|
| 19126 |
+
return "picks";
|
| 19127 |
+
}
|
| 19128 |
if (token === "new" || token === "recent" || token === "latest") {
|
| 19129 |
return "new";
|
| 19130 |
}
|
|
|
|
| 19135 |
};
|
| 19136 |
const hfModelViewLabel = (value) => {
|
| 19137 |
const view = normalizeHfModelView(value);
|
| 19138 |
+
if (view === "picks") {
|
| 19139 |
+
return "Tater Picks";
|
| 19140 |
+
}
|
| 19141 |
if (view === "new") {
|
| 19142 |
return "new";
|
| 19143 |
}
|
|
|
|
| 19234 |
</div>
|
| 19235 |
${pathLabel ? `<div class="local-model-path">${escapeHtml(pathLabel)}</div>` : ""}
|
| 19236 |
<div class="local-model-actions">
|
| 19237 |
+
<button type="button" class="inline-btn" data-local-model-template data-provider="${escapeHtml(row.provider)}" data-model="${escapeHtml(row.model)}">Chat Template</button>
|
| 19238 |
<button type="button" class="inline-btn danger" data-local-model-delete data-provider="${escapeHtml(row.provider)}" data-model="${escapeHtml(row.model)}">Delete</button>
|
| 19239 |
</div>
|
| 19240 |
</article>
|
|
|
|
| 19355 |
const library = String(model?.library_name || model?.pipeline_tag || "").trim();
|
| 19356 |
const compatible = Boolean(model?.compatible);
|
| 19357 |
const supportsVision = Boolean(model?.supports_vision) || normalizeHfModelTask(model?.task) === "image-text-to-text" || normalizeHfModelTask(hfModelBrowserState.task) === "image-text-to-text";
|
| 19358 |
+
const isTaterPick = Boolean(model?.tater_pick);
|
| 19359 |
+
const taterPickLabel = String(model?.tater_pick_label || "Tater Pick").trim();
|
| 19360 |
+
const taterPickNote = String(model?.tater_pick_note || "").trim();
|
| 19361 |
const isMlxProvider = provider === "mlx_lm";
|
| 19362 |
const downloadLabel = isMlxProvider ? "Download Repo" : "Download";
|
| 19363 |
return `
|
| 19364 |
+
<article class="hf-model-card ${compatible ? "" : "is-uncertain"} ${isTaterPick ? "is-tater-pick" : ""}" role="button" tabindex="0" aria-label="Show files for ${escapeHtml(id)}" data-hf-model-card data-provider="${escapeHtml(provider)}" data-repo-id="${escapeHtml(id)}">
|
| 19365 |
<div class="hf-model-card-head">
|
| 19366 |
<div>
|
| 19367 |
<strong>${escapeHtml(id)}</strong>
|
| 19368 |
<span>${escapeHtml(library || hfModelProviderLabel(provider))}</span>
|
| 19369 |
</div>
|
| 19370 |
+
<span class="${isTaterPick ? "hf-model-tater-pill" : "hf-model-provider-pill"}">${escapeHtml(isTaterPick ? taterPickLabel : hfModelProviderLabel(provider))}</span>
|
| 19371 |
</div>
|
| 19372 |
<div class="hf-model-card-meta">
|
| 19373 |
${modelSize ? `<span class="hf-model-size-pill">${escapeHtml(modelSize)}</span>` : ""}
|
| 19374 |
${supportsVision ? `<span class="hf-model-vision-pill" title="Supports vision models">Vision</span>` : ""}
|
| 19375 |
+
${isTaterPick && taterPickNote ? `<span class="hf-model-pick-note">${escapeHtml(taterPickNote)}</span>` : ""}
|
| 19376 |
<span>${escapeHtml(downloads)} downloads</span>
|
| 19377 |
<span>${escapeHtml(likes)} likes</span>
|
| 19378 |
${updated ? `<span>${escapeHtml(updated)}</span>` : ""}
|
|
|
|
| 19480 |
hfModelBrowserState.provider = provider;
|
| 19481 |
hfModelBrowserState.task = task;
|
| 19482 |
hfModelBrowserState.query = query;
|
| 19483 |
+
const viewLabel = hfModelViewLabel(hfModelBrowserState.view);
|
| 19484 |
+
const viewIsPicks = normalizeHfModelView(hfModelBrowserState.view) === "picks";
|
| 19485 |
+
setHfModelBrowserStatus(
|
| 19486 |
+
viewIsPicks
|
| 19487 |
+
? `Loading ${viewLabel} ${hfModelProviderLabel(provider)} ${hfModelTaskLabel(task)} models...`
|
| 19488 |
+
: `Loading ${viewLabel} ${hfModelProviderLabel(provider)} ${hfModelTaskLabel(task)} models page ${hfModelBrowserState.pageIndex + 1}...`
|
| 19489 |
+
);
|
| 19490 |
if (hfModelBrowserResultsEl) {
|
| 19491 |
hfModelBrowserResultsEl.innerHTML = `<div class="hf-model-browser-empty">Loading models...</div>`;
|
| 19492 |
}
|
|
|
|
| 19513 |
}
|
| 19514 |
renderHfModelCards(payload?.models || []);
|
| 19515 |
renderHfModelBrowserPagination();
|
| 19516 |
+
setHfModelBrowserStatus(
|
| 19517 |
+
viewIsPicks
|
| 19518 |
+
? `${(payload?.models || []).length} ${viewLabel} ${hfModelProviderLabel(provider)} model${(payload?.models || []).length === 1 ? "" : "s"} loaded.`
|
| 19519 |
+
: `Page ${hfModelBrowserState.pageIndex + 1}: ${(payload?.models || []).length} models loaded.`,
|
| 19520 |
+
"success"
|
| 19521 |
+
);
|
| 19522 |
} catch (error) {
|
| 19523 |
if (seq !== hfModelBrowserState.loadingSeq) {
|
| 19524 |
return;
|
|
|
|
| 19665 |
showToast(`Model delete failed: ${error.message}`, "error", 3600);
|
| 19666 |
}
|
| 19667 |
};
|
| 19668 |
+
const llmDebugTimeLabel = (value) => {
|
| 19669 |
+
const seconds = Number(value || 0);
|
| 19670 |
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
| 19671 |
+
return "--:--";
|
| 19672 |
+
}
|
| 19673 |
+
try {
|
| 19674 |
+
return new Date(seconds * 1000).toLocaleTimeString([], { hour: "numeric", minute: "2-digit", second: "2-digit" });
|
| 19675 |
+
} catch {
|
| 19676 |
+
return "--:--";
|
| 19677 |
+
}
|
| 19678 |
+
};
|
| 19679 |
+
const llmDebugDurationLabel = (value) => {
|
| 19680 |
+
const ms = Number(value || 0);
|
| 19681 |
+
if (!Number.isFinite(ms) || ms <= 0) {
|
| 19682 |
+
return "";
|
| 19683 |
+
}
|
| 19684 |
+
if (ms >= 1000) {
|
| 19685 |
+
return `${(ms / 1000).toFixed(ms >= 10000 ? 1 : 2)}s`;
|
| 19686 |
+
}
|
| 19687 |
+
return `${Math.round(ms)}ms`;
|
| 19688 |
+
};
|
| 19689 |
+
const llmDebugTokenLabel = (event) => {
|
| 19690 |
+
const promptTokens = Math.max(0, Number(event?.prompt_tokens || 0));
|
| 19691 |
+
const completionTokens = Math.max(0, Number(event?.completion_tokens || 0));
|
| 19692 |
+
const totalTokens = Math.max(0, Number(event?.total_tokens || 0));
|
| 19693 |
+
const parts = [];
|
| 19694 |
+
if (promptTokens > 0) {
|
| 19695 |
+
parts.push(`in ${Math.round(promptTokens).toLocaleString()}`);
|
| 19696 |
+
}
|
| 19697 |
+
if (completionTokens > 0) {
|
| 19698 |
+
parts.push(`out ${Math.round(completionTokens).toLocaleString()}`);
|
| 19699 |
+
}
|
| 19700 |
+
if (!parts.length && totalTokens > 0) {
|
| 19701 |
+
parts.push(`${Math.round(totalTokens).toLocaleString()} tok`);
|
| 19702 |
+
}
|
| 19703 |
+
return parts.join(" / ");
|
| 19704 |
+
};
|
| 19705 |
+
const normalizeLlmDebugEvent = (row) => ({
|
| 19706 |
+
id: Math.max(0, Number(row?.id || 0)),
|
| 19707 |
+
ts: Number(row?.ts || row?.timestamp || 0),
|
| 19708 |
+
level: String(row?.level || "info").trim().toLowerCase() || "info",
|
| 19709 |
+
phase: String(row?.phase || "event").trim().toLowerCase() || "event",
|
| 19710 |
+
message: String(row?.message || "").trim(),
|
| 19711 |
+
detail: String(row?.detail || "").trim(),
|
| 19712 |
+
output: String(row?.output || "").trim(),
|
| 19713 |
+
provider: String(row?.provider || "").trim(),
|
| 19714 |
+
host: String(row?.host || "").trim(),
|
| 19715 |
+
model: String(row?.model || "").trim(),
|
| 19716 |
+
activity: String(row?.activity || "").trim(),
|
| 19717 |
+
source_label: String(row?.source_label || "").trim(),
|
| 19718 |
+
source: String(row?.source || "").trim(),
|
| 19719 |
+
kind: String(row?.kind || "").trim(),
|
| 19720 |
+
call_id: String(row?.call_id || "").trim(),
|
| 19721 |
+
prompt_tokens: Math.max(0, Number(row?.prompt_tokens || 0)),
|
| 19722 |
+
completion_tokens: Math.max(0, Number(row?.completion_tokens || 0)),
|
| 19723 |
+
total_tokens: Math.max(0, Number(row?.total_tokens || 0)),
|
| 19724 |
+
duration_ms: Math.max(0, Number(row?.duration_ms || 0)),
|
| 19725 |
+
});
|
| 19726 |
+
const renderLlmDebugActiveCalls = (rows = []) => {
|
| 19727 |
+
if (!llmDebugActiveEl) {
|
| 19728 |
+
return;
|
| 19729 |
+
}
|
| 19730 |
+
const calls = Array.isArray(rows) ? rows : [];
|
| 19731 |
+
if (!calls.length) {
|
| 19732 |
+
llmDebugActiveEl.innerHTML = `<div class="llm-debug-active-empty">No active LLM calls.</div>`;
|
| 19733 |
+
return;
|
| 19734 |
+
}
|
| 19735 |
+
llmDebugActiveEl.innerHTML = calls
|
| 19736 |
+
.map((call) => {
|
| 19737 |
+
const model = String(call?.model || "model").trim();
|
| 19738 |
+
const source = String(call?.source_label || call?.source || "unknown").trim();
|
| 19739 |
+
const activity = String(call?.activity || "llm").trim();
|
| 19740 |
+
const age = Math.max(0, Number(call?.age_seconds || 0));
|
| 19741 |
+
const host = String(call?.host || "").trim();
|
| 19742 |
+
const messages = Math.max(0, Number(call?.message_count || 0));
|
| 19743 |
+
return `
|
| 19744 |
+
<div class="llm-debug-active-call">
|
| 19745 |
+
<strong>${escapeHtml(activity)}</strong>
|
| 19746 |
+
<span>${escapeHtml(model)}</span>
|
| 19747 |
+
<small>${escapeHtml(source)}${host ? ` · ${escapeHtml(host)}` : ""} · ${messages} messages · ${Math.round(age)}s</small>
|
| 19748 |
+
</div>
|
| 19749 |
+
`;
|
| 19750 |
+
})
|
| 19751 |
+
.join("");
|
| 19752 |
+
};
|
| 19753 |
+
const renderLlmDebugEvents = () => {
|
| 19754 |
+
if (!llmDebugEventsEl) {
|
| 19755 |
+
return;
|
| 19756 |
+
}
|
| 19757 |
+
const rows = Array.isArray(state.llmDebugEvents) ? state.llmDebugEvents.slice(-220) : [];
|
| 19758 |
+
if (!rows.length) {
|
| 19759 |
+
llmDebugEventsEl.innerHTML = `<div class="llm-debug-empty">No LLM debug events yet.</div>`;
|
| 19760 |
+
return;
|
| 19761 |
+
}
|
| 19762 |
+
const shouldScroll =
|
| 19763 |
+
state.llmDebugAutoScroll &&
|
| 19764 |
+
Math.abs(llmDebugEventsEl.scrollHeight - llmDebugEventsEl.clientHeight - llmDebugEventsEl.scrollTop) < 72;
|
| 19765 |
+
llmDebugEventsEl.innerHTML = rows
|
| 19766 |
+
.map((event) => {
|
| 19767 |
+
const phase = event.phase || event.level || "event";
|
| 19768 |
+
const meta = [
|
| 19769 |
+
event.provider,
|
| 19770 |
+
event.source_label || event.source,
|
| 19771 |
+
event.activity,
|
| 19772 |
+
event.model,
|
| 19773 |
+
llmDebugTokenLabel(event),
|
| 19774 |
+
llmDebugDurationLabel(event.duration_ms),
|
| 19775 |
+
].filter(Boolean);
|
| 19776 |
+
return `
|
| 19777 |
+
<article class="llm-debug-line ${escapeHtml(event.level)} phase-${escapeHtml(phase)}">
|
| 19778 |
+
<span class="llm-debug-time">${escapeHtml(llmDebugTimeLabel(event.ts))}</span>
|
| 19779 |
+
<span class="llm-debug-phase">${escapeHtml(phase.replace(/_/g, " "))}</span>
|
| 19780 |
+
<div class="llm-debug-body">
|
| 19781 |
+
<strong>${escapeHtml(event.message || phase)}</strong>
|
| 19782 |
+
${meta.length ? `<small>${escapeHtml(meta.join(" · "))}</small>` : ""}
|
| 19783 |
+
${event.detail ? `<pre>${escapeHtml(event.detail)}</pre>` : ""}
|
| 19784 |
+
${event.output ? `<pre class="llm-debug-output">${escapeHtml(event.output)}</pre>` : ""}
|
| 19785 |
+
</div>
|
| 19786 |
+
</article>
|
| 19787 |
+
`;
|
| 19788 |
+
})
|
| 19789 |
+
.join("");
|
| 19790 |
+
if (shouldScroll) {
|
| 19791 |
+
llmDebugEventsEl.scrollTop = llmDebugEventsEl.scrollHeight;
|
| 19792 |
+
}
|
| 19793 |
+
};
|
| 19794 |
+
const setLlmDebugStatus = (message, tone = "") => {
|
| 19795 |
+
if (!llmDebugStatusEl) {
|
| 19796 |
+
return;
|
| 19797 |
+
}
|
| 19798 |
+
llmDebugStatusEl.textContent = String(message || "");
|
| 19799 |
+
llmDebugStatusEl.classList.toggle("error", tone === "error");
|
| 19800 |
+
llmDebugStatusEl.classList.toggle("success", tone === "success");
|
| 19801 |
+
};
|
| 19802 |
+
const renderLlmDebugSnapshot = (payload = {}, { reset = false } = {}) => {
|
| 19803 |
+
state.llmDebugLastSnapshot = payload && typeof payload === "object" ? payload : {};
|
| 19804 |
+
const events = Array.isArray(payload?.events) ? payload.events.map((row) => normalizeLlmDebugEvent(row)).filter((row) => row.id > 0) : [];
|
| 19805 |
+
if (reset) {
|
| 19806 |
+
state.llmDebugEvents = events;
|
| 19807 |
+
} else if (events.length) {
|
| 19808 |
+
const merged = new Map((Array.isArray(state.llmDebugEvents) ? state.llmDebugEvents : []).map((row) => [Number(row.id), row]));
|
| 19809 |
+
events.forEach((event) => merged.set(Number(event.id), event));
|
| 19810 |
+
state.llmDebugEvents = Array.from(merged.values()).sort((a, b) => Number(a.id) - Number(b.id)).slice(-260);
|
| 19811 |
+
}
|
| 19812 |
+
state.llmDebugNextId = Math.max(Number(state.llmDebugNextId || 0), Number(payload?.next_id || 0));
|
| 19813 |
+
renderLlmDebugActiveCalls(Array.isArray(payload?.active_calls) ? payload.active_calls : []);
|
| 19814 |
+
renderLlmDebugEvents();
|
| 19815 |
+
const activeTotal = Number(payload?.summary?.active_total ?? (payload?.active_calls || []).length ?? 0);
|
| 19816 |
+
const eventCount = Array.isArray(state.llmDebugEvents) ? state.llmDebugEvents.length : 0;
|
| 19817 |
+
setLlmDebugStatus(`${Math.max(0, activeTotal)} active LLM call${activeTotal === 1 ? "" : "s"} · ${eventCount} visible event${eventCount === 1 ? "" : "s"}`, "success");
|
| 19818 |
+
};
|
| 19819 |
+
const updateLlmDebugAutoscrollButton = () => {
|
| 19820 |
+
if (llmDebugAutoscrollEl) {
|
| 19821 |
+
llmDebugAutoscrollEl.textContent = state.llmDebugAutoScroll ? "Auto-scroll On" : "Auto-scroll Off";
|
| 19822 |
+
llmDebugAutoscrollEl.classList.toggle("active", Boolean(state.llmDebugAutoScroll));
|
| 19823 |
+
}
|
| 19824 |
+
};
|
| 19825 |
+
const refreshLlmDebugConsole = async ({ reset = false } = {}) => {
|
| 19826 |
+
if (
|
| 19827 |
+
!llmDebugConsoleEl ||
|
| 19828 |
+
!document.body.contains(llmDebugConsoleEl) ||
|
| 19829 |
+
document.body.dataset.view !== "settings" ||
|
| 19830 |
+
state.llmDebugInFlight
|
| 19831 |
+
) {
|
| 19832 |
+
return;
|
| 19833 |
+
}
|
| 19834 |
+
state.llmDebugInFlight = true;
|
| 19835 |
+
try {
|
| 19836 |
+
const sinceId = reset ? 0 : Math.max(0, Number(state.llmDebugNextId || 0));
|
| 19837 |
+
const params = new URLSearchParams({ since_id: String(sinceId), limit: "220" });
|
| 19838 |
+
const payload = await api(`/api/runtime/llm/debug?${params.toString()}`, { _skipRedisRecovery: true, _timeoutMs: HEALTH_REQUEST_TIMEOUT_MS });
|
| 19839 |
+
renderLlmDebugSnapshot(payload, { reset });
|
| 19840 |
+
} catch (error) {
|
| 19841 |
+
setLlmDebugStatus(`Debug refresh failed: ${error.message}`, "error");
|
| 19842 |
+
} finally {
|
| 19843 |
+
state.llmDebugInFlight = false;
|
| 19844 |
+
}
|
| 19845 |
+
};
|
| 19846 |
+
const scheduleLlmDebugPoll = (delayMs = 1200) => {
|
| 19847 |
+
clearLlmDebugPollTimer();
|
| 19848 |
+
if (
|
| 19849 |
+
!llmDebugConsoleEl ||
|
| 19850 |
+
!document.body.contains(llmDebugConsoleEl) ||
|
| 19851 |
+
document.body.dataset.view !== "settings" ||
|
| 19852 |
+
llmDebugConsoleEl.hidden
|
| 19853 |
+
) {
|
| 19854 |
+
return;
|
| 19855 |
+
}
|
| 19856 |
+
state.llmDebugPollTimer = window.setTimeout(async () => {
|
| 19857 |
+
state.llmDebugPollTimer = 0;
|
| 19858 |
+
await refreshLlmDebugConsole();
|
| 19859 |
+
scheduleLlmDebugPoll(1200);
|
| 19860 |
+
}, Math.max(250, Number(delayMs || 1200)));
|
| 19861 |
+
};
|
| 19862 |
const activateLlmVisionTab = (tab) => {
|
| 19863 |
const normalized = String(tab || "settings").trim() || "settings";
|
| 19864 |
const browserActive = normalized === "huggingface";
|
| 19865 |
const manageActive = normalized === "manage";
|
| 19866 |
+
const debugActive = normalized === "debug";
|
| 19867 |
llmVisionTabButtons.forEach((button) => {
|
| 19868 |
button.classList.toggle("active", String(button.dataset.llmVisionTab || "") === normalized);
|
| 19869 |
});
|
| 19870 |
root.querySelectorAll(".llm-vision-settings-block").forEach((block) => {
|
| 19871 |
+
block.classList.toggle("llm-vision-hidden", browserActive || manageActive || debugActive);
|
| 19872 |
});
|
| 19873 |
if (hfModelBrowserEl) {
|
| 19874 |
hfModelBrowserEl.hidden = !browserActive;
|
|
|
|
| 19878 |
localModelManagerEl.hidden = !manageActive;
|
| 19879 |
localModelManagerEl.classList.toggle("active", manageActive);
|
| 19880 |
}
|
| 19881 |
+
if (llmDebugConsoleEl) {
|
| 19882 |
+
llmDebugConsoleEl.hidden = !debugActive;
|
| 19883 |
+
llmDebugConsoleEl.classList.toggle("active", debugActive);
|
| 19884 |
+
}
|
| 19885 |
+
if (!debugActive) {
|
| 19886 |
+
clearLlmDebugPollTimer();
|
| 19887 |
+
}
|
| 19888 |
if (browserActive) {
|
| 19889 |
renderHfDownloadSummary(state.hfLlmWarmupLastSnapshot || {});
|
| 19890 |
scheduleHfLlmWarmupPoll(150);
|
|
|
|
| 19893 |
renderLocalModelManager();
|
| 19894 |
void refreshLocalLlmModels();
|
| 19895 |
}
|
| 19896 |
+
if (debugActive) {
|
| 19897 |
+
updateLlmDebugAutoscrollButton();
|
| 19898 |
+
renderLlmDebugSnapshot(state.llmDebugLastSnapshot || {}, { reset: false });
|
| 19899 |
+
void refreshLlmDebugConsole({ reset: state.llmDebugNextId <= 0 });
|
| 19900 |
+
scheduleLlmDebugPoll(800);
|
| 19901 |
+
}
|
| 19902 |
if (browserActive && !hfModelBrowserState.loaded) {
|
| 19903 |
if (hfModelBrowserProviderEl) {
|
| 19904 |
hfModelBrowserProviderEl.value = isHydraLocalProvider(hydraBaseProviderEl?.value || "")
|
|
|
|
| 19911 |
llmVisionTabButtons.forEach((button) => {
|
| 19912 |
button.addEventListener("click", () => activateLlmVisionTab(button.dataset.llmVisionTab || "settings"));
|
| 19913 |
});
|
| 19914 |
+
llmDebugRefreshEl?.addEventListener("click", () => {
|
| 19915 |
+
void refreshLlmDebugConsole({ reset: true });
|
| 19916 |
+
scheduleLlmDebugPoll(1000);
|
| 19917 |
+
});
|
| 19918 |
+
llmDebugAutoscrollEl?.addEventListener("click", () => {
|
| 19919 |
+
state.llmDebugAutoScroll = !state.llmDebugAutoScroll;
|
| 19920 |
+
safeStorageSet("tater_llm_debug_auto_scroll", state.llmDebugAutoScroll ? "true" : "false");
|
| 19921 |
+
updateLlmDebugAutoscrollButton();
|
| 19922 |
+
if (state.llmDebugAutoScroll && llmDebugEventsEl) {
|
| 19923 |
+
llmDebugEventsEl.scrollTop = llmDebugEventsEl.scrollHeight;
|
| 19924 |
+
}
|
| 19925 |
+
});
|
| 19926 |
+
llmDebugClearEl?.addEventListener("click", () => {
|
| 19927 |
+
state.llmDebugEvents = [];
|
| 19928 |
+
renderLlmDebugEvents();
|
| 19929 |
+
setLlmDebugStatus("Debug view cleared. New events will continue to appear.");
|
| 19930 |
+
});
|
| 19931 |
hfModelBrowserViewButtons.forEach((button) => {
|
| 19932 |
button.addEventListener("click", () => {
|
| 19933 |
hfModelBrowserState.view = normalizeHfModelView(button.dataset.hfModelView || "trending");
|
|
|
|
| 20064 |
void refreshLocalLlmModels();
|
| 20065 |
});
|
| 20066 |
localModelManagerListEl?.addEventListener("click", (event) => {
|
| 20067 |
+
const templateButton = event.target instanceof Element ? event.target.closest("[data-local-model-template]") : null;
|
| 20068 |
+
if (templateButton) {
|
| 20069 |
+
const provider = normalizeHydraBaseProvider(templateButton.getAttribute("data-provider") || "");
|
| 20070 |
+
const model = templateButton.getAttribute("data-model") || "";
|
| 20071 |
+
if (isHydraLocalProvider(provider)) {
|
| 20072 |
+
void openLocalLlmChatTemplateModal(provider, model, {
|
| 20073 |
+
onSaved: () => {
|
| 20074 |
+
void refreshLocalLlmModels({ provider, selectModel: model });
|
| 20075 |
+
syncLlamaChatTemplateTools();
|
| 20076 |
+
},
|
| 20077 |
+
});
|
| 20078 |
+
}
|
| 20079 |
+
return;
|
| 20080 |
+
}
|
| 20081 |
const button = event.target instanceof Element ? event.target.closest("[data-local-model-delete]") : null;
|
| 20082 |
if (!button) {
|
| 20083 |
return;
|
|
|
|
| 21490 |
const baseModel = getHydraBaseModelValue();
|
| 21491 |
const baseApiKey = String(document.getElementById("set_hydra_llm_api_key")?.value || "").trim();
|
| 21492 |
const hfTransformersContextTokens = String(document.getElementById("set_hydra_hf_transformers_context_tokens")?.value || "").trim();
|
| 21493 |
+
const hfTransformersDevice = String(document.getElementById("set_hydra_hf_transformers_device")?.value || "auto").trim();
|
| 21494 |
+
const hfTransformersDtype = String(document.getElementById("set_hydra_hf_transformers_dtype")?.value || "auto").trim();
|
| 21495 |
+
const hfTransformersDeviceMap = String(document.getElementById("set_hydra_hf_transformers_device_map")?.value || "default").trim();
|
| 21496 |
+
const hfTransformersAttnImplementation = String(document.getElementById("set_hydra_hf_transformers_attn_implementation")?.value || "auto").trim();
|
| 21497 |
+
const hfTransformersTrustRemoteCode = Boolean(document.getElementById("set_hydra_hf_transformers_trust_remote_code")?.checked);
|
| 21498 |
const llamaCppContextTokens = String(document.getElementById("set_hydra_llama_cpp_context_tokens")?.value || "").trim();
|
| 21499 |
const llamaCppMtpEnabled = Boolean(document.getElementById("set_hydra_llama_cpp_mtp_enabled")?.checked);
|
| 21500 |
const llamaCppMtpDraftTokens = String(document.getElementById("set_hydra_llama_cpp_mtp_draft_tokens")?.value || "3").trim();
|
| 21501 |
+
const llamaCppNBatch = String(document.getElementById("set_hydra_llama_cpp_n_batch")?.value || "512").trim();
|
| 21502 |
+
const llamaCppNUbatch = String(document.getElementById("set_hydra_llama_cpp_n_ubatch")?.value || "0").trim();
|
| 21503 |
+
const llamaCppFlashAttn = Boolean(document.getElementById("set_hydra_llama_cpp_flash_attn")?.checked);
|
| 21504 |
+
const llamaCppOffloadKqv = Boolean(document.getElementById("set_hydra_llama_cpp_offload_kqv")?.checked);
|
| 21505 |
const mlxLmContextTokens = String(document.getElementById("set_hydra_mlx_lm_context_tokens")?.value || "").trim();
|
| 21506 |
+
const mlxLmTrustRemoteCode = Boolean(document.getElementById("set_hydra_mlx_lm_trust_remote_code")?.checked);
|
| 21507 |
+
const mlxLmLazyLoad = Boolean(document.getElementById("set_hydra_mlx_lm_lazy_load")?.checked);
|
| 21508 |
+
const mlxEnginePrefillStepSize = String(document.getElementById("set_hydra_mlx_engine_prefill_step_size")?.value || "").trim();
|
| 21509 |
+
const mlxEngineKvBits = String(document.getElementById("set_hydra_mlx_engine_kv_bits")?.value || "").trim();
|
| 21510 |
+
const mlxEngineKvGroupSize = String(document.getElementById("set_hydra_mlx_engine_kv_group_size")?.value || "").trim();
|
| 21511 |
+
const mlxEngineQuantizedKvStart = String(document.getElementById("set_hydra_mlx_engine_quantized_kv_start")?.value || "").trim();
|
| 21512 |
const additionalBaseRows = readHydraAdditionalBaseRows();
|
| 21513 |
const hydraBaseServersPayload = [
|
| 21514 |
normalizeHydraBaseRowInput({ provider: baseProvider, host: baseHost, port: basePort, model: baseModel, api_key: baseApiKey }),
|
|
|
|
| 21526 |
hydra_llm_model: baseModel,
|
| 21527 |
hydra_llm_api_key: baseApiKey,
|
| 21528 |
hydra_hf_transformers_context_tokens: hfTransformersContextTokens,
|
| 21529 |
+
hydra_hf_transformers_device: hfTransformersDevice,
|
| 21530 |
+
hydra_hf_transformers_dtype: hfTransformersDtype,
|
| 21531 |
+
hydra_hf_transformers_device_map: hfTransformersDeviceMap,
|
| 21532 |
+
hydra_hf_transformers_attn_implementation: hfTransformersAttnImplementation,
|
| 21533 |
+
hydra_hf_transformers_trust_remote_code: hfTransformersTrustRemoteCode,
|
| 21534 |
hydra_llama_cpp_context_tokens: llamaCppContextTokens,
|
| 21535 |
hydra_llama_cpp_mtp_enabled: llamaCppMtpEnabled,
|
| 21536 |
hydra_llama_cpp_mtp_draft_tokens: llamaCppMtpDraftTokens,
|
| 21537 |
+
hydra_llama_cpp_n_batch: llamaCppNBatch,
|
| 21538 |
+
hydra_llama_cpp_n_ubatch: llamaCppNUbatch,
|
| 21539 |
+
hydra_llama_cpp_flash_attn: llamaCppFlashAttn,
|
| 21540 |
+
hydra_llama_cpp_offload_kqv: llamaCppOffloadKqv,
|
| 21541 |
hydra_mlx_lm_context_tokens: mlxLmContextTokens,
|
| 21542 |
+
hydra_mlx_lm_trust_remote_code: mlxLmTrustRemoteCode,
|
| 21543 |
+
hydra_mlx_lm_lazy_load: mlxLmLazyLoad,
|
| 21544 |
+
hydra_mlx_engine_prefill_step_size: mlxEnginePrefillStepSize,
|
| 21545 |
+
hydra_mlx_engine_kv_bits: mlxEngineKvBits,
|
| 21546 |
+
hydra_mlx_engine_kv_group_size: mlxEngineKvGroupSize,
|
| 21547 |
+
hydra_mlx_engine_quantized_kv_start: mlxEngineQuantizedKvStart,
|
| 21548 |
hydra_base_servers: hydraBaseServersPayload,
|
| 21549 |
},
|
| 21550 |
};
|
|
|
|
| 24209 |
if (state.view !== "spudex") {
|
| 24210 |
clearSpudexPollTimer();
|
| 24211 |
}
|
| 24212 |
+
if (state.view !== "settings") {
|
| 24213 |
+
clearLlmDebugPollTimer();
|
| 24214 |
+
}
|
| 24215 |
document.body.dataset.view = String(viewName || "").trim().toLowerCase();
|
| 24216 |
setActiveNav(viewName);
|
| 24217 |
updateHeader();
|
|
|
|
| 24300 |
}
|
| 24301 |
clearDashboardRefreshTimer();
|
| 24302 |
clearSpudexPollTimer();
|
| 24303 |
+
clearLlmDebugPollTimer();
|
| 24304 |
closeChatEventSource();
|
| 24305 |
stopAllChatJobPolling();
|
| 24306 |
stopRuntimeBreakdownPolling();
|
tater_reachy_standalone/vendor/tater_app/tateros_static/styles.css
CHANGED
|
@@ -2291,6 +2291,154 @@ body[data-popup-effect="melt"] .app-shell.sidebar-expanding .sidebar::after {
|
|
| 2291 |
min-width: 0;
|
| 2292 |
}
|
| 2293 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2294 |
.hf-model-card,
|
| 2295 |
.local-model-card,
|
| 2296 |
.hf-model-browser-detail {
|
|
@@ -2310,19 +2458,52 @@ body[data-popup-effect="melt"] .app-shell.sidebar-expanding .sidebar::after {
|
|
| 2310 |
|
| 2311 |
.hf-model-card {
|
| 2312 |
cursor: pointer;
|
| 2313 |
-
|
|
|
|
| 2314 |
}
|
| 2315 |
|
| 2316 |
.hf-model-card.is-uncertain {
|
| 2317 |
border-color: rgba(255, 255, 255, 0.075);
|
| 2318 |
}
|
| 2319 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2320 |
.hf-model-card:hover,
|
| 2321 |
.hf-model-card:focus-visible {
|
| 2322 |
border-color: rgba(255, 151, 83, 0.38);
|
| 2323 |
background: linear-gradient(180deg, rgba(35, 34, 34, 0.98), rgba(24, 24, 25, 0.97));
|
| 2324 |
}
|
| 2325 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2326 |
.hf-model-card:focus-visible {
|
| 2327 |
outline: 2px solid rgba(255, 151, 83, 0.42);
|
| 2328 |
outline-offset: 2px;
|
|
@@ -2369,6 +2550,7 @@ body[data-popup-effect="melt"] .app-shell.sidebar-expanding .sidebar::after {
|
|
| 2369 |
}
|
| 2370 |
|
| 2371 |
.hf-model-provider-pill,
|
|
|
|
| 2372 |
.hf-model-tags span {
|
| 2373 |
flex: 0 0 auto;
|
| 2374 |
border: 1px solid rgba(255, 151, 83, 0.24);
|
|
@@ -2383,6 +2565,14 @@ body[data-popup-effect="melt"] .app-shell.sidebar-expanding .sidebar::after {
|
|
| 2383 |
padding: 5px 8px;
|
| 2384 |
}
|
| 2385 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2386 |
.hf-model-card-meta {
|
| 2387 |
flex-wrap: wrap;
|
| 2388 |
color: #aaa198;
|
|
@@ -2408,6 +2598,11 @@ body[data-popup-effect="melt"] .app-shell.sidebar-expanding .sidebar::after {
|
|
| 2408 |
font-weight: 850;
|
| 2409 |
}
|
| 2410 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2411 |
.hf-model-tags {
|
| 2412 |
display: flex;
|
| 2413 |
flex-wrap: wrap;
|
|
@@ -2439,6 +2634,19 @@ body[data-popup-effect="melt"] .app-shell.sidebar-expanding .sidebar::after {
|
|
| 2439 |
justify-content: flex-end;
|
| 2440 |
}
|
| 2441 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2442 |
.local-model-path {
|
| 2443 |
overflow-wrap: anywhere;
|
| 2444 |
border: 1px solid rgba(255, 255, 255, 0.06);
|
|
@@ -2733,6 +2941,110 @@ body[data-popup-effect="melt"] .app-shell.sidebar-expanding .sidebar::after {
|
|
| 2733 |
font-size: 0.68rem;
|
| 2734 |
}
|
| 2735 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2736 |
.hf-model-browser-empty {
|
| 2737 |
grid-column: 1 / -1;
|
| 2738 |
padding: 12px;
|
|
@@ -2770,6 +3082,18 @@ body[data-popup-effect="melt"] .app-shell.sidebar-expanding .sidebar::after {
|
|
| 2770 |
grid-template-columns: 1fr;
|
| 2771 |
}
|
| 2772 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2773 |
.hf-download-summary {
|
| 2774 |
grid-template-columns: auto minmax(0, 1fr);
|
| 2775 |
}
|
|
|
|
| 2291 |
min-width: 0;
|
| 2292 |
}
|
| 2293 |
|
| 2294 |
+
.llm-debug-console-shell {
|
| 2295 |
+
min-width: 0;
|
| 2296 |
+
}
|
| 2297 |
+
|
| 2298 |
+
.llm-debug-hero {
|
| 2299 |
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
| 2300 |
+
}
|
| 2301 |
+
|
| 2302 |
+
.llm-debug-actions {
|
| 2303 |
+
display: flex;
|
| 2304 |
+
flex-wrap: wrap;
|
| 2305 |
+
justify-content: flex-end;
|
| 2306 |
+
gap: 8px;
|
| 2307 |
+
}
|
| 2308 |
+
|
| 2309 |
+
.llm-debug-actions .inline-btn.active {
|
| 2310 |
+
border-color: rgba(214, 90, 31, 0.52);
|
| 2311 |
+
background: rgba(214, 90, 31, 0.16);
|
| 2312 |
+
color: var(--accent-soft);
|
| 2313 |
+
}
|
| 2314 |
+
|
| 2315 |
+
.llm-debug-active {
|
| 2316 |
+
display: grid;
|
| 2317 |
+
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
| 2318 |
+
gap: 8px;
|
| 2319 |
+
}
|
| 2320 |
+
|
| 2321 |
+
.llm-debug-active-empty,
|
| 2322 |
+
.llm-debug-empty {
|
| 2323 |
+
padding: 12px;
|
| 2324 |
+
border: 1px solid var(--line-soft);
|
| 2325 |
+
border-radius: 8px;
|
| 2326 |
+
background: rgba(255, 255, 255, 0.035);
|
| 2327 |
+
color: var(--text-muted);
|
| 2328 |
+
font-size: 0.82rem;
|
| 2329 |
+
}
|
| 2330 |
+
|
| 2331 |
+
.llm-debug-active-call {
|
| 2332 |
+
display: grid;
|
| 2333 |
+
gap: 3px;
|
| 2334 |
+
min-width: 0;
|
| 2335 |
+
padding: 10px;
|
| 2336 |
+
border: 1px solid rgba(255, 151, 83, 0.22);
|
| 2337 |
+
border-radius: 8px;
|
| 2338 |
+
background:
|
| 2339 |
+
linear-gradient(180deg, rgba(31, 32, 34, 0.98), rgba(20, 21, 23, 0.97)),
|
| 2340 |
+
linear-gradient(135deg, rgba(214, 90, 31, 0.1), rgba(92, 155, 220, 0.06));
|
| 2341 |
+
}
|
| 2342 |
+
|
| 2343 |
+
.llm-debug-active-call strong {
|
| 2344 |
+
color: #f9efe7;
|
| 2345 |
+
font-size: 0.86rem;
|
| 2346 |
+
line-height: 1.2;
|
| 2347 |
+
}
|
| 2348 |
+
|
| 2349 |
+
.llm-debug-active-call span,
|
| 2350 |
+
.llm-debug-active-call small {
|
| 2351 |
+
min-width: 0;
|
| 2352 |
+
overflow-wrap: anywhere;
|
| 2353 |
+
color: #b8afa5;
|
| 2354 |
+
font-size: 0.76rem;
|
| 2355 |
+
line-height: 1.35;
|
| 2356 |
+
}
|
| 2357 |
+
|
| 2358 |
+
.llm-debug-events {
|
| 2359 |
+
max-height: min(58vh, 620px);
|
| 2360 |
+
min-height: 360px;
|
| 2361 |
+
overflow: auto;
|
| 2362 |
+
border: 1px solid rgba(240, 131, 69, 0.2);
|
| 2363 |
+
border-radius: 10px;
|
| 2364 |
+
background:
|
| 2365 |
+
linear-gradient(180deg, rgba(10, 11, 13, 0.96), rgba(8, 9, 11, 0.98)),
|
| 2366 |
+
repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.018) 0, rgba(255, 255, 255, 0.018) 1px, transparent 1px, transparent 24px);
|
| 2367 |
+
box-shadow: var(--shadow-soft);
|
| 2368 |
+
padding: 10px;
|
| 2369 |
+
color: #e5e9e4;
|
| 2370 |
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
| 2371 |
+
font-size: 0.78rem;
|
| 2372 |
+
line-height: 1.45;
|
| 2373 |
+
}
|
| 2374 |
+
|
| 2375 |
+
.llm-debug-line {
|
| 2376 |
+
display: grid;
|
| 2377 |
+
grid-template-columns: 88px 92px minmax(0, 1fr);
|
| 2378 |
+
gap: 10px;
|
| 2379 |
+
align-items: start;
|
| 2380 |
+
padding: 7px 0;
|
| 2381 |
+
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
| 2382 |
+
}
|
| 2383 |
+
|
| 2384 |
+
.llm-debug-time {
|
| 2385 |
+
color: #8f9aa5;
|
| 2386 |
+
white-space: nowrap;
|
| 2387 |
+
}
|
| 2388 |
+
|
| 2389 |
+
.llm-debug-phase {
|
| 2390 |
+
color: #f0a678;
|
| 2391 |
+
font-weight: 900;
|
| 2392 |
+
text-transform: lowercase;
|
| 2393 |
+
}
|
| 2394 |
+
|
| 2395 |
+
.llm-debug-line.success .llm-debug-phase,
|
| 2396 |
+
.llm-debug-line.phase-output .llm-debug-phase {
|
| 2397 |
+
color: #8ddfb1;
|
| 2398 |
+
}
|
| 2399 |
+
|
| 2400 |
+
.llm-debug-line.warning .llm-debug-phase {
|
| 2401 |
+
color: #ffd08a;
|
| 2402 |
+
}
|
| 2403 |
+
|
| 2404 |
+
.llm-debug-line.error .llm-debug-phase {
|
| 2405 |
+
color: #ff9b8e;
|
| 2406 |
+
}
|
| 2407 |
+
|
| 2408 |
+
.llm-debug-body {
|
| 2409 |
+
display: grid;
|
| 2410 |
+
gap: 3px;
|
| 2411 |
+
min-width: 0;
|
| 2412 |
+
}
|
| 2413 |
+
|
| 2414 |
+
.llm-debug-body strong {
|
| 2415 |
+
color: #f5efe7;
|
| 2416 |
+
font-size: 0.8rem;
|
| 2417 |
+
line-height: 1.35;
|
| 2418 |
+
}
|
| 2419 |
+
|
| 2420 |
+
.llm-debug-body small {
|
| 2421 |
+
color: #aeb8c2;
|
| 2422 |
+
font-size: 0.72rem;
|
| 2423 |
+
overflow-wrap: anywhere;
|
| 2424 |
+
}
|
| 2425 |
+
|
| 2426 |
+
.llm-debug-body pre {
|
| 2427 |
+
margin: 3px 0 0;
|
| 2428 |
+
white-space: pre-wrap;
|
| 2429 |
+
word-break: break-word;
|
| 2430 |
+
color: #ccd5dc;
|
| 2431 |
+
font: inherit;
|
| 2432 |
+
}
|
| 2433 |
+
|
| 2434 |
+
.llm-debug-body .llm-debug-output {
|
| 2435 |
+
padding: 8px;
|
| 2436 |
+
border-left: 2px solid rgba(141, 223, 177, 0.55);
|
| 2437 |
+
border-radius: 6px;
|
| 2438 |
+
background: rgba(141, 223, 177, 0.07);
|
| 2439 |
+
color: #e7f4ec;
|
| 2440 |
+
}
|
| 2441 |
+
|
| 2442 |
.hf-model-card,
|
| 2443 |
.local-model-card,
|
| 2444 |
.hf-model-browser-detail {
|
|
|
|
| 2458 |
|
| 2459 |
.hf-model-card {
|
| 2460 |
cursor: pointer;
|
| 2461 |
+
position: relative;
|
| 2462 |
+
transition: border-color 0.16s ease, transform 0.16s ease, background 0.16s ease, box-shadow 0.16s ease;
|
| 2463 |
}
|
| 2464 |
|
| 2465 |
.hf-model-card.is-uncertain {
|
| 2466 |
border-color: rgba(255, 255, 255, 0.075);
|
| 2467 |
}
|
| 2468 |
|
| 2469 |
+
.hf-model-card.is-tater-pick {
|
| 2470 |
+
border-color: rgba(255, 151, 83, 0.74);
|
| 2471 |
+
background:
|
| 2472 |
+
radial-gradient(circle at 8% 0%, rgba(255, 151, 83, 0.18), transparent 34%),
|
| 2473 |
+
linear-gradient(180deg, rgba(37, 33, 29, 0.99), rgba(24, 23, 23, 0.98));
|
| 2474 |
+
box-shadow:
|
| 2475 |
+
0 0 0 1px rgba(255, 151, 83, 0.28),
|
| 2476 |
+
0 0 24px rgba(255, 129, 55, 0.24),
|
| 2477 |
+
0 12px 32px rgba(0, 0, 0, 0.38);
|
| 2478 |
+
}
|
| 2479 |
+
|
| 2480 |
+
.hf-model-card.is-tater-pick::before {
|
| 2481 |
+
content: "";
|
| 2482 |
+
position: absolute;
|
| 2483 |
+
inset: -2px;
|
| 2484 |
+
z-index: -1;
|
| 2485 |
+
border-radius: 10px;
|
| 2486 |
+
background: linear-gradient(135deg, rgba(255, 151, 83, 0.42), rgba(255, 194, 104, 0.08), rgba(255, 151, 83, 0.28));
|
| 2487 |
+
filter: blur(8px);
|
| 2488 |
+
opacity: 0.72;
|
| 2489 |
+
pointer-events: none;
|
| 2490 |
+
}
|
| 2491 |
+
|
| 2492 |
.hf-model-card:hover,
|
| 2493 |
.hf-model-card:focus-visible {
|
| 2494 |
border-color: rgba(255, 151, 83, 0.38);
|
| 2495 |
background: linear-gradient(180deg, rgba(35, 34, 34, 0.98), rgba(24, 24, 25, 0.97));
|
| 2496 |
}
|
| 2497 |
|
| 2498 |
+
.hf-model-card.is-tater-pick:hover,
|
| 2499 |
+
.hf-model-card.is-tater-pick:focus-visible {
|
| 2500 |
+
border-color: rgba(255, 176, 102, 0.9);
|
| 2501 |
+
box-shadow:
|
| 2502 |
+
0 0 0 1px rgba(255, 151, 83, 0.34),
|
| 2503 |
+
0 0 32px rgba(255, 129, 55, 0.34),
|
| 2504 |
+
0 16px 36px rgba(0, 0, 0, 0.42);
|
| 2505 |
+
}
|
| 2506 |
+
|
| 2507 |
.hf-model-card:focus-visible {
|
| 2508 |
outline: 2px solid rgba(255, 151, 83, 0.42);
|
| 2509 |
outline-offset: 2px;
|
|
|
|
| 2550 |
}
|
| 2551 |
|
| 2552 |
.hf-model-provider-pill,
|
| 2553 |
+
.hf-model-tater-pill,
|
| 2554 |
.hf-model-tags span {
|
| 2555 |
flex: 0 0 auto;
|
| 2556 |
border: 1px solid rgba(255, 151, 83, 0.24);
|
|
|
|
| 2565 |
padding: 5px 8px;
|
| 2566 |
}
|
| 2567 |
|
| 2568 |
+
.hf-model-tater-pill {
|
| 2569 |
+
padding: 5px 9px;
|
| 2570 |
+
border-color: rgba(255, 176, 102, 0.58);
|
| 2571 |
+
background: linear-gradient(180deg, rgba(255, 151, 83, 0.24), rgba(255, 129, 55, 0.14));
|
| 2572 |
+
color: #ffd2a8;
|
| 2573 |
+
box-shadow: 0 0 14px rgba(255, 129, 55, 0.2);
|
| 2574 |
+
}
|
| 2575 |
+
|
| 2576 |
.hf-model-card-meta {
|
| 2577 |
flex-wrap: wrap;
|
| 2578 |
color: #aaa198;
|
|
|
|
| 2598 |
font-weight: 850;
|
| 2599 |
}
|
| 2600 |
|
| 2601 |
+
.hf-model-pick-note {
|
| 2602 |
+
color: #ffc086;
|
| 2603 |
+
font-weight: 800;
|
| 2604 |
+
}
|
| 2605 |
+
|
| 2606 |
.hf-model-tags {
|
| 2607 |
display: flex;
|
| 2608 |
flex-wrap: wrap;
|
|
|
|
| 2634 |
justify-content: flex-end;
|
| 2635 |
}
|
| 2636 |
|
| 2637 |
+
.hydra-chat-template-tools {
|
| 2638 |
+
display: flex;
|
| 2639 |
+
grid-column: 1 / -1;
|
| 2640 |
+
flex-wrap: wrap;
|
| 2641 |
+
align-items: center;
|
| 2642 |
+
gap: 8px;
|
| 2643 |
+
min-width: 0;
|
| 2644 |
+
}
|
| 2645 |
+
|
| 2646 |
+
.hydra-chat-template-tools .small {
|
| 2647 |
+
color: #a99f95;
|
| 2648 |
+
}
|
| 2649 |
+
|
| 2650 |
.local-model-path {
|
| 2651 |
overflow-wrap: anywhere;
|
| 2652 |
border: 1px solid rgba(255, 255, 255, 0.06);
|
|
|
|
| 2941 |
font-size: 0.68rem;
|
| 2942 |
}
|
| 2943 |
|
| 2944 |
+
.llama-chat-template-dialog {
|
| 2945 |
+
width: min(980px, 96vw);
|
| 2946 |
+
border-color: rgba(240, 131, 69, 0.18);
|
| 2947 |
+
background:
|
| 2948 |
+
linear-gradient(180deg, rgba(28, 29, 30, 0.98), rgba(18, 19, 20, 0.985)),
|
| 2949 |
+
radial-gradient(circle at top left, rgba(240, 131, 69, 0.1), transparent 42%);
|
| 2950 |
+
}
|
| 2951 |
+
|
| 2952 |
+
.llama-chat-template-body {
|
| 2953 |
+
display: grid;
|
| 2954 |
+
gap: 12px;
|
| 2955 |
+
}
|
| 2956 |
+
|
| 2957 |
+
.llama-chat-template-summary {
|
| 2958 |
+
display: grid;
|
| 2959 |
+
gap: 8px;
|
| 2960 |
+
padding: 10px;
|
| 2961 |
+
border: 1px solid rgba(255, 151, 83, 0.14);
|
| 2962 |
+
border-radius: 8px;
|
| 2963 |
+
background: rgba(0, 0, 0, 0.18);
|
| 2964 |
+
}
|
| 2965 |
+
|
| 2966 |
+
.llama-chat-template-summary-row {
|
| 2967 |
+
display: grid;
|
| 2968 |
+
gap: 3px;
|
| 2969 |
+
}
|
| 2970 |
+
|
| 2971 |
+
.llama-chat-template-summary-row strong {
|
| 2972 |
+
color: #f5e9dc;
|
| 2973 |
+
font-size: 0.88rem;
|
| 2974 |
+
line-height: 1.25;
|
| 2975 |
+
}
|
| 2976 |
+
|
| 2977 |
+
.llama-chat-template-summary-row span {
|
| 2978 |
+
overflow-wrap: anywhere;
|
| 2979 |
+
color: #a99f95;
|
| 2980 |
+
font-size: 0.74rem;
|
| 2981 |
+
}
|
| 2982 |
+
|
| 2983 |
+
.llama-chat-template-badges,
|
| 2984 |
+
.llama-chat-template-meta,
|
| 2985 |
+
.llama-chat-template-actions {
|
| 2986 |
+
display: flex;
|
| 2987 |
+
flex-wrap: wrap;
|
| 2988 |
+
align-items: center;
|
| 2989 |
+
gap: 7px;
|
| 2990 |
+
}
|
| 2991 |
+
|
| 2992 |
+
.llama-template-badge,
|
| 2993 |
+
.llama-chat-template-meta span {
|
| 2994 |
+
border: 1px solid rgba(255, 255, 255, 0.07);
|
| 2995 |
+
border-radius: 999px;
|
| 2996 |
+
background: rgba(255, 255, 255, 0.035);
|
| 2997 |
+
color: #a99f95;
|
| 2998 |
+
font-size: 0.7rem;
|
| 2999 |
+
font-weight: 750;
|
| 3000 |
+
line-height: 1.2;
|
| 3001 |
+
padding: 4px 8px;
|
| 3002 |
+
}
|
| 3003 |
+
|
| 3004 |
+
.llama-template-badge.active {
|
| 3005 |
+
border-color: rgba(103, 204, 138, 0.28);
|
| 3006 |
+
background: rgba(78, 188, 132, 0.12);
|
| 3007 |
+
color: #a9edc4;
|
| 3008 |
+
}
|
| 3009 |
+
|
| 3010 |
+
.llama-chat-template-editor {
|
| 3011 |
+
width: 100%;
|
| 3012 |
+
min-height: 360px;
|
| 3013 |
+
max-height: min(56vh, 640px);
|
| 3014 |
+
resize: vertical;
|
| 3015 |
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
| 3016 |
+
border-radius: 8px;
|
| 3017 |
+
background: rgba(4, 5, 7, 0.5);
|
| 3018 |
+
color: #efe6dc;
|
| 3019 |
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
| 3020 |
+
font-size: 0.78rem;
|
| 3021 |
+
line-height: 1.45;
|
| 3022 |
+
padding: 11px;
|
| 3023 |
+
}
|
| 3024 |
+
|
| 3025 |
+
.llama-chat-template-editor:focus {
|
| 3026 |
+
border-color: rgba(255, 151, 83, 0.42);
|
| 3027 |
+
outline: none;
|
| 3028 |
+
box-shadow: 0 0 0 2px rgba(255, 151, 83, 0.12);
|
| 3029 |
+
}
|
| 3030 |
+
|
| 3031 |
+
.llama-chat-template-actions {
|
| 3032 |
+
justify-content: flex-end;
|
| 3033 |
+
}
|
| 3034 |
+
|
| 3035 |
+
.llama-chat-template-status {
|
| 3036 |
+
min-height: 1.2em;
|
| 3037 |
+
color: #a99f95;
|
| 3038 |
+
}
|
| 3039 |
+
|
| 3040 |
+
.llama-chat-template-status.success {
|
| 3041 |
+
color: #a9edc4;
|
| 3042 |
+
}
|
| 3043 |
+
|
| 3044 |
+
.llama-chat-template-status.error {
|
| 3045 |
+
color: #ffb2a8;
|
| 3046 |
+
}
|
| 3047 |
+
|
| 3048 |
.hf-model-browser-empty {
|
| 3049 |
grid-column: 1 / -1;
|
| 3050 |
padding: 12px;
|
|
|
|
| 3082 |
grid-template-columns: 1fr;
|
| 3083 |
}
|
| 3084 |
|
| 3085 |
+
.llm-debug-actions {
|
| 3086 |
+
justify-content: flex-start;
|
| 3087 |
+
}
|
| 3088 |
+
|
| 3089 |
+
.llm-debug-line {
|
| 3090 |
+
grid-template-columns: 72px minmax(0, 1fr);
|
| 3091 |
+
}
|
| 3092 |
+
|
| 3093 |
+
.llm-debug-body {
|
| 3094 |
+
grid-column: 1 / -1;
|
| 3095 |
+
}
|
| 3096 |
+
|
| 3097 |
.hf-download-summary {
|
| 3098 |
grid-template-columns: auto minmax(0, 1fr);
|
| 3099 |
}
|
tater_voice_sat/__init__.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
"""Tater voice satellite for Reachy."""
|
| 2 |
|
| 3 |
-
__version__ = "0.1.
|
| 4 |
-
|
|
|
|
| 1 |
"""Tater voice satellite for Reachy."""
|
| 2 |
|
| 3 |
+
__version__ = "0.1.1"
|
|
|
tater_voice_sat/sdk_app.py
CHANGED
|
@@ -210,8 +210,8 @@ def _resolve_app_config_path(config_path: str) -> Path:
|
|
| 210 |
|
| 211 |
runtime_dir = Path.home() / ".tater_voice_sat"
|
| 212 |
runtime_config = runtime_dir / requested.name
|
| 213 |
-
|
| 214 |
-
|
| 215 |
return runtime_config.resolve()
|
| 216 |
|
| 217 |
|
|
@@ -253,5 +253,28 @@ def _merge_wake_word_entries(*entry_groups: Any) -> list[dict[str, str]]:
|
|
| 253 |
return list(merged.values())
|
| 254 |
|
| 255 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 256 |
if __name__ == "__main__":
|
| 257 |
main()
|
|
|
|
| 210 |
|
| 211 |
runtime_dir = Path.home() / ".tater_voice_sat"
|
| 212 |
runtime_config = runtime_dir / requested.name
|
| 213 |
+
_seed_runtime_assets(runtime_dir)
|
| 214 |
+
_migrate_default_wake_word(runtime_config)
|
| 215 |
return runtime_config.resolve()
|
| 216 |
|
| 217 |
|
|
|
|
| 253 |
return list(merged.values())
|
| 254 |
|
| 255 |
|
| 256 |
+
def _migrate_default_wake_word(config_path: Path) -> None:
|
| 257 |
+
try:
|
| 258 |
+
text = config_path.read_text(encoding="utf-8")
|
| 259 |
+
except OSError:
|
| 260 |
+
return
|
| 261 |
+
old_markers = (
|
| 262 |
+
'wake_word = "hey tater"',
|
| 263 |
+
'model = "hey_tater"',
|
| 264 |
+
'model_config = "./wakewords/hey_tater.json"',
|
| 265 |
+
)
|
| 266 |
+
if not all(marker in text for marker in old_markers):
|
| 267 |
+
return
|
| 268 |
+
updated = text
|
| 269 |
+
updated = updated.replace('wake_word = "hey tater"', 'wake_word = "hey reachy"', 1)
|
| 270 |
+
updated = updated.replace('model = "hey_tater"', 'model = "hey_reachy"', 1)
|
| 271 |
+
updated = updated.replace('model_config = "./wakewords/hey_tater.json"', 'model_config = "./wakewords/hey_reachy.json"', 1)
|
| 272 |
+
updated = updated.replace("threshold = 0.70", "threshold = 0.81", 1)
|
| 273 |
+
if updated == text:
|
| 274 |
+
return
|
| 275 |
+
config_path.write_text(updated, encoding="utf-8")
|
| 276 |
+
logger.info("migrated default wake word to hey_reachy")
|
| 277 |
+
|
| 278 |
+
|
| 279 |
if __name__ == "__main__":
|
| 280 |
main()
|