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

View File

@@ -131,7 +131,7 @@ import {
type LocalSpeechModelId,
} from "./speech/providers/local/models.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";
const execAsync = promisify(exec);
@@ -358,6 +358,12 @@ type VoiceFeatureUnavailableContext = {
missingModelIds: LocalSpeechModelId[];
};
type VoiceFeatureUnavailableResponseMetadata = {
reasonCode?: SpeechReadinessSnapshot["voiceFeature"]["reasonCode"];
retryable?: boolean;
missingModelIds?: LocalSpeechModelId[];
};
class VoiceFeatureUnavailableError extends Error {
readonly reasonCode: SpeechReadinessSnapshot["voiceFeature"]["reasonCode"];
readonly retryable: boolean;
@@ -1759,7 +1765,7 @@ export class Session {
}
private toVoiceFeatureUnavailableContext(
state: SpeechReadinessSnapshot["voiceFeature"]
state: SpeechReadinessState
): VoiceFeatureUnavailableContext {
return {
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(
mode: "voice_mode" | "dictation"
): VoiceFeatureUnavailableContext | null {
@@ -1777,27 +1806,15 @@ export class Session {
return null;
}
if (mode === "voice_mode") {
if (!readiness.realtimeVoice.enabled) {
return this.toVoiceFeatureUnavailableContext(readiness.realtimeVoice);
}
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);
const modeReadiness = this.resolveModeReadinessState(readiness, mode);
if (!modeReadiness.enabled) {
return this.toVoiceFeatureUnavailableContext(modeReadiness);
}
if (!readiness.voiceFeature.available) {
return this.toVoiceFeatureUnavailableContext(readiness.voiceFeature);
}
if (!readiness.dictation.available) {
return this.toVoiceFeatureUnavailableContext(readiness.dictation);
if (!modeReadiness.available) {
return this.toVoiceFeatureUnavailableContext(modeReadiness);
}
return null;
}
@@ -1875,14 +1892,7 @@ export class Session {
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : "Failed to set voice mode";
const unavailable =
error instanceof VoiceFeatureUnavailableError
? {
reasonCode: error.reasonCode,
retryable: error.retryable,
missingModelIds: error.missingModelIds,
}
: null;
const unavailable = this.getVoiceFeatureUnavailableResponseMetadata(error);
this.sessionLogger.error(
{
err: error,
@@ -1900,9 +1910,7 @@ export class Session {
agentId: this.voiceModeAgentId,
accepted: false,
error: errorMessage,
...(unavailable ? { reasonCode: unavailable.reasonCode } : {}),
...(unavailable ? { retryable: unavailable.retryable } : {}),
...(unavailable ? { missingModelIds: unavailable.missingModelIds } : {}),
...unavailable,
},
});
return;