Provision OpenWebUI workspace model presets
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
umask 077
|
||||
|
||||
CONTAINER=${OPENWEBUI_CONTAINER:-mike-ai-open-webui}
|
||||
VOLUME=${OPENWEBUI_VOLUME:-mike-ai_open-webui-data}
|
||||
|
||||
die() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; }
|
||||
[[ $EUID -eq 0 ]] || die "Bitte als root ausführen."
|
||||
|
||||
volume_path=$(docker volume inspect -f '{{.Mountpoint}}' "$VOLUME")
|
||||
db=$volume_path/webui.db
|
||||
[[ -s $db ]] || die "OpenWebUI-Datenbank fehlt: $db"
|
||||
|
||||
was_running=false
|
||||
if [[ $(docker inspect -f '{{.State.Running}}' "$CONTAINER" 2>/dev/null || true) == true ]]; then
|
||||
was_running=true
|
||||
docker stop "$CONTAINER" >/dev/null
|
||||
fi
|
||||
restart_on_exit() {
|
||||
if [[ $was_running == true ]]; then
|
||||
docker start "$CONTAINER" >/dev/null 2>&1 || true
|
||||
fi
|
||||
}
|
||||
trap restart_on_exit EXIT
|
||||
|
||||
stamp=$(date +%Y%m%d-%H%M%S)
|
||||
backup=$volume_path/webui.db.before-model-install-$stamp
|
||||
cp -a "$db" "$backup"
|
||||
rm -f "$volume_path/webui.db-wal" "$volume_path/webui.db-shm"
|
||||
|
||||
python3 - "$db" "${OPENWEBUI_MODEL_OWNER_ID:-}" <<'PY'
|
||||
import json
|
||||
import sqlite3
|
||||
import sys
|
||||
import time
|
||||
|
||||
db, requested_owner = sys.argv[1:]
|
||||
con = sqlite3.connect(db)
|
||||
columns = {row[1] for row in con.execute("pragma table_info(model)")}
|
||||
required = {
|
||||
"id", "user_id", "base_model_id", "name", "params", "meta",
|
||||
"is_active", "updated_at", "created_at",
|
||||
}
|
||||
if not required <= columns:
|
||||
raise SystemExit("Unbekanntes OpenWebUI-Modellschema; keine Änderung vorgenommen.")
|
||||
|
||||
owner = requested_owner
|
||||
if not owner:
|
||||
row = con.execute(
|
||||
"select user_id from model where id in "
|
||||
"('mikeai-fast','mikeai-medium','mikeai-large','mikeai-ultra',"
|
||||
"'mikeai---fast-72k') order by updated_at desc limit 1"
|
||||
).fetchone()
|
||||
if row:
|
||||
owner = row[0]
|
||||
if not owner:
|
||||
admins = con.execute("select id from user where role='admin'").fetchall()
|
||||
if len(admins) != 1:
|
||||
raise SystemExit(
|
||||
"Modelleigentümer ist nicht eindeutig; OPENWEBUI_MODEL_OWNER_ID setzen."
|
||||
)
|
||||
owner = admins[0][0]
|
||||
|
||||
now = int(time.time())
|
||||
filter_ids = [
|
||||
"reasoning_default_off",
|
||||
"thinking",
|
||||
"stability_guard",
|
||||
"secret_redaction",
|
||||
"local_performance_metrics",
|
||||
]
|
||||
|
||||
def capabilities(vision: bool) -> dict:
|
||||
return {
|
||||
"file_context": True,
|
||||
"vision": vision,
|
||||
"file_upload": True,
|
||||
"web_search": True,
|
||||
"image_generation": False,
|
||||
"code_interpreter": True,
|
||||
"terminal": True,
|
||||
"citations": True,
|
||||
"status_updates": True,
|
||||
"memory": True,
|
||||
"builtin_tools": True,
|
||||
}
|
||||
|
||||
profiles = [
|
||||
{
|
||||
"id": "mikeai-fast",
|
||||
"base": "qwen-fast",
|
||||
"name": "MikeAI · Fast · 76K",
|
||||
"description": (
|
||||
"Schnelles Alltags- und Agentenprofil: Qwen3.8-27B IQ4-MIX, "
|
||||
"76.800 Token, RTX 5080, MTP2 und Bildanalyse."
|
||||
),
|
||||
"vision": True,
|
||||
"tags": ["MikeAI", "Schnell", "Vision"],
|
||||
},
|
||||
{
|
||||
"id": "mikeai-medium",
|
||||
"base": "qwen-medium",
|
||||
"name": "MikeAI · Medium · 160K · Standard",
|
||||
"description": (
|
||||
"Verbindliches Standardprofil: Qwen3.8-27B IQ4_XS Pure, "
|
||||
"160.000 Token, RTX 5080 + RTX 3060 (90:10), MTP3 und Bildanalyse."
|
||||
),
|
||||
"vision": True,
|
||||
"tags": ["MikeAI", "Standard", "Vision"],
|
||||
},
|
||||
{
|
||||
"id": "mikeai-large",
|
||||
"base": "qwen-large",
|
||||
"name": "MikeAI · Large · 192K",
|
||||
"description": (
|
||||
"Großes Agenten- und MCP-Profil: Qwen3.8-27B IQ4_XS Pure, "
|
||||
"192.000 Token, RTX 5080 + RTX 3060 (86:14), MTP3 und Bildanalyse."
|
||||
),
|
||||
"vision": True,
|
||||
"tags": ["MikeAI", "Großer Kontext", "Vision"],
|
||||
},
|
||||
{
|
||||
"id": "mikeai-ultra",
|
||||
"base": "qwen-ultra",
|
||||
"name": "MikeAI · Ultra · 256K · Text",
|
||||
"description": (
|
||||
"Maximaler Textkontext: Qwen3.8-27B IQ4_XS Pure, 262.144 Token, "
|
||||
"RTX 5080 + RTX 3060 (80:20), MTP2; bewusst ohne Bildanalyse."
|
||||
),
|
||||
"vision": False,
|
||||
"tags": ["MikeAI", "Maximaler Kontext", "Nur Text"],
|
||||
},
|
||||
]
|
||||
|
||||
params = {
|
||||
"temperature": 0.2,
|
||||
"top_p": 0.8,
|
||||
"top_k": 20,
|
||||
"max_tokens": 32768,
|
||||
}
|
||||
|
||||
def upsert(model_id, base_model_id, name, model_params, meta, active=True):
|
||||
existing = con.execute(
|
||||
"select created_at from model where id=?", (model_id,)
|
||||
).fetchone()
|
||||
created_at = existing[0] if existing else now
|
||||
con.execute(
|
||||
"""
|
||||
insert into model
|
||||
(id,user_id,base_model_id,name,params,meta,is_active,updated_at,created_at)
|
||||
values (?,?,?,?,?,?,?,?,?)
|
||||
on conflict(id) do update set
|
||||
user_id=excluded.user_id,
|
||||
base_model_id=excluded.base_model_id,
|
||||
name=excluded.name,
|
||||
params=excluded.params,
|
||||
meta=excluded.meta,
|
||||
is_active=excluded.is_active,
|
||||
updated_at=excluded.updated_at
|
||||
""",
|
||||
(
|
||||
model_id, owner, base_model_id, name,
|
||||
json.dumps(model_params, ensure_ascii=False),
|
||||
json.dumps(meta, ensure_ascii=False),
|
||||
active, now, created_at,
|
||||
),
|
||||
)
|
||||
|
||||
with con:
|
||||
# Hide the raw router aliases while keeping them resolvable as bases for
|
||||
# the user-facing workspace presets.
|
||||
for raw_id in ("qwen-fast", "qwen-medium", "qwen-large", "qwen-ultra"):
|
||||
upsert(
|
||||
raw_id, None, raw_id, {},
|
||||
{
|
||||
"description": "Interner Router-Alias; bitte das zugehörige MikeAI-Profil verwenden.",
|
||||
"hidden": True,
|
||||
},
|
||||
)
|
||||
|
||||
for profile in profiles:
|
||||
meta = {
|
||||
"profile_image_url": "/static/favicon.png",
|
||||
"description": profile["description"],
|
||||
"capabilities": capabilities(profile["vision"]),
|
||||
"knowledge": None,
|
||||
"suggestion_prompts": None,
|
||||
"tags": [{"name": tag} for tag in profile["tags"]],
|
||||
# Features remain available but are not forced on every request;
|
||||
# this avoids needlessly injecting large tool schemas.
|
||||
"defaultFeatureIds": [],
|
||||
"filterIds": filter_ids,
|
||||
"actionIds": ["quick_actions"],
|
||||
"tts": {"voice": "alloy"},
|
||||
"hidden": False,
|
||||
}
|
||||
upsert(
|
||||
profile["id"], profile["base"], profile["name"], params, meta
|
||||
)
|
||||
|
||||
# Exact legacy entries from the experimental 72K/Long setup. The database
|
||||
# backup above makes the cleanup fully recoverable.
|
||||
con.execute(
|
||||
"delete from model where id in ('mikeai---fast-72k','qwen-long')"
|
||||
)
|
||||
con.execute(
|
||||
"""
|
||||
insert into config (key,value,updated_at) values (?,?,?)
|
||||
on conflict(key) do update set value=excluded.value, updated_at=excluded.updated_at
|
||||
""",
|
||||
("ui.default_models", json.dumps("mikeai-medium"), now),
|
||||
)
|
||||
|
||||
print("OpenWebUI-Modelle installiert: Fast, Medium (Standard), Large, Ultra")
|
||||
PY
|
||||
|
||||
printf 'Datenbanksicherung: %s\n' "$backup"
|
||||
Reference in New Issue
Block a user