refactor(session): simplify voice readiness gating flow

This commit is contained in:
Mohamed Boudra
2026-02-14 00:57:02 +07:00
parent c5d7fa52b4
commit cf8393407e
+38 -30
View File
@@ -131,7 +131,7 @@ import {
type LocalSpeechModelId, type LocalSpeechModelId,
} from "./speech/providers/local/models.js"; } from "./speech/providers/local/models.js";
import type { Resolvable } from "./speech/provider-resolver.js"; import type { Resolvable } from "./speech/provider-resolver.js";
import type { SpeechReadinessSnapshot } from "./speech/speech-runtime.js"; import type { SpeechReadinessSnapshot, SpeechReadinessState } from "./speech/speech-runtime.js";
import type pino from "pino"; import type pino from "pino";
const execAsync = promisify(exec); const execAsync = promisify(exec);
@@ -358,6 +358,12 @@ type VoiceFeatureUnavailableContext = {
missingModelIds: LocalSpeechModelId[]; missingModelIds: LocalSpeechModelId[];
}; };
type VoiceFeatureUnavailableResponseMetadata = {
reasonCode?: SpeechReadinessSnapshot["voiceFeature"]["reasonCode"];
retryable?: boolean;
missingModelIds?: LocalSpeechModelId[];
};
class VoiceFeatureUnavailableError extends Error { class VoiceFeatureUnavailableError extends Error {
readonly reasonCode: SpeechReadinessSnapshot["voiceFeature"]["reasonCode"]; readonly reasonCode: SpeechReadinessSnapshot["voiceFeature"]["reasonCode"];
readonly retryable: boolean; readonly retryable: boolean;
@@ -1759,7 +1765,7 @@ export class Session {
} }
private toVoiceFeatureUnavailableContext( private toVoiceFeatureUnavailableContext(
state: SpeechReadinessSnapshot["voiceFeature"] state: SpeechReadinessState
): VoiceFeatureUnavailableContext { ): VoiceFeatureUnavailableContext {
return { return {
reasonCode: state.reasonCode, reasonCode: state.reasonCode,
@@ -1769,6 +1775,29 @@ export class Session {
}; };
} }
private resolveModeReadinessState(
readiness: SpeechReadinessSnapshot,
mode: "voice_mode" | "dictation"
): SpeechReadinessState {
if (mode === "voice_mode") {
return readiness.realtimeVoice;
}
return readiness.dictation;
}
private getVoiceFeatureUnavailableResponseMetadata(
error: unknown
): VoiceFeatureUnavailableResponseMetadata {
if (!(error instanceof VoiceFeatureUnavailableError)) {
return {};
}
return {
reasonCode: error.reasonCode,
retryable: error.retryable,
missingModelIds: error.missingModelIds,
};
}
private resolveVoiceFeatureUnavailableContext( private resolveVoiceFeatureUnavailableContext(
mode: "voice_mode" | "dictation" mode: "voice_mode" | "dictation"
): VoiceFeatureUnavailableContext | null { ): VoiceFeatureUnavailableContext | null {
@@ -1777,27 +1806,15 @@ export class Session {
return null; return null;
} }
if (mode === "voice_mode") { const modeReadiness = this.resolveModeReadinessState(readiness, mode);
if (!readiness.realtimeVoice.enabled) { if (!modeReadiness.enabled) {
return this.toVoiceFeatureUnavailableContext(readiness.realtimeVoice); return this.toVoiceFeatureUnavailableContext(modeReadiness);
}
if (!readiness.voiceFeature.available) {
return this.toVoiceFeatureUnavailableContext(readiness.voiceFeature);
}
if (!readiness.realtimeVoice.available) {
return this.toVoiceFeatureUnavailableContext(readiness.realtimeVoice);
}
return null;
}
if (!readiness.dictation.enabled) {
return this.toVoiceFeatureUnavailableContext(readiness.dictation);
} }
if (!readiness.voiceFeature.available) { if (!readiness.voiceFeature.available) {
return this.toVoiceFeatureUnavailableContext(readiness.voiceFeature); return this.toVoiceFeatureUnavailableContext(readiness.voiceFeature);
} }
if (!readiness.dictation.available) { if (!modeReadiness.available) {
return this.toVoiceFeatureUnavailableContext(readiness.dictation); return this.toVoiceFeatureUnavailableContext(modeReadiness);
} }
return null; return null;
} }
@@ -1875,14 +1892,7 @@ export class Session {
} catch (error) { } catch (error) {
const errorMessage = const errorMessage =
error instanceof Error ? error.message : "Failed to set voice mode"; error instanceof Error ? error.message : "Failed to set voice mode";
const unavailable = const unavailable = this.getVoiceFeatureUnavailableResponseMetadata(error);
error instanceof VoiceFeatureUnavailableError
? {
reasonCode: error.reasonCode,
retryable: error.retryable,
missingModelIds: error.missingModelIds,
}
: null;
this.sessionLogger.error( this.sessionLogger.error(
{ {
err: error, err: error,
@@ -1900,9 +1910,7 @@ export class Session {
agentId: this.voiceModeAgentId, agentId: this.voiceModeAgentId,
accepted: false, accepted: false,
error: errorMessage, error: errorMessage,
...(unavailable ? { reasonCode: unavailable.reasonCode } : {}), ...unavailable,
...(unavailable ? { retryable: unavailable.retryable } : {}),
...(unavailable ? { missingModelIds: unavailable.missingModelIds } : {}),
}, },
}); });
return; return;