import type { VoiceProvider } from "@rakazo/adapter-kit"; import { KokoroVoiceProvider } from "./kokoro-voice.js"; import { SCRIPTED_VOICE_CATALOG_ENTRY, ScriptedVoiceProvider } from "./scripted-voice.js"; export const VOICE_CATALOG = [ { id: "kokoro", name: "Local voice", description: "Free local speech on your server.", transcribe: false, }, ] as const; export { SCRIPTED_VOICE_CATALOG_ENTRY }; export type HostedVoiceProviderId = (typeof VOICE_CATALOG)[number]["id"]; export type VoiceProviderId = HostedVoiceProviderId | "scripted"; export function scriptedVoiceEnabled() { return process.env.AGENT_RUNTIME === "scripted"; } export function listVoiceCatalog() { return scriptedVoiceEnabled() ? [...VOICE_CATALOG, SCRIPTED_VOICE_CATALOG_ENTRY] : [...VOICE_CATALOG]; } export function voiceCatalogEntry(id: string) { if (id === SCRIPTED_VOICE_CATALOG_ENTRY.id) return SCRIPTED_VOICE_CATALOG_ENTRY; return VOICE_CATALOG.find((entry) => entry.id === id); } export function isVoiceProviderId(value: string): value is VoiceProviderId { if (value === "scripted") return scriptedVoiceEnabled(); return VOICE_CATALOG.some((entry) => entry.id === value); } /** Construct the provider adapter selected by a persisted voice credential. */ export function createVoiceProvider(kind: string): VoiceProvider { switch (kind) { case "kokoro": return new KokoroVoiceProvider(); case "scripted": if (!scriptedVoiceEnabled()) break; return new ScriptedVoiceProvider(); default: break; } throw new Error(`Unknown voice provider "${kind}". Use kokoro.`); } export class NoVoiceConfigured extends Error { readonly reason: "key" | "voice"; constructor(reason: "key" | "voice") { super( reason === "key" ? "Turn on speaking in Voice settings." : "Pick a voice in Voice settings.", ); this.reason = reason; this.name = "NoVoiceConfigured"; } } export const MAX_SPEAK_CHARS = 2000; export const MAX_TRANSCRIBE_BYTES = 8 * 1024 * 1024;