refactor(session): simplify voice readiness gating flow
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user