Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 08da28f475 | |||
| 8c1014d281 | |||
| 271fc4edf6 |
@@ -341,10 +341,10 @@ Erreichbar unter `http://<VM-IP>:3001`. Teilt das Netzwerk mit aria-core.
|
|||||||
- **Chat-Test**: Nachrichten direkt an ARIA senden (Gateway oder via RVS), Vollbild-Modus
|
- **Chat-Test**: Nachrichten direkt an ARIA senden (Gateway oder via RVS), Vollbild-Modus
|
||||||
- **"ARIA denkt..." Indikator**: Zeigt live was ARIA gerade tut (Denken, Tool, Schreiben)
|
- **"ARIA denkt..." Indikator**: Zeigt live was ARIA gerade tut (Denken, Tool, Schreiben)
|
||||||
- **Abbrechen-Button**: Stoppt laufende Anfragen + doctor --fix
|
- **Abbrechen-Button**: Stoppt laufende Anfragen + doctor --fix
|
||||||
- **Session-Verwaltung**: Sessions auflisten, wechseln, erstellen, loeschen
|
- **Session-Verwaltung**: Sessions auflisten, wechseln, erstellen, loeschen, als Markdown exportieren (⬇ Button)
|
||||||
- **Chat-History**: Wird beim Laden und Session-Wechsel angezeigt (read-only aus JSONL)
|
- **Chat-History**: Wird beim Laden und Session-Wechsel angezeigt (read-only aus JSONL)
|
||||||
- **TTS-Diagnose Tab**: Stimmen testen, Status pruefen, Fehler anzeigen
|
- **TTS-Diagnose Tab**: Stimmen testen, Status pruefen, Fehler anzeigen
|
||||||
- **Einstellungen**: TTS-Engine (Piper/XTTS), Stimmen, Speed, Highlight-Trigger, Betriebsmodi
|
- **Einstellungen**: TTS-Engine (Piper/XTTS), Stimmen, Speed, Highlight-Trigger, Betriebsmodi, Whisper-Modell (tiny…large-v3, Hot-Reload)
|
||||||
- **XTTS Voice Cloning**: Audio-Samples hochladen, eigene Stimme erstellen
|
- **XTTS Voice Cloning**: Audio-Samples hochladen, eigene Stimme erstellen
|
||||||
- **Claude Login**: Browser-Terminal zum Einloggen in den Proxy
|
- **Claude Login**: Browser-Terminal zum Einloggen in den Proxy
|
||||||
- **Core Terminal**: Shell in aria-core (openclaw CLI)
|
- **Core Terminal**: Shell in aria-core (openclaw CLI)
|
||||||
@@ -370,7 +370,9 @@ API-Endpoint fuer andere Services: `GET http://localhost:3001/api/session`
|
|||||||
- **Sprachaufnahme**: Push-to-Talk (halten) oder Tap-to-Talk (tippen, Auto-Stop bei Stille)
|
- **Sprachaufnahme**: Push-to-Talk (halten) oder Tap-to-Talk (tippen, Auto-Stop bei Stille)
|
||||||
- **Gespraechsmodus** (Ohr-Button): Nach jeder ARIA-Antwort startet automatisch die Aufnahme — wie ein natuerliches Gespraech hin und her, ohne Buttons druecken
|
- **Gespraechsmodus** (Ohr-Button): Nach jeder ARIA-Antwort startet automatisch die Aufnahme — wie ein natuerliches Gespraech hin und her, ohne Buttons druecken
|
||||||
- **VAD (Voice Activity Detection)**: Erkennt 1.8s Stille und stoppt automatisch
|
- **VAD (Voice Activity Detection)**: Erkennt 1.8s Stille und stoppt automatisch
|
||||||
- **STT (Speech-to-Text)**: Audio wird in der Bridge per Whisper transkribiert, transkribierter Text erscheint im Chat
|
- **Speech Gate**: Aufnahme wird verworfen wenn keine Sprache erkannt (kein Rauschen an Whisper)
|
||||||
|
- **STT (Speech-to-Text)**: Audio wird als 16kHz mono aufgenommen und in der Bridge per Whisper transkribiert, transkribierter Text erscheint im Chat
|
||||||
|
- **"ARIA denkt..." Indicator**: Zeigt live den Status vom Core (Denken, Tool, Schreiben) + Abbrechen-Button
|
||||||
- **TTS-Wiedergabe**: ARIA antwortet per Lautsprecher (Piper oder XTTS v2), Audio-Queue mit Preloading
|
- **TTS-Wiedergabe**: ARIA antwortet per Lautsprecher (Piper oder XTTS v2), Audio-Queue mit Preloading
|
||||||
- **Play-Button**: Jede ARIA-Nachricht kann nochmal vorgelesen werden
|
- **Play-Button**: Jede ARIA-Nachricht kann nochmal vorgelesen werden
|
||||||
- **Chat-Suche**: Lupe in der Statusleiste filtert Nachrichten live
|
- **Chat-Suche**: Lupe in der Statusleiste filtert Nachrichten live
|
||||||
@@ -424,6 +426,17 @@ GITEA_USER=stefan
|
|||||||
RVS_UPDATE_HOST=root@aria-rvs # Optional: fuer Auto-Update
|
RVS_UPDATE_HOST=root@aria-rvs # Optional: fuer Auto-Update
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Docker-Cleanup
|
||||||
|
|
||||||
|
Das Bridge-Image zieht grosse ML-Deps (faster-whisper, ctranslate2, onnxruntime,
|
||||||
|
openwakeword, piper-tts) — bei jedem Rebuild waechst der Docker-Build-Cache. Wenn
|
||||||
|
die VM voll laeuft:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./cleanup.sh # sicher: Build-Cache + ungenutzte Images
|
||||||
|
./cleanup.sh --full # aggressiv: zusaetzlich ungenutzte Volumes (mit Rueckfrage)
|
||||||
|
```
|
||||||
|
|
||||||
### Auto-Update
|
### Auto-Update
|
||||||
|
|
||||||
Die App prueft beim Start ob eine neuere Version auf dem RVS liegt.
|
Die App prueft beim Start ob eine neuere Version auf dem RVS liegt.
|
||||||
@@ -717,6 +730,12 @@ docker exec aria-core ssh aria-wohnung hostname
|
|||||||
- [x] Markdown-Bereinigung fuer TTS
|
- [x] Markdown-Bereinigung fuer TTS
|
||||||
- [x] Auto-Update mit FileProvider + Update-Check Button
|
- [x] Auto-Update mit FileProvider + Update-Check Button
|
||||||
- [x] Inverted FlatList (zuverlaessiges Scroll-to-Bottom)
|
- [x] Inverted FlatList (zuverlaessiges Scroll-to-Bottom)
|
||||||
|
- [x] Speech Gate (VAD verwirft Aufnahme ohne erkannte Sprache)
|
||||||
|
- [x] Session-Persistenz ueber Container-Restarts (sessionFromFile + atomic write)
|
||||||
|
- [x] Session-Export als Markdown-Datei (Download-Button pro Session)
|
||||||
|
- [x] "ARIA denkt..."-Indicator + Abbrechen-Button in App (via Bridge → RVS)
|
||||||
|
- [x] Whisper-Modell waehlbar in Diagnostic (tiny…large-v3, Hot-Reload)
|
||||||
|
- [x] App-Aufnahme explizit 16kHz mono (optimal fuer Whisper, kein Resample)
|
||||||
|
|
||||||
### Phase 2 — ARIA wird produktiv
|
### Phase 2 — ARIA wird produktiv
|
||||||
|
|
||||||
|
|||||||
@@ -79,8 +79,8 @@ android {
|
|||||||
applicationId "com.ariacockpit"
|
applicationId "com.ariacockpit"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 308
|
versionCode 309
|
||||||
versionName "0.0.3.8"
|
versionName "0.0.3.9"
|
||||||
// Fallback fuer Libraries mit Product Flavors
|
// Fallback fuer Libraries mit Product Flavors
|
||||||
missingDimensionStrategy 'react-native-camera', 'general'
|
missingDimensionStrategy 'react-native-camera', 'general'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "aria-cockpit",
|
"name": "aria-cockpit",
|
||||||
"version": "0.0.3.8",
|
"version": "0.0.3.9",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"android": "react-native run-android",
|
"android": "react-native run-android",
|
||||||
|
|||||||
+13
-1
@@ -554,6 +554,9 @@ class ARIABridge:
|
|||||||
|
|
||||||
# Letzter gesendeter agent_activity-State (zum Entduplizieren)
|
# Letzter gesendeter agent_activity-State (zum Entduplizieren)
|
||||||
self._last_activity_state: Optional[tuple] = None
|
self._last_activity_state: Optional[tuple] = None
|
||||||
|
# Zeitstempel des letzten chat:final — waehrend 3s danach werden
|
||||||
|
# trailing Agent-Events unterdrueckt (Core raeumt manchmal nach).
|
||||||
|
self._last_chat_final_at: float = 0.0
|
||||||
|
|
||||||
def initialize(self) -> None:
|
def initialize(self) -> None:
|
||||||
"""Initialisiert alle Komponenten.
|
"""Initialisiert alle Komponenten.
|
||||||
@@ -779,6 +782,7 @@ class ARIABridge:
|
|||||||
|
|
||||||
if state == "final":
|
if state == "final":
|
||||||
text = self._extract_chat_text(payload)
|
text = self._extract_chat_text(payload)
|
||||||
|
self._last_chat_final_at = asyncio.get_event_loop().time()
|
||||||
await self._emit_activity("idle", "")
|
await self._emit_activity("idle", "")
|
||||||
if not text:
|
if not text:
|
||||||
logger.warning("[core] chat final ohne Text: %s", json.dumps(payload)[:200])
|
logger.warning("[core] chat final ohne Text: %s", json.dumps(payload)[:200])
|
||||||
@@ -790,6 +794,7 @@ class ARIABridge:
|
|||||||
if state == "error":
|
if state == "error":
|
||||||
error = payload.get("error", "Unbekannt")
|
error = payload.get("error", "Unbekannt")
|
||||||
logger.error("[core] Chat-Fehler: %s", error)
|
logger.error("[core] Chat-Fehler: %s", error)
|
||||||
|
self._last_chat_final_at = asyncio.get_event_loop().time()
|
||||||
await self._emit_activity("idle", "")
|
await self._emit_activity("idle", "")
|
||||||
await self._send_to_rvs({
|
await self._send_to_rvs({
|
||||||
"type": "chat",
|
"type": "chat",
|
||||||
@@ -1468,7 +1473,14 @@ class ARIABridge:
|
|||||||
logger.info("[cancel] Diagnostic /api/cancel: %s", status)
|
logger.info("[cancel] Diagnostic /api/cancel: %s", status)
|
||||||
|
|
||||||
async def _emit_activity(self, activity: str, tool: str = "") -> None:
|
async def _emit_activity(self, activity: str, tool: str = "") -> None:
|
||||||
"""Sendet agent_activity an die App — nur wenn sich der State geaendert hat."""
|
"""Sendet agent_activity an die App — nur wenn sich der State geaendert hat.
|
||||||
|
|
||||||
|
Trailing Agent-Events nach chat:final werden 3s lang unterdrueckt
|
||||||
|
(nur 'idle' kommt immer durch)."""
|
||||||
|
if activity != "idle" and self._last_chat_final_at > 0:
|
||||||
|
since_final = asyncio.get_event_loop().time() - self._last_chat_final_at
|
||||||
|
if since_final < 3.0:
|
||||||
|
return
|
||||||
state = (activity, tool)
|
state = (activity, tool)
|
||||||
if state == self._last_activity_state:
|
if state == self._last_activity_state:
|
||||||
return
|
return
|
||||||
|
|||||||
Executable
+44
@@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ARIA Docker Cleanup
|
||||||
|
#
|
||||||
|
# Standard: docker builder prune + image prune (sicher, loescht keine Volumes)
|
||||||
|
# --full: Volle Reinigung inkl. --volumes (Vorsicht bei ungenutzten Volumes!)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./cleanup.sh # sicherer Cleanup
|
||||||
|
# ./cleanup.sh --full # aggressiver Cleanup (inkl. Volumes)
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
FULL=0
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--full|-f) FULL=1 ;;
|
||||||
|
-h|--help)
|
||||||
|
grep '^#' "$0" | sed 's/^# \{0,1\}//'
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "── Docker Speicher VOR Cleanup ───────────────────"
|
||||||
|
docker system df
|
||||||
|
echo
|
||||||
|
|
||||||
|
if [ "$FULL" = "1" ]; then
|
||||||
|
echo ">>> VOLLE Reinigung (inkl. ungenutzter Volumes)"
|
||||||
|
read -p "Wirklich? [y/N] " -n 1 -r REPLY
|
||||||
|
echo
|
||||||
|
[[ ! $REPLY =~ ^[Yy]$ ]] && { echo "Abgebrochen."; exit 0; }
|
||||||
|
docker system prune -a --volumes -f
|
||||||
|
else
|
||||||
|
echo ">>> Sicherer Cleanup (Build-Cache + ungenutzte Images)"
|
||||||
|
docker builder prune -a -f
|
||||||
|
docker image prune -a -f
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "── Docker Speicher NACH Cleanup ──────────────────"
|
||||||
|
docker system df
|
||||||
|
echo
|
||||||
|
df -h / | head -2
|
||||||
+16
-3
@@ -82,6 +82,12 @@ const browserClients = new Set();
|
|||||||
let pipelineActive = false;
|
let pipelineActive = false;
|
||||||
let pipelineStartTime = 0;
|
let pipelineStartTime = 0;
|
||||||
|
|
||||||
|
// Nach chat:final kommen oft noch Trailing Agent-Events. Waehrend dieses
|
||||||
|
// Fensters unterdruecken wir agent_activity-Broadcasts, damit der
|
||||||
|
// Thinking-Indicator nicht wieder anspringt.
|
||||||
|
let lastChatFinalAt = 0;
|
||||||
|
const SETTLED_WINDOW_MS = 3000;
|
||||||
|
|
||||||
function plog(message, level) {
|
function plog(message, level) {
|
||||||
const elapsed = pipelineActive ? `+${Date.now() - pipelineStartTime}ms` : "";
|
const elapsed = pipelineActive ? `+${Date.now() - pipelineStartTime}ms` : "";
|
||||||
const entry = { ts: new Date().toISOString(), level: level || "info", source: "pipeline", message: `${elapsed ? `[${elapsed}] ` : ""}${message}` };
|
const entry = { ts: new Date().toISOString(), level: level || "info", source: "pipeline", message: `${elapsed ? `[${elapsed}] ` : ""}${message}` };
|
||||||
@@ -356,17 +362,22 @@ function handleGatewayMessage(msg) {
|
|||||||
broadcast({ type: "chat_delta", delta, payload });
|
broadcast({ type: "chat_delta", delta, payload });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nach chat:final trickeln noch Aufraeum-Events rein — unterdruecken,
|
||||||
|
// damit der Thinking-Indicator nicht wieder anspringt.
|
||||||
|
const settled = lastChatFinalAt && (Date.now() - lastChatFinalAt) < SETTLED_WINDOW_MS;
|
||||||
|
|
||||||
// Tool-Nutzung erkennen und broadcasten
|
// Tool-Nutzung erkennen und broadcasten
|
||||||
if (stream === "tool_use" || data.type === "tool_use") {
|
if (stream === "tool_use" || data.type === "tool_use") {
|
||||||
const toolName = data.name || data.tool || payload.tool || "";
|
const toolName = data.name || data.tool || payload.tool || "";
|
||||||
if (toolName) {
|
if (toolName && !settled) {
|
||||||
broadcast({ type: "agent_activity", activity: "tool", tool: toolName, data });
|
broadcast({ type: "agent_activity", activity: "tool", tool: toolName, data });
|
||||||
log("info", "gateway", `Tool: ${toolName}`);
|
log("info", "gateway", `Tool: ${toolName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Genereller Activity-Heartbeat (ARIA denkt)
|
if (!settled) {
|
||||||
broadcast({ type: "agent_activity", activity: stream || "thinking" });
|
broadcast({ type: "agent_activity", activity: stream || "thinking" });
|
||||||
|
}
|
||||||
updateAgentActivity();
|
updateAgentActivity();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -381,6 +392,7 @@ function handleGatewayMessage(msg) {
|
|||||||
if (runId && seenFinalRuns.has(runId)) return; // Duplikat
|
if (runId && seenFinalRuns.has(runId)) return; // Duplikat
|
||||||
if (runId) { seenFinalRuns.add(runId); setTimeout(() => seenFinalRuns.delete(runId), 60000); }
|
if (runId) { seenFinalRuns.add(runId); setTimeout(() => seenFinalRuns.delete(runId), 60000); }
|
||||||
log("info", "gateway", `ANTWORT: "${text.slice(0, 200)}"`);
|
log("info", "gateway", `ANTWORT: "${text.slice(0, 200)}"`);
|
||||||
|
lastChatFinalAt = Date.now();
|
||||||
if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`);
|
if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`);
|
||||||
broadcast({ type: "chat_final", text, payload });
|
broadcast({ type: "chat_final", text, payload });
|
||||||
broadcast({ type: "agent_activity", activity: "idle" });
|
broadcast({ type: "agent_activity", activity: "idle" });
|
||||||
@@ -424,6 +436,7 @@ function handleGatewayMessage(msg) {
|
|||||||
if (runId) { seenFinalRuns.add(runId); setTimeout(() => seenFinalRuns.delete(runId), 60000); }
|
if (runId) { seenFinalRuns.add(runId); setTimeout(() => seenFinalRuns.delete(runId), 60000); }
|
||||||
const text = extractChatText(payload) || payload.text || "";
|
const text = extractChatText(payload) || payload.text || "";
|
||||||
log("info", "gateway", `ANTWORT: "${text.slice(0, 200)}"`);
|
log("info", "gateway", `ANTWORT: "${text.slice(0, 200)}"`);
|
||||||
|
lastChatFinalAt = Date.now();
|
||||||
if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`);
|
if (pipelineActive) pipelineEnd(true, `"${text.slice(0, 120)}"`);
|
||||||
else broadcast({ type: "agent_activity", activity: "idle" });
|
else broadcast({ type: "agent_activity", activity: "idle" });
|
||||||
broadcast({ type: "chat_final", text, payload });
|
broadcast({ type: "chat_final", text, payload });
|
||||||
|
|||||||
Reference in New Issue
Block a user