release(v0.2.0): Marke „Alexis" + sichtbare Versionsnummer
- Anzeigename Voice Assistant -> Alexis (UI-Titel/Kopfzeile mit Untertitel „Ihr Sprachbegleiter", FastAPI-Titel, README/Handbuch). Technische Identifier (Dienst, Pfade, Paketname, Konfig-/DB-Dateinamen) bleiben unveraendert. - Version als EINE Quelle: pyproject.toml = 0.2.0; app.__version__ liest sie (pyproject zuerst, Paket-Metadaten als Fallback). Sichtbar in der UI-Kopfzeile, unter /api/config (version) und in OpenAPI. - CHANGELOG.md mit 0.2.0-Eintrag; Tests fuer /api/config.version + Branding. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
5e128a74ce
commit
9eb5c31eb6
11 changed files with 92 additions and 9 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# Voice Assistant Gateway — Handbuch
|
||||
# Alexis — Handbuch
|
||||
|
||||
> **Zielgruppen:** 👤 Endnutzer · 🔧 Admin/Betreiber · 💻 Entwickler
|
||||
>
|
||||
|
|
@ -68,7 +68,7 @@ funktioniert trotzdem, die Ausgabe ist dann unformatiert.
|
|||
|
||||
### 1.1 Überblick
|
||||
|
||||
Der **Voice Assistant Gateway** ist ein modulares Sprachassistenten-System. Er nimmt
|
||||
**Alexis** ist ein modulares Sprachassistenten-System. Es nimmt
|
||||
gesprochene oder getippte Eingaben entgegen, lässt sie von einer KI beantworten und
|
||||
liest die Antwort vor. Die drei KI-Stufen — **Spracherkennung (STT)**, **Sprachmodell (LLM)**
|
||||
und **Sprachsynthese (TTS)** — sind einzeln austauschbar: lokal oder in der Cloud,
|
||||
|
|
|
|||
33
CHANGELOG.md
Normal file
33
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Changelog
|
||||
|
||||
Alle nennenswerten Änderungen an **Alexis**. Format lose nach [Keep a Changelog](https://keepachangelog.com/de/);
|
||||
Versionierung nach [SemVer](https://semver.org/lang/de/) (prä-1.0: `0.x`).
|
||||
|
||||
## [0.2.0] — 2026-06-27
|
||||
|
||||
### Geändert
|
||||
- **Marke:** Produkt heißt jetzt **Alexis** (Anzeigename); technische Identifier
|
||||
(Dienst, Pfade, Paketname, Konfig-/DB-Dateinamen) bleiben unverändert.
|
||||
- Version wird im UI (Kopfzeile) und unter `/api/config` (`version`) angezeigt; eine
|
||||
Quelle der Wahrheit ist `pyproject.toml`.
|
||||
|
||||
### Hinzugefügt
|
||||
- **Aufgeräumtes Vorlese-Menü:** nicht nutzbare TTS-Provider werden ausgeblendet
|
||||
(statt ausgegraut). Backend liefert die fertige Sichtbarkeit (`tts_menu`),
|
||||
„Im Gerät" nur auf Mobilgeräten. `/api/config` wird serverseitig gecacht
|
||||
(spart die zuvor pro Seitenladen ausgelöste, kostenpflichtige OpenRouter-Probe).
|
||||
- **GPU-Erkennung:** Setting `GPUS` (`auto|none|"0,1,2"`), an `CUDA_VISIBLE_DEVICES`
|
||||
gekoppelt; zentrale Erkennung in `app/capabilities.py`.
|
||||
|
||||
### Behoben
|
||||
- **Stimm-Geschlechtswahl wirkt wieder:** `voice_gender` fehlte im `UserPrefs`-Schema
|
||||
und wurde still verworfen → es kam immer die weibliche Stimme. Feld ergänzt
|
||||
(+ Regressions-Test).
|
||||
- **Geräte-TTS** folgt der Geschlechtswahl (Heuristik über Stimmname/voiceURI), sonst
|
||||
Server-Fallback.
|
||||
|
||||
## [0.1.0]
|
||||
|
||||
- Erste interne Version: modulares Gateway (STT/LLM/TTS austauschbar), Profile,
|
||||
Provider-Registries, Notruf-Eskalation, Auto-Erinnerungen, Admin-Panel,
|
||||
Auth/SSO + Passwort-Login.
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Voice Assistant Gateway
|
||||
# Alexis — modulares Sprachassistenten-Gateway
|
||||
|
||||
Modulares FastAPI-Gateway für einen **seniorengerechten Sprachassistenten** —
|
||||
cloud-first, aber hybrid/lokal betreibbar, mit austauschbaren STT-/LLM-/TTS-Providern.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
"""Alexis — modulares Sprachassistenten-Gateway.
|
||||
|
||||
`__version__` ist die **eine** Versionsquelle für UI/OpenAPI/`/api/config`. Sie wird
|
||||
bevorzugt direkt aus `pyproject.toml` gelesen (dieses Repo ist ein editable In-Place-
|
||||
Deployment unter /opt/voice-assistant, ein `pip install -e .` nach jeder Versionsänderung
|
||||
entfällt damit); Fallback sind die installierten Paket-Metadaten, dann "0.0.0".
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def _detect_version() -> str:
|
||||
# 1) pyproject.toml im Quellbaum (maßgeblich für das In-Place-Deployment)
|
||||
try:
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
pyproject = Path(__file__).resolve().parent.parent / "pyproject.toml"
|
||||
with pyproject.open("rb") as fh:
|
||||
return tomllib.load(fh)["project"]["version"]
|
||||
except Exception:
|
||||
pass
|
||||
# 2) installierte Paket-Metadaten (gepackte Deployments ohne Quellbaum)
|
||||
try:
|
||||
from importlib.metadata import version
|
||||
|
||||
return version("voice-assistant-gateway")
|
||||
except Exception:
|
||||
return "0.0.0"
|
||||
|
||||
|
||||
__version__ = _detect_version()
|
||||
|
|
@ -4,6 +4,7 @@ import time
|
|||
import httpx
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app import __version__
|
||||
from app.config import settings, active_profile
|
||||
from app.capabilities import resolve_gpus
|
||||
from app.providers.tts.cartesia import _UNSUPPORTED_LANGS as _CARTESIA_UNSUPPORTED
|
||||
|
|
@ -94,6 +95,7 @@ async def _build_config() -> dict:
|
|||
{"provider": "device", "visible": True, "client_only": True},
|
||||
]
|
||||
return {
|
||||
"version": __version__,
|
||||
"profile": active_profile(),
|
||||
"app_env": settings.app_env,
|
||||
"default_route": route.as_dict(),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from fastapi import FastAPI, Request
|
|||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.responses import RedirectResponse, JSONResponse, HTMLResponse
|
||||
|
||||
from app import __version__
|
||||
from app.config import settings
|
||||
from urllib.parse import urlencode
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ async def _lifespan(app: FastAPI):
|
|||
task.cancel()
|
||||
|
||||
|
||||
app = FastAPI(title="Voice Assistant Gateway", lifespan=_lifespan)
|
||||
app = FastAPI(title="Alexis", version=__version__, lifespan=_lifespan)
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
|
|
|
|||
|
|
@ -290,6 +290,8 @@ async function refreshTtsMenu() {
|
|||
const cfg = await fetch("/api/config", { cache: "no-store" }).then((r) => r.json());
|
||||
_ttsMenu = cfg.tts_menu || null;
|
||||
cartesiaUnsupportedLangs = cfg.available?.cartesia_unsupported_languages || [];
|
||||
const vEl = document.getElementById("app-version");
|
||||
if (vEl && cfg.version) vEl.textContent = "v" + cfg.version;
|
||||
} catch (e) { /* offline -> nichts verstecken */ }
|
||||
applyTtsVisibility();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>Voice Assistant</title>
|
||||
<title>Alexis</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg?v=1" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png?v=1" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png?v=1" />
|
||||
|
|
@ -227,7 +227,7 @@
|
|||
<!-- ═══════════════════ CHAT INTERFACE ═══════════════════ -->
|
||||
<header class="shrink-0 h-14 flex items-center gap-2 px-3 border-b border-slate-200 dark:border-slate-700 bg-white/80 dark:bg-slate-800/80 backdrop-blur">
|
||||
<img src="/favicon.svg?v=1" alt="" class="w-7 h-7 shrink-0" />
|
||||
<h1 class="flex-1 truncate font-semibold text-base">Voice Assistant</h1>
|
||||
<h1 class="flex-1 truncate font-semibold text-base">Alexis<span class="hidden min-[420px]:inline font-normal text-slate-400 dark:text-slate-500 text-sm"> · Ihr Sprachbegleiter</span></h1>
|
||||
<button type="button" id="emergency-btn" title="Hilfe rufen" aria-label="Hilfe rufen"
|
||||
class="shrink-0 h-8 w-8 grid place-items-center rounded-lg bg-red-600 hover:bg-red-700 text-white font-extrabold text-sm leading-none shadow-sm transition-colors disabled:opacity-100">SOS</button>
|
||||
<select id="lang-sel" title="Sprache"
|
||||
|
|
@ -257,7 +257,10 @@
|
|||
<!-- Identitätsleiste: angemeldeter Nutzer (links) + Abmelden (rechts) -->
|
||||
<div class="shrink-0 flex items-center justify-between px-4 py-1 text-xs text-slate-500 dark:text-slate-400 border-b border-slate-200 dark:border-slate-700">
|
||||
<span id="identity">lade …</span>
|
||||
<a id="logout" class="hidden hover:underline" href="#">Abmelden</a>
|
||||
<div class="flex items-center gap-3">
|
||||
<span id="app-version" class="opacity-70 tabular-nums"></span>
|
||||
<a id="logout" class="hidden hover:underline" href="#">Abmelden</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Verstecktes Quell-Select: speist die Ton-Presets (bewahrt die bestehende Logik) -->
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "voice-assistant-gateway"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
description = "Modular voice assistant gateway with pluggable audio endpoints and provider adapters"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
|
|
|||
|
|
@ -115,3 +115,13 @@ def test_config_reports_gpus(monkeypatch):
|
|||
assert {m["provider"] for m in data["tts_menu"]} == {
|
||||
"piper", "chatterbox", "cartesia", "openrouter", "device"
|
||||
}
|
||||
|
||||
|
||||
def test_config_exposes_version(monkeypatch):
|
||||
import re
|
||||
import app
|
||||
|
||||
_patch_probes(monkeypatch, chatterbox=True, openrouter=True, cartesia=True, gpus=[])
|
||||
version = client.get("/api/config").json()["version"]
|
||||
assert re.match(r"^\d+\.\d+\.\d+", version)
|
||||
assert version == app.__version__
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ def test_static_index_served():
|
|||
# Auth aus (LAN-Dev) -> anonymer Nutzer -> Seite wird ausgeliefert.
|
||||
r = client.get("/")
|
||||
assert r.status_code == 200
|
||||
assert "Voice Assistant" in r.text
|
||||
assert "Alexis" in r.text
|
||||
|
||||
|
||||
def test_static_index_gated_without_auth(monkeypatch):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue