Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f09e2bca3 | ||
|
|
ebe0e8065f | ||
|
|
9d2c07d8d1 |
@@ -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 20309
|
versionCode 20400
|
||||||
versionName "0.2.3.9"
|
versionName "0.2.4.0"
|
||||||
// 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.2.3.9",
|
"version": "0.2.4.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"android": "react-native run-android",
|
"android": "react-native run-android",
|
||||||
|
|||||||
@@ -1679,7 +1679,11 @@ const ChatScreen: React.FC = () => {
|
|||||||
rememberMyRequest(audioRequestId);
|
rememberMyRequest(audioRequestId);
|
||||||
const wasInterrupted = interruptAriaIfBusy();
|
const wasInterrupted = interruptAriaIfBusy();
|
||||||
const location = await getCurrentLocation();
|
const location = await getCurrentLocation();
|
||||||
const windowMs = await loadConvWindowMs();
|
// EIN Wert regiert: die Stille-Toleranz. Sie gilt sowohl als Pause WÄHREND
|
||||||
|
// des Redens (endpointMs) ALS AUCH als "wenn du nicht anfängst zu reden,
|
||||||
|
// ist Schluss" (noSpeechTimeoutMs). Kein separates 30s-Konversationsfenster
|
||||||
|
// mehr — Stefans Modell: sagst du nichts, greift der Stille-Wert.
|
||||||
|
const sttEndpointMs = await loadSttEndpointMs();
|
||||||
|
|
||||||
const userMsg: ChatMessage = {
|
const userMsg: ChatMessage = {
|
||||||
id: nextId(),
|
id: nextId(),
|
||||||
@@ -1697,8 +1701,8 @@ const ChatScreen: React.FC = () => {
|
|||||||
speed: ttsSpeedRef.current,
|
speed: ttsSpeedRef.current,
|
||||||
interrupted: wasInterrupted,
|
interrupted: wasInterrupted,
|
||||||
location: location || null,
|
location: location || null,
|
||||||
noSpeechTimeoutMs: windowMs,
|
noSpeechTimeoutMs: sttEndpointMs,
|
||||||
endpointMs: await loadSttEndpointMs(),
|
endpointMs: sttEndpointMs,
|
||||||
// Notbremse 5 min (nicht 1 min) — der Stille-Endpoint beendet normale
|
// Notbremse 5 min (nicht 1 min) — der Stille-Endpoint beendet normale
|
||||||
// Turns eh sofort; der Cap darf lange Diktate nicht mitten drin kappen.
|
// Turns eh sofort; der Cap darf lange Diktate nicht mitten drin kappen.
|
||||||
hardCapMs: await loadMaxRecordingMs(),
|
hardCapMs: await loadMaxRecordingMs(),
|
||||||
@@ -1756,12 +1760,12 @@ const ChatScreen: React.FC = () => {
|
|||||||
!(m.audioRequestId === ev.audioRequestId
|
!(m.audioRequestId === ev.audioRequestId
|
||||||
&& m.text.includes('Spracheingabe wird verarbeitet'))));
|
&& m.text.includes('Spracheingabe wird verarbeitet'))));
|
||||||
}
|
}
|
||||||
// Bei Passive-Listen + speaker_mismatch oder no-speech: erneut passiv
|
// Kein Re-Arm mehr: nach ARIAs Antwort gab es EIN Stille-Fenster (=
|
||||||
// lauschen (Timer im wakeword-service laeuft weiter, regelt das Ende).
|
// Stille-Toleranz). Kam nichts, ist Schluss → zurück aufs Wake-Word.
|
||||||
// Sonst endConversation wie bisher.
|
// Kein 30s-Nachlauschen. (speaker_mismatch/no-speech landen beide hier.)
|
||||||
if (wakeWordService.getState() === 'listening') {
|
if (wakeWordService.getState() === 'listening') {
|
||||||
console.log('[Chat] Passive-Listen: leeres Endpoint — naechste passive Aufnahme');
|
console.log('[Chat] Passive-Listen: leeres Endpoint — Ende, zurueck aufs Wake-Word');
|
||||||
startPassiveStreamingRecording();
|
wakeWordService.exitPassiveListening('timeout').catch(() => {});
|
||||||
} else {
|
} else {
|
||||||
wakeWordService.endConversation();
|
wakeWordService.endConversation();
|
||||||
if (!wakeWordService.isActive()) setWakeWordActive(false);
|
if (!wakeWordService.isActive()) setWakeWordActive(false);
|
||||||
@@ -1791,7 +1795,7 @@ const ChatScreen: React.FC = () => {
|
|||||||
const audioRequestId = `audio_${Date.now()}_${Math.floor(Math.random() * 100000)}`;
|
const audioRequestId = `audio_${Date.now()}_${Math.floor(Math.random() * 100000)}`;
|
||||||
rememberMyRequest(audioRequestId);
|
rememberMyRequest(audioRequestId);
|
||||||
const location = await getCurrentLocation();
|
const location = await getCurrentLocation();
|
||||||
const windowMs = await loadConvWindowMs();
|
const sttEndpointMs = await loadSttEndpointMs(); // ein Wert für Pause + No-Speech
|
||||||
|
|
||||||
const userMsg: ChatMessage = {
|
const userMsg: ChatMessage = {
|
||||||
id: nextId(),
|
id: nextId(),
|
||||||
@@ -1809,8 +1813,8 @@ const ChatScreen: React.FC = () => {
|
|||||||
speed: ttsSpeedRef.current,
|
speed: ttsSpeedRef.current,
|
||||||
interrupted: true, // Barge-In → Brain weiss "User hat unterbrochen"
|
interrupted: true, // Barge-In → Brain weiss "User hat unterbrochen"
|
||||||
location: location || null,
|
location: location || null,
|
||||||
noSpeechTimeoutMs: windowMs,
|
noSpeechTimeoutMs: sttEndpointMs,
|
||||||
endpointMs: await loadSttEndpointMs(),
|
endpointMs: sttEndpointMs,
|
||||||
// Notbremse 5 min (s.o.) — lange Diktate nicht bei 1 min abschneiden.
|
// Notbremse 5 min (s.o.) — lange Diktate nicht bei 1 min abschneiden.
|
||||||
hardCapMs: await loadMaxRecordingMs(),
|
hardCapMs: await loadMaxRecordingMs(),
|
||||||
projectId: focusedProjectIdRef.current,
|
projectId: focusedProjectIdRef.current,
|
||||||
@@ -1871,16 +1875,20 @@ const ChatScreen: React.FC = () => {
|
|||||||
const audioRequestId = `audio_passive_${Date.now()}_${Math.floor(Math.random() * 100000)}`;
|
const audioRequestId = `audio_passive_${Date.now()}_${Math.floor(Math.random() * 100000)}`;
|
||||||
rememberMyRequest(audioRequestId);
|
rememberMyRequest(audioRequestId);
|
||||||
const location = await getCurrentLocation();
|
const location = await getCurrentLocation();
|
||||||
const passiveMs = await loadPassiveListenMs();
|
// Kein 30s-Passiv-Fenster mehr: nach ARIAs Antwort geht das Mikro auf, und
|
||||||
|
// fängst du nicht innerhalb der Stille-Toleranz an zu reden, ist Schluss →
|
||||||
|
// zurück aufs Wake-Word. Derselbe Wert wie die Pause-Toleranz beim Reden.
|
||||||
|
const sttEndpointMs = await loadSttEndpointMs();
|
||||||
const { ok } = await audioService.startStreamingRecording({
|
const { ok } = await audioService.startStreamingRecording({
|
||||||
audioRequestId,
|
audioRequestId,
|
||||||
voice: localXttsVoiceRef.current,
|
voice: localXttsVoiceRef.current,
|
||||||
speed: ttsSpeedRef.current,
|
speed: ttsSpeedRef.current,
|
||||||
interrupted: false,
|
interrupted: false,
|
||||||
location: location || null,
|
location: location || null,
|
||||||
noSpeechTimeoutMs: Math.min(passiveMs, 30000),
|
noSpeechTimeoutMs: sttEndpointMs,
|
||||||
endpointMs: await loadSttEndpointMs(),
|
endpointMs: sttEndpointMs,
|
||||||
hardCapMs: Math.max(passiveMs + 5000, 35000),
|
// Lange Antworten nicht kappen (früher 35s → schnitt langes Reden ab).
|
||||||
|
hardCapMs: await loadMaxRecordingMs(),
|
||||||
projectId: focusedProjectIdRef.current,
|
projectId: focusedProjectIdRef.current,
|
||||||
});
|
});
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
|||||||
+38
-5
@@ -1418,6 +1418,30 @@ def _user_wants_conversation_end(text: str) -> bool:
|
|||||||
return bool(_END_CONVERSATION_RE.search(_strip_leading_hint_blocks(text)))
|
return bool(_END_CONVERSATION_RE.search(_strip_leading_hint_blocks(text)))
|
||||||
|
|
||||||
|
|
||||||
|
# Gegenstueck zu _END: expliziter "Konversation OFFEN halten / fortfuehren"-Wunsch.
|
||||||
|
# Wichtig fuer BEFEHLE die den Fast-Path treffen: "spiel Spotify ab ABER Konversation
|
||||||
|
# fortfuehren" — der Fast-Path (Regex) versteht den Satz-Rest nicht und wuerde mit
|
||||||
|
# converse=false schliessen. Dieser Detektor erzwingt converse=true, auch am
|
||||||
|
# Fast-Path, egal was das Skill-Manifest sagt. Nomen+Verb in einem Satzteil, plus
|
||||||
|
# "weiter reden/sprechen" ohne Nomen.
|
||||||
|
_CONT_VERB = (r"(?:fortf[uü]hr\w*|fortsetz\w*|weiterf[uü]hr\w*|weiter\s*mach\w*|"
|
||||||
|
r"weiter\b|fort\b|offen\s+(?:halten|lassen)|nicht\s+beenden|weiterlauf\w*)")
|
||||||
|
_CONTINUE_CONVERSATION_RE = re.compile(
|
||||||
|
rf"\b{_CONV_NOUN}\b[^.!?]{{0,20}}\b{_CONT_VERB}"
|
||||||
|
rf"|\b{_CONT_VERB}[^.!?]{{0,20}}\b{_CONV_NOUN}\b"
|
||||||
|
rf"|\bweiter\s*(?:reden|sprechen|quatschen|plaudern|labern)\b",
|
||||||
|
re.IGNORECASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _user_wants_conversation_continue(text: str) -> bool:
|
||||||
|
"""True, wenn der User explizit weiter im Gespraech bleiben will (converse=true
|
||||||
|
erzwingen — auch bei einem Fast-Path-Befehl). [[ENDE]]/_wants_end hat Vorrang."""
|
||||||
|
if not text:
|
||||||
|
return False
|
||||||
|
return bool(_CONTINUE_CONVERSATION_RE.search(_strip_leading_hint_blocks(text)))
|
||||||
|
|
||||||
|
|
||||||
def _normalize_for_fast_match(text: str) -> str:
|
def _normalize_for_fast_match(text: str) -> str:
|
||||||
norm = _strip_leading_hint_blocks(text).lower()
|
norm = _strip_leading_hint_blocks(text).lower()
|
||||||
norm = _fold_umlauts(norm)
|
norm = _fold_umlauts(norm)
|
||||||
@@ -1826,10 +1850,13 @@ class Agent:
|
|||||||
if not user_message:
|
if not user_message:
|
||||||
raise ValueError("Leere Nachricht")
|
raise ValueError("Leere Nachricht")
|
||||||
|
|
||||||
# Expliziter "Konversation/Kette beenden"-Wunsch (deterministisch). Wird an
|
# Explizite Gespraechs-Steuerung vom USER (deterministisch, an JEDEM Return
|
||||||
# JEDEM Return auf converse=false angewendet — unabhaengig davon, ob ARIA
|
# angewendet — auch am Fast-Path, den die LLM-Marker nicht erreichen):
|
||||||
# den [[ENDE]]-Marker setzt. Stefans fester Trigger "Konversation Ende".
|
# _wants_end → converse=false ("Konversation Ende")
|
||||||
|
# _wants_continue → converse=true ("... aber Konversation fortfuehren")
|
||||||
|
# End hat Vorrang bei Widerspruch.
|
||||||
_wants_end = _user_wants_conversation_end(user_message)
|
_wants_end = _user_wants_conversation_end(user_message)
|
||||||
|
_wants_continue = (not _wants_end) and _user_wants_conversation_continue(user_message)
|
||||||
|
|
||||||
# Events vom letzten Turn weglassen
|
# Events vom letzten Turn weglassen
|
||||||
self._pending_events = []
|
self._pending_events = []
|
||||||
@@ -1860,6 +1887,8 @@ class Agent:
|
|||||||
converse = bool(getattr(self, "_fast_path_converse", False))
|
converse = bool(getattr(self, "_fast_path_converse", False))
|
||||||
if _wants_end:
|
if _wants_end:
|
||||||
converse = False
|
converse = False
|
||||||
|
elif _wants_continue:
|
||||||
|
converse = True
|
||||||
# Fast-Path = reiner Steuerbefehl, nie eine Rueckfrage → awaiting=False.
|
# Fast-Path = reiner Steuerbefehl, nie eine Rueckfrage → awaiting=False.
|
||||||
return fast_reply, "fast-path", speak, converse, False
|
return fast_reply, "fast-path", speak, converse, False
|
||||||
|
|
||||||
@@ -1881,6 +1910,8 @@ class Agent:
|
|||||||
converse = getattr(self, "_local_turn_converse", True)
|
converse = getattr(self, "_local_turn_converse", True)
|
||||||
if _wants_end:
|
if _wants_end:
|
||||||
converse = False
|
converse = False
|
||||||
|
elif _wants_continue:
|
||||||
|
converse = True
|
||||||
# Local ist tool-loses Reden; blockierende Rueckfragen macht Claude.
|
# Local ist tool-loses Reden; blockierende Rueckfragen macht Claude.
|
||||||
return local_reply, "local", speak, converse, False
|
return local_reply, "local", speak, converse, False
|
||||||
|
|
||||||
@@ -2129,10 +2160,12 @@ class Agent:
|
|||||||
speak = _speak_ov
|
speak = _speak_ov
|
||||||
if _conv_ov is not None:
|
if _conv_ov is not None:
|
||||||
converse = _conv_ov
|
converse = _conv_ov
|
||||||
# Expliziter User-Wunsch "Konversation beenden" gewinnt IMMER — Frage wird
|
# Explizite User-Woerter gewinnen ueber Marker/Manifest: "Konversation
|
||||||
# noch beantwortet (speak bleibt), aber danach zurueck aufs Wake-Word.
|
# beenden" → zu; "... fortfuehren" → offen halten. End hat Vorrang.
|
||||||
if _wants_end:
|
if _wants_end:
|
||||||
converse = False
|
converse = False
|
||||||
|
elif _wants_continue:
|
||||||
|
converse = True
|
||||||
# awaiting_reply = ARIA stellt eine blockierende Rueckfrage (Queue pausiert).
|
# awaiting_reply = ARIA stellt eine blockierende Rueckfrage (Queue pausiert).
|
||||||
return (final_reply, "claude", speak, converse, awaiting_reply)
|
return (final_reply, "claude", speak, converse, awaiting_reply)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user