Merge pull request #22 from boudra/local-streaming-speech-tts

voice: harden local voice agent path and UUID validation
This commit is contained in:
Mohamed Boudra
2026-02-07 00:31:30 +07:00
committed by GitHub
69 changed files with 3991 additions and 2096 deletions
+12 -20
View File
@@ -2,12 +2,13 @@ import { createContext, useContext, useState, ReactNode, useCallback, useEffect,
import { useSpeechmaticsAudio } from "@/hooks/use-speechmatics-audio";
import type { SessionState } from "@/stores/session-store";
import { useSessionStore } from "@/stores/session-store";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { randomUUID } from "expo-crypto";
import { activateKeepAwakeAsync, deactivateKeepAwake } from "expo-keep-awake";
const VOICE_CONVERSATION_ID_STORAGE_KEY = "@paseo:voice-conversation-id";
const KEEP_AWAKE_TAG = "paseo:voice";
const VOICE_VAD_VOLUME_THRESHOLD = 0.18;
const VOICE_VAD_SILENCE_DURATION_MS = 1400;
const VOICE_VAD_SPEECH_CONFIRMATION_MS = 120;
const VOICE_VAD_DETECTION_GRACE_PERIOD_MS = 700;
interface VoiceContextValue {
isVoiceMode: boolean;
@@ -123,10 +124,10 @@ export function VoiceProvider({ children }: VoiceProviderProps) {
console.error("[Voice] Cannot handle error - setMessages not available from SessionState");
}
},
volumeThreshold: 0.3,
silenceDuration: 2000,
speechConfirmationDuration: 300,
detectionGracePeriod: 200,
volumeThreshold: VOICE_VAD_VOLUME_THRESHOLD,
silenceDuration: VOICE_VAD_SILENCE_DURATION_MS,
speechConfirmationDuration: VOICE_VAD_SPEECH_CONFIRMATION_MS,
detectionGracePeriod: VOICE_VAD_DETECTION_GRACE_PERIOD_MS,
});
useEffect(() => {
@@ -166,18 +167,9 @@ export function VoiceProvider({ children }: VoiceProviderProps) {
console.log("[Voice] Mode enabled");
if (session?.client) {
let voiceConversationId =
(await AsyncStorage.getItem(VOICE_CONVERSATION_ID_STORAGE_KEY)) ?? null;
if (!voiceConversationId) {
voiceConversationId = randomUUID();
await AsyncStorage.setItem(
VOICE_CONVERSATION_ID_STORAGE_KEY,
voiceConversationId
);
}
await session.client.setVoiceConversation(true, voiceConversationId);
await session.client.setVoiceMode(true);
} else {
console.warn("[Voice] setVoiceConversation skipped: daemon unavailable");
console.warn("[Voice] setVoiceMode skipped: daemon unavailable");
}
} catch (error: any) {
console.error("[Voice] Failed to start:", error);
@@ -200,9 +192,9 @@ export function VoiceProvider({ children }: VoiceProviderProps) {
console.log("[Voice] Mode disabled");
if (session?.client) {
await session.client.setVoiceConversation(false);
await session.client.setVoiceMode(false);
} else {
console.warn("[Voice] setVoiceConversation skipped: daemon unavailable");
console.warn("[Voice] setVoiceMode skipped: daemon unavailable");
}
} catch (error: any) {
console.error("[Voice] Failed to stop:", error);