refactor: move voice-call realtime providers into extensions

This commit is contained in:
Peter Steinberger
2026-04-04 12:04:37 +09:00
parent 61f93540b2
commit a23ab9b906
90 changed files with 3134 additions and 792 deletions

View File

@@ -66,6 +66,7 @@ export type {
ProviderReplaySessionState,
ProviderResolveDynamicModelContext,
ProviderResolvedUsageAuth,
RealtimeTranscriptionProviderPlugin,
ProviderSanitizeReplayHistoryContext,
ProviderToolSchemaDiagnostic,
ProviderResolveUsageAuthContext,

View File

@@ -51,6 +51,7 @@ export type {
ProviderAuthContext,
ProviderAuthResult,
ProviderRuntimeModel,
RealtimeTranscriptionProviderPlugin,
SpeechProviderPlugin,
} from "../plugins/types.js";
export type {

View File

@@ -46,6 +46,7 @@ import type {
ProviderReplayPolicyContext,
ProviderReplaySessionEntry,
ProviderReplaySessionState,
RealtimeTranscriptionProviderPlugin,
ProviderResolvedUsageAuth,
ProviderResolveDynamicModelContext,
ProviderSanitizeReplayHistoryContext,
@@ -102,6 +103,7 @@ export type {
ProviderResolveDynamicModelContext,
ProviderNormalizeResolvedModelContext,
ProviderRuntimeModel,
RealtimeTranscriptionProviderPlugin,
SpeechProviderPlugin,
ProviderThinkingPolicyContext,
ProviderValidateReplayTurnsContext,

View File

@@ -0,0 +1,16 @@
export type { RealtimeTranscriptionProviderPlugin } from "../plugins/types.js";
export type {
RealtimeTranscriptionProviderConfig,
RealtimeTranscriptionProviderConfiguredContext,
RealtimeTranscriptionProviderId,
RealtimeTranscriptionProviderResolveConfigContext,
RealtimeTranscriptionSession,
RealtimeTranscriptionSessionCallbacks,
RealtimeTranscriptionSessionCreateRequest,
} from "../realtime-transcription/provider-types.js";
export {
canonicalizeRealtimeTranscriptionProviderId,
getRealtimeTranscriptionProvider,
listRealtimeTranscriptionProviders,
normalizeRealtimeTranscriptionProviderId,
} from "../realtime-transcription/provider-registry.js";

View File

@@ -0,0 +1,20 @@
export type { RealtimeVoiceProviderPlugin } from "../plugins/types.js";
export type {
RealtimeVoiceBridge,
RealtimeVoiceBridgeCallbacks,
RealtimeVoiceBridgeCreateRequest,
RealtimeVoiceCloseReason,
RealtimeVoiceProviderConfig,
RealtimeVoiceProviderConfiguredContext,
RealtimeVoiceProviderId,
RealtimeVoiceProviderResolveConfigContext,
RealtimeVoiceRole,
RealtimeVoiceTool,
RealtimeVoiceToolCallEvent,
} from "../realtime-voice/provider-types.js";
export {
canonicalizeRealtimeVoiceProviderId,
getRealtimeVoiceProvider,
listRealtimeVoiceProviders,
normalizeRealtimeVoiceProviderId,
} from "../realtime-voice/provider-registry.js";

View File

@@ -1,7 +1,12 @@
import { rmSync } from "node:fs";
import type { OpenClawConfig } from "../config/config.js";
import type { ResolvedTtsConfig } from "../tts/tts.js";
// Public speech helpers for bundled or third-party plugins.
//
// Keep this surface neutral. Provider plugins should not need to know about the
// bundled `speech-core` plugin id just to consume shared speech types/helpers.
// Keep this surface neutral and import-light. Provider builders commonly import
// this module just to get types and a few validation helpers, so avoid pulling
// in the heavy TTS runtime graph at module load time.
export type { SpeechProviderPlugin } from "../plugins/types.js";
export type {
@@ -22,14 +27,6 @@ export type {
TtsDirectiveParseResult,
} from "../tts/provider-types.js";
export {
scheduleCleanup,
summarizeText,
normalizeApplyTextNormalization,
normalizeLanguageCode,
normalizeSeed,
requireInRange,
} from "../tts/tts-core.js";
export { parseTtsDirectives } from "../tts/directives.js";
export {
canonicalizeSpeechProviderId,
@@ -44,3 +41,71 @@ export {
trimToUndefined,
truncateErrorDetail,
} from "../tts/provider-error-utils.js";
const TEMP_FILE_CLEANUP_DELAY_MS = 5 * 60 * 1000; // 5 minutes
export function requireInRange(value: number, min: number, max: number, label: string): void {
if (!Number.isFinite(value) || value < min || value > max) {
throw new Error(`${label} must be between ${min} and ${max}`);
}
}
export function normalizeLanguageCode(code?: string): string | undefined {
const trimmed = code?.trim();
if (!trimmed) {
return undefined;
}
const normalized = trimmed.toLowerCase();
if (!/^[a-z]{2}$/.test(normalized)) {
throw new Error("languageCode must be a 2-letter ISO 639-1 code (e.g. en, de, fr)");
}
return normalized;
}
export function normalizeApplyTextNormalization(mode?: string): "auto" | "on" | "off" | undefined {
const trimmed = mode?.trim();
if (!trimmed) {
return undefined;
}
const normalized = trimmed.toLowerCase();
if (normalized === "auto" || normalized === "on" || normalized === "off") {
return normalized;
}
throw new Error("applyTextNormalization must be one of: auto, on, off");
}
export function normalizeSeed(seed?: number): number | undefined {
if (seed == null) {
return undefined;
}
const next = Math.floor(seed);
if (!Number.isFinite(next) || next < 0 || next > 4_294_967_295) {
throw new Error("seed must be between 0 and 4294967295");
}
return next;
}
export function scheduleCleanup(
tempDir: string,
delayMs: number = TEMP_FILE_CLEANUP_DELAY_MS,
): void {
const timer = setTimeout(() => {
try {
rmSync(tempDir, { recursive: true, force: true });
} catch {
// ignore cleanup errors
}
}, delayMs);
timer.unref();
}
export async function summarizeText(params: {
text: string;
targetLength: number;
cfg: OpenClawConfig;
config: ResolvedTtsConfig;
timeoutMs: number;
}) {
const { summarizeText: summarizeTextRuntime } = await import("../tts/tts-core.js");
return summarizeTextRuntime(params);
}