Compare commits
7 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a7d67df7a | |||
| 80f38c9443 | |||
| 6918d9417f | |||
| 9cbe6a165b | |||
| 886d941a65 | |||
| e7b5274af6 | |||
| 4b98744495 |
4 changed files with 64 additions and 48 deletions
|
|
@ -21,6 +21,7 @@ services:
|
||||||
# Ollama at http://host.docker.internal:11434.
|
# Ollama at http://host.docker.internal:11434.
|
||||||
- "host.docker.internal:host-gateway"
|
- "host.docker.internal:host-gateway"
|
||||||
environment:
|
environment:
|
||||||
|
- LD_LIBRARY_PATH=/app/.local/bin
|
||||||
- LLM_HOST=${LLM_HOST:-localhost}
|
- LLM_HOST=${LLM_HOST:-localhost}
|
||||||
- LLM_HOSTS=${LLM_HOSTS:-}
|
- LLM_HOSTS=${LLM_HOSTS:-}
|
||||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||||
|
|
@ -71,11 +72,14 @@ services:
|
||||||
- PUID=${PUID:-1000}
|
- PUID=${PUID:-1000}
|
||||||
- PGID=${PGID:-1000}
|
- PGID=${PGID:-1000}
|
||||||
depends_on:
|
depends_on:
|
||||||
searxng:
|
# searxng:
|
||||||
condition: service_healthy
|
# condition: service_healthy
|
||||||
chromadb:
|
chromadb:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- default
|
||||||
|
- searxng-net
|
||||||
|
|
||||||
chromadb:
|
chromadb:
|
||||||
image: docker.io/chromadb/chroma:latest
|
image: docker.io/chromadb/chroma:latest
|
||||||
|
|
@ -87,55 +91,55 @@ services:
|
||||||
- ANONYMIZED_TELEMETRY=FALSE
|
- ANONYMIZED_TELEMETRY=FALSE
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
searxng:
|
# searxng:
|
||||||
# Pinned, not :latest — odysseus waits on searxng's healthcheck
|
# Pinned, not :latest — odysseus waits on searxng's healthcheck
|
||||||
# (depends_on: condition: service_healthy), so a broken upstream `latest`
|
# (depends_on: condition: service_healthy), so a broken upstream `latest`
|
||||||
# tag blocks the whole app from starting. 2026.6.2 crashes on boot with
|
# tag blocks the whole app from starting. 2026.6.2 crashes on boot with
|
||||||
# `KeyError: 'default_doi_resolver'`, failing the healthcheck (issue #1414).
|
# `KeyError: 'default_doi_resolver'`, failing the healthcheck (issue #1414).
|
||||||
# Bump this deliberately after verifying a newer tag boots clean.
|
# Bump this deliberately after verifying a newer tag boots clean.
|
||||||
image: docker.io/searxng/searxng:2026.5.31-7159b8aed
|
# image: docker.io/searxng/searxng:2026.5.31-7159b8aed
|
||||||
entrypoint:
|
# entrypoint:
|
||||||
- /bin/sh
|
# - /bin/sh
|
||||||
- -c
|
# - -c
|
||||||
- |
|
# - |
|
||||||
set -eu
|
# set -eu
|
||||||
if [ ! -s /etc/searxng/settings.yml ] || grep -q 'odysseus-local-searxng-json-2026-05-30\|__SEARXNG_SECRET__' /etc/searxng/settings.yml; then
|
# if [ ! -s /etc/searxng/settings.yml ] || grep -q 'odysseus-local-searxng-json-2026-05-30\|__SEARXNG_SECRET__' /etc/searxng/settings.yml; then
|
||||||
secret="$${SEARXNG_SECRET:-}"
|
# secret="$${SEARXNG_SECRET:-}"
|
||||||
if [ -z "$$secret" ]; then
|
# if [ -z "$$secret" ]; then
|
||||||
secret="$$(python -c 'import secrets; print(secrets.token_urlsafe(48))')"
|
# secret="$$(python -c 'import secrets; print(secrets.token_urlsafe(48))')"
|
||||||
fi
|
# fi
|
||||||
sed "s|__SEARXNG_SECRET__|$$secret|g" /tmp/searxng-settings.yml.template > /etc/searxng/settings.yml
|
# sed "s|__SEARXNG_SECRET__|$$secret|g" /tmp/searxng-settings.yml.template > /etc/searxng/settings.yml
|
||||||
fi
|
# fi
|
||||||
exec /usr/local/searxng/entrypoint.sh
|
# exec /usr/local/searxng/entrypoint.sh
|
||||||
ports:
|
# ports:
|
||||||
- "127.0.0.1:8080:8080"
|
# - "127.0.0.1:8080:8080"
|
||||||
volumes:
|
# volumes:
|
||||||
- searxng-data:/etc/searxng
|
# - searxng-data:/etc/searxng
|
||||||
- ./config/searxng/settings.yml:/tmp/searxng-settings.yml.template:ro,z
|
# - ./config/searxng/settings.yml:/tmp/searxng-settings.yml.template:ro,z
|
||||||
environment:
|
# environment:
|
||||||
- SEARXNG_BASE_URL=http://localhost:8080/
|
# - SEARXNG_BASE_URL=http://localhost:8080/
|
||||||
- SEARXNG_SECRET=${SEARXNG_SECRET:-}
|
# restart: unless-stopped
|
||||||
# The official searxng image runs as the non-root `searxng` user, but its
|
# # The official searxng image runs as the non-root `searxng` user, but its
|
||||||
# entrypoint still needs to chown /etc/searxng on first boot, drop privs via
|
# # entrypoint still needs to chown /etc/searxng on first boot, drop privs via
|
||||||
# su-exec, and (with our wrapper above) write settings.yml into the named
|
# # su-exec, and (with our wrapper above) write settings.yml into the named
|
||||||
# volume. Without these capabilities the wrapper aborts at the redirection
|
# # volume. Without these capabilities the wrapper aborts at the redirection
|
||||||
# with EACCES and the container fails its healthcheck with permission
|
# # with EACCES and the container fails its healthcheck with permission
|
||||||
# errors during setup. Mirrors the cap set recommended by the upstream
|
# # errors during setup. Mirrors the cap set recommended by the upstream
|
||||||
# searxng-docker compose file. See issue #721.
|
# # searxng-docker compose file. See issue #721.
|
||||||
cap_drop:
|
# cap_drop:
|
||||||
- ALL
|
# - ALL
|
||||||
cap_add:
|
# cap_add:
|
||||||
- CHOWN
|
# - CHOWN
|
||||||
- SETGID
|
# - SETGID
|
||||||
- SETUID
|
# - SETUID
|
||||||
- DAC_OVERRIDE
|
# - DAC_OVERRIDE
|
||||||
healthcheck:
|
# healthcheck:
|
||||||
test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8080/', timeout=5).read(1)\""]
|
# test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8080/', timeout=5).read(1)\""]
|
||||||
interval: 5s
|
# interval: 5s
|
||||||
timeout: 6s
|
# timeout: 6s
|
||||||
retries: 20
|
# retries: 20
|
||||||
start_period: 10s
|
# start_period: 10s
|
||||||
restart: unless-stopped
|
# restart: unless-stopped
|
||||||
|
|
||||||
ntfy:
|
ntfy:
|
||||||
image: docker.io/binwiederhier/ntfy
|
image: docker.io/binwiederhier/ntfy
|
||||||
|
|
@ -149,6 +153,11 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
searxng-data:
|
# searxng-data:
|
||||||
chromadb-data:
|
chromadb-data:
|
||||||
ntfy-cache:
|
ntfy-cache:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
searxng-net:
|
||||||
|
external: true
|
||||||
|
name: searxng_default
|
||||||
|
|
|
||||||
|
|
@ -2696,6 +2696,9 @@ def setup_cookbook_routes() -> APIRouter:
|
||||||
elif disk_env.get("servers") and not inc_env.get("servers"):
|
elif disk_env.get("servers") and not inc_env.get("servers"):
|
||||||
inc_env["servers"] = disk_env["servers"]
|
inc_env["servers"] = disk_env["servers"]
|
||||||
logger.warning("cookbook state POST: incoming env.servers empty; preserved on-disk servers (anti-wipe guard)")
|
logger.warning("cookbook state POST: incoming env.servers empty; preserved on-disk servers (anti-wipe guard)")
|
||||||
|
if disk_env.get("hfToken") and not inc_env.get("hfToken"):
|
||||||
|
inc_env["hfToken"] = disk_env["hfToken"]
|
||||||
|
logger.debug("cookbook state POST: incoming env missing hfToken; preserved on-disk token (anti-wipe guard)")
|
||||||
|
|
||||||
disk_tasks = on_disk.get("tasks") or [] if isinstance(on_disk, dict) else []
|
disk_tasks = on_disk.get("tasks") or [] if isinstance(on_disk, dict) else []
|
||||||
incoming_tasks = data.get("tasks") if isinstance(data.get("tasks"), list) else []
|
incoming_tasks = data.get("tasks") if isinstance(data.get("tasks"), list) else []
|
||||||
|
|
|
||||||
|
|
@ -3036,7 +3036,7 @@ import { wireArrowUpRecall, getLastUserMessageFromChatHistory } from './composer
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Stop streaming TTS on any error/abort
|
// Stop streaming TTS on any error/abort
|
||||||
if (streamingTTS && window.aiTTSManager) window.aiTTSManager.stop();
|
if (window.aiTTSManager?.autoPlay && window.aiTTSManager?.available) window.aiTTSManager.stop();
|
||||||
|
|
||||||
if (currentAbort && currentAbort.signal.aborted) {
|
if (currentAbort && currentAbort.signal.aborted) {
|
||||||
const abortReason = currentAbort._reason || '';
|
const abortReason = currentAbort._reason || '';
|
||||||
|
|
|
||||||
|
|
@ -408,6 +408,10 @@ export function _parseServePhase(snapshot) {
|
||||||
if (/Ollama API ready on port\s+\d+/i.test(flat)) {
|
if (/Ollama API ready on port\s+\d+/i.test(flat)) {
|
||||||
return { phase: 'ready', status: 'ready' };
|
return { phase: 'ready', status: 'ready' };
|
||||||
}
|
}
|
||||||
|
// llama.cpp server (llama-server / llama-cli) — "server is listening" variants
|
||||||
|
if (/(?:server is listening|HTTP server listening|listening on (?:http|port))/i.test(flat)) {
|
||||||
|
return { phase: 'ready', status: 'ready' };
|
||||||
|
}
|
||||||
const llamaBuildMatches = [...flat.matchAll(/\[\s*(\d{1,3})%\]\s*(?:Building|Linking)/gi)];
|
const llamaBuildMatches = [...flat.matchAll(/\[\s*(\d{1,3})%\]\s*(?:Building|Linking)/gi)];
|
||||||
if (llamaBuildMatches.length) {
|
if (llamaBuildMatches.length) {
|
||||||
const pct = Math.min(100, parseInt(llamaBuildMatches[llamaBuildMatches.length - 1][1], 10));
|
const pct = Math.min(100, parseInt(llamaBuildMatches[llamaBuildMatches.length - 1][1], 10));
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue