@framers/agentos 0.9.122 → 0.9.124

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/core/llm/providers/implementations/OpenAIProvider.d.ts +35 -0
  2. package/dist/core/llm/providers/implementations/OpenAIProvider.d.ts.map +1 -1
  3. package/dist/core/llm/providers/implementations/OpenAIProvider.js +181 -1
  4. package/dist/core/llm/providers/implementations/OpenAIProvider.js.map +1 -1
  5. package/dist/core/llm/providers/model-effort.d.ts +20 -0
  6. package/dist/core/llm/providers/model-effort.d.ts.map +1 -1
  7. package/dist/core/llm/providers/model-effort.js +27 -0
  8. package/dist/core/llm/providers/model-effort.js.map +1 -1
  9. package/dist/io/avatar/index.d.ts +13 -0
  10. package/dist/io/avatar/index.d.ts.map +1 -0
  11. package/dist/io/avatar/index.js +12 -0
  12. package/dist/io/avatar/index.js.map +1 -0
  13. package/dist/io/avatar/providerCatalog.d.ts +24 -0
  14. package/dist/io/avatar/providerCatalog.d.ts.map +1 -0
  15. package/dist/io/avatar/providerCatalog.js +21 -0
  16. package/dist/io/avatar/providerCatalog.js.map +1 -0
  17. package/dist/io/avatar/providers/SimliAvatarProvider.d.ts +44 -0
  18. package/dist/io/avatar/providers/SimliAvatarProvider.d.ts.map +1 -0
  19. package/dist/io/avatar/providers/SimliAvatarProvider.js +90 -0
  20. package/dist/io/avatar/providers/SimliAvatarProvider.js.map +1 -0
  21. package/dist/io/avatar/providers/SimliServerSession.d.ts +78 -0
  22. package/dist/io/avatar/providers/SimliServerSession.d.ts.map +1 -0
  23. package/dist/io/avatar/providers/SimliServerSession.js +178 -0
  24. package/dist/io/avatar/providers/SimliServerSession.js.map +1 -0
  25. package/dist/io/avatar/types.d.ts +77 -0
  26. package/dist/io/avatar/types.d.ts.map +1 -0
  27. package/dist/io/avatar/types.js +19 -0
  28. package/dist/io/avatar/types.js.map +1 -0
  29. package/dist/io/voice-pipeline/env-constructor.d.ts +5 -3
  30. package/dist/io/voice-pipeline/env-constructor.d.ts.map +1 -1
  31. package/dist/io/voice-pipeline/env-constructor.js +25 -0
  32. package/dist/io/voice-pipeline/env-constructor.js.map +1 -1
  33. package/dist/io/voice-pipeline/provider-family.d.ts +1 -1
  34. package/dist/io/voice-pipeline/provider-family.d.ts.map +1 -1
  35. package/dist/io/voice-pipeline/provider-family.js +4 -0
  36. package/dist/io/voice-pipeline/provider-family.js.map +1 -1
  37. package/dist/io/voice-pipeline/providers/CartesiaBatchTTS.d.ts +79 -0
  38. package/dist/io/voice-pipeline/providers/CartesiaBatchTTS.d.ts.map +1 -0
  39. package/dist/io/voice-pipeline/providers/CartesiaBatchTTS.js +163 -0
  40. package/dist/io/voice-pipeline/providers/CartesiaBatchTTS.js.map +1 -0
  41. package/dist/io/voice-pipeline/providers/CartesiaStreamingTTS.d.ts +57 -0
  42. package/dist/io/voice-pipeline/providers/CartesiaStreamingTTS.d.ts.map +1 -0
  43. package/dist/io/voice-pipeline/providers/CartesiaStreamingTTS.js +235 -0
  44. package/dist/io/voice-pipeline/providers/CartesiaStreamingTTS.js.map +1 -0
  45. package/dist/io/voice-pipeline/providers/HumeBatchTTS.d.ts +81 -0
  46. package/dist/io/voice-pipeline/providers/HumeBatchTTS.d.ts.map +1 -0
  47. package/dist/io/voice-pipeline/providers/HumeBatchTTS.js +177 -0
  48. package/dist/io/voice-pipeline/providers/HumeBatchTTS.js.map +1 -0
  49. package/dist/io/voice-pipeline/providers/HumeStreamingTTS.d.ts +58 -0
  50. package/dist/io/voice-pipeline/providers/HumeStreamingTTS.d.ts.map +1 -0
  51. package/dist/io/voice-pipeline/providers/HumeStreamingTTS.js +240 -0
  52. package/dist/io/voice-pipeline/providers/HumeStreamingTTS.js.map +1 -0
  53. package/dist/io/voice-pipeline/providers/index.d.ts +4 -0
  54. package/dist/io/voice-pipeline/providers/index.d.ts.map +1 -1
  55. package/dist/io/voice-pipeline/providers/index.js +4 -0
  56. package/dist/io/voice-pipeline/providers/index.js.map +1 -1
  57. package/dist/io/voice-pipeline/types.d.ts +8 -0
  58. package/dist/io/voice-pipeline/types.d.ts.map +1 -1
  59. package/package.json +6 -1
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @module voice-pipeline/providers/CartesiaBatchTTS
3
+ *
4
+ * Batch text-to-speech via Cartesia's REST API (`POST /tts/bytes`).
5
+ * Implements {@link IBatchTTS} for one-shot narration synthesis.
6
+ *
7
+ * Contract notes (verified 2026-07-08): Bearer auth with the pinned
8
+ * `Cartesia-Version: 2026-03-01` header (the only valid value today); models
9
+ * sonic-3.5 (default) / sonic-3 / sonic-latest; prosody rides
10
+ * `generation_config` (the vendor's top-level `speed` is deprecated — the
11
+ * AgentOS `speed` knob maps into generation_config, clamped to Cartesia's
12
+ * documented 0.6–1.5 range). `opus` output has no Cartesia container and
13
+ * falls back to mp3, with the produced format reported on the result.
14
+ * Cartesia has no natural-language instruction surface, so
15
+ * `expressiveness.instructions` is ignored and never reported.
16
+ */
17
+ import type { IBatchTTS, BatchTTSConfig, BatchTTSResult } from '../types.js';
18
+ import { type HealthyProvider, type HealthCheckResult, type ProviderCapabilities } from '../HealthyProvider.js';
19
+ /** The only Cartesia-Version value the API accepts today. Pinned, not a knob. */
20
+ export declare const CARTESIA_VERSION = "2026-03-01";
21
+ /** Configuration for the Cartesia batch TTS provider. */
22
+ export interface CartesiaBatchTTSConfig {
23
+ /** Cartesia API key (`sk_car_…`). */
24
+ apiKey: string;
25
+ /** Default voice id. Cartesia voices are ids; there is no vendor default. */
26
+ voiceId: string;
27
+ /** Model identifier. @default 'sonic-3.5' */
28
+ model?: string;
29
+ /** Sample rate (Hz) used when `format: 'pcm'` is requested. @default 16000 */
30
+ pcmSampleRate?: number;
31
+ /** Base URL for the Cartesia API. @default 'https://api.cartesia.ai' */
32
+ baseUrl?: string;
33
+ /** Chain priority. Lower values are tried first. @default 85 */
34
+ priority?: number;
35
+ /** Optional capability overrides. */
36
+ capabilities?: Partial<ProviderCapabilities>;
37
+ /** Injectable health probe for tests. */
38
+ healthProbe?: (apiKey: string) => Promise<{
39
+ ok: boolean;
40
+ status: number;
41
+ latencyMs: number;
42
+ }>;
43
+ }
44
+ /**
45
+ * Batch (one-shot) TTS provider using Cartesia's `/tts/bytes` endpoint.
46
+ *
47
+ * Returns mp3 by default; `format: 'pcm'` requests raw `pcm_s16le` at the
48
+ * configured sample rate — the shape realtime avatar consumers (Simli) feed
49
+ * on directly.
50
+ */
51
+ export declare class CartesiaBatchTTS implements IBatchTTS, HealthyProvider {
52
+ readonly providerId = "cartesia-sonic";
53
+ readonly priority: number;
54
+ readonly capabilities: ProviderCapabilities;
55
+ /** API key pool for round-robin rotation and quota failover. */
56
+ private readonly keyPool;
57
+ /** Default voice id when none is provided in the synthesis config. */
58
+ private readonly voiceId;
59
+ /** Model identifier sent with each request. */
60
+ private readonly model;
61
+ /** Sample rate for raw pcm output. */
62
+ private readonly pcmSampleRate;
63
+ /** Base URL for all API requests. */
64
+ private readonly baseUrl;
65
+ /** Injectable health probe for tests. */
66
+ private readonly healthProbe;
67
+ constructor(config: CartesiaBatchTTSConfig);
68
+ healthCheck(): Promise<HealthCheckResult>;
69
+ /**
70
+ * Synthesize complete text into audio via Cartesia's REST API.
71
+ *
72
+ * @param text - The text to synthesize.
73
+ * @param config - Optional synthesis configuration (voice, model, format, speed).
74
+ * @returns Resolved {@link BatchTTSResult} with the audio buffer.
75
+ * @throws Error if the API returns a non-OK status.
76
+ */
77
+ synthesize(text: string, config?: BatchTTSConfig): Promise<BatchTTSResult>;
78
+ }
79
+ //# sourceMappingURL=CartesiaBatchTTS.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CartesiaBatchTTS.d.ts","sourceRoot":"","sources":["../../../../src/io/voice-pipeline/providers/CartesiaBatchTTS.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7E,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAC;AAG/B,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAqB7C,yDAAyD;AACzD,MAAM,WAAW,sBAAsB;IACrC,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,YAAY,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC7C,yCAAyC;IACzC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/F;AAED;;;;;;GAMG;AACH,qBAAa,gBAAiB,YAAW,SAAS,EAAE,eAAe;IACjE,QAAQ,CAAC,UAAU,oBAAoB;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;IAE5C,gEAAgE;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IAErC,sEAAsE;IACtE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,+CAA+C;IAC/C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAE/B,sCAAsC;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IAEvC,qCAAqC;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,yCAAyC;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqD;gBAErE,MAAM,EAAE,sBAAsB;IAiBpC,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;IA6B/C;;;;;;;OAOG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;CAyEjF"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * @module voice-pipeline/providers/CartesiaBatchTTS
3
+ *
4
+ * Batch text-to-speech via Cartesia's REST API (`POST /tts/bytes`).
5
+ * Implements {@link IBatchTTS} for one-shot narration synthesis.
6
+ *
7
+ * Contract notes (verified 2026-07-08): Bearer auth with the pinned
8
+ * `Cartesia-Version: 2026-03-01` header (the only valid value today); models
9
+ * sonic-3.5 (default) / sonic-3 / sonic-latest; prosody rides
10
+ * `generation_config` (the vendor's top-level `speed` is deprecated — the
11
+ * AgentOS `speed` knob maps into generation_config, clamped to Cartesia's
12
+ * documented 0.6–1.5 range). `opus` output has no Cartesia container and
13
+ * falls back to mp3, with the produced format reported on the result.
14
+ * Cartesia has no natural-language instruction surface, so
15
+ * `expressiveness.instructions` is ignored and never reported.
16
+ */
17
+ import { ApiKeyPool } from '../../../core/providers/ApiKeyPool.js';
18
+ import { isQuotaError } from '../../../core/providers/quotaErrors.js';
19
+ import { defaultCapabilities, } from '../HealthyProvider.js';
20
+ import { VoicePipelineError } from '../VoicePipelineError.js';
21
+ /** The only Cartesia-Version value the API accepts today. Pinned, not a knob. */
22
+ export const CARTESIA_VERSION = '2026-03-01';
23
+ /** Approximate bytes per second for 128kbps MP3 audio (duration estimate). */
24
+ const BYTES_PER_SEC_MP3 = 16000;
25
+ /**
26
+ * Per-request synthesize timeout. Caps how long a single Cartesia call can
27
+ * hang before the batch fallback chain takes over (same rationale as the
28
+ * ElevenLabs provider's cap — a wedged socket must not stall callers).
29
+ */
30
+ const SYNTHESIZE_TIMEOUT_MS = 60000;
31
+ async function defaultCartesiaProbe(apiKey) {
32
+ const start = Date.now();
33
+ const res = await fetch('https://api.cartesia.ai/voices?limit=1', {
34
+ headers: { Authorization: `Bearer ${apiKey}`, 'Cartesia-Version': CARTESIA_VERSION },
35
+ signal: AbortSignal.timeout(1000),
36
+ });
37
+ return { ok: res.ok, status: res.status, latencyMs: Date.now() - start };
38
+ }
39
+ /**
40
+ * Batch (one-shot) TTS provider using Cartesia's `/tts/bytes` endpoint.
41
+ *
42
+ * Returns mp3 by default; `format: 'pcm'` requests raw `pcm_s16le` at the
43
+ * configured sample rate — the shape realtime avatar consumers (Simli) feed
44
+ * on directly.
45
+ */
46
+ export class CartesiaBatchTTS {
47
+ constructor(config) {
48
+ this.providerId = 'cartesia-sonic';
49
+ this.keyPool = new ApiKeyPool(config.apiKey);
50
+ this.voiceId = config.voiceId;
51
+ this.model = config.model ?? 'sonic-3.5';
52
+ this.pcmSampleRate = config.pcmSampleRate ?? 16000;
53
+ this.baseUrl = config.baseUrl ?? 'https://api.cartesia.ai';
54
+ this.priority = config.priority ?? 85;
55
+ this.capabilities = defaultCapabilities({
56
+ languages: ['*'],
57
+ streaming: false,
58
+ costTier: 'standard',
59
+ latencyClass: 'batch',
60
+ ...(config.capabilities ?? {}),
61
+ });
62
+ this.healthProbe = config.healthProbe ?? defaultCartesiaProbe;
63
+ }
64
+ async healthCheck() {
65
+ if (!this.keyPool.hasKeys) {
66
+ return { ok: false, error: { class: 'auth', message: 'no api key available' } };
67
+ }
68
+ const key = this.keyPool.next();
69
+ try {
70
+ const res = await this.healthProbe(key);
71
+ if (res.ok)
72
+ return { ok: true, latencyMs: res.latencyMs };
73
+ const classified = VoicePipelineError.classifyError(new Error(`HTTP ${res.status}`), { kind: 'tts', provider: this.providerId });
74
+ return {
75
+ ok: false,
76
+ latencyMs: res.latencyMs,
77
+ error: { class: classified.errorClass, message: `HTTP ${res.status}` },
78
+ };
79
+ }
80
+ catch (err) {
81
+ const classified = VoicePipelineError.classifyError(err, {
82
+ kind: 'tts',
83
+ provider: this.providerId,
84
+ });
85
+ return {
86
+ ok: false,
87
+ error: { class: classified.errorClass, message: classified.message },
88
+ };
89
+ }
90
+ }
91
+ /**
92
+ * Synthesize complete text into audio via Cartesia's REST API.
93
+ *
94
+ * @param text - The text to synthesize.
95
+ * @param config - Optional synthesis configuration (voice, model, format, speed).
96
+ * @returns Resolved {@link BatchTTSResult} with the audio buffer.
97
+ * @throws Error if the API returns a non-OK status.
98
+ */
99
+ async synthesize(text, config) {
100
+ // opus has no Cartesia container — fall back to mp3 and report what was
101
+ // actually produced on the result.
102
+ const requested = config?.format ?? 'mp3';
103
+ const format = requested === 'pcm' ? 'pcm' : 'mp3';
104
+ const outputFormat = format === 'pcm'
105
+ ? { container: 'raw', encoding: 'pcm_s16le', sample_rate: this.pcmSampleRate }
106
+ : { container: 'mp3', bit_rate: 128000, sample_rate: 44100 };
107
+ // Caller speed (top-level wins over expressiveness, per BatchTTSConfig
108
+ // docs) → generation_config, clamped to the documented 0.6–1.5 range.
109
+ const rawSpeed = config?.speed ?? config?.expressiveness?.speed;
110
+ const speed = typeof rawSpeed === 'number' && Number.isFinite(rawSpeed)
111
+ ? Math.min(1.5, Math.max(0.6, rawSpeed))
112
+ : undefined;
113
+ const applied = [];
114
+ if (speed != null)
115
+ applied.push('speed');
116
+ const body = {
117
+ model_id: config?.model ?? this.model,
118
+ transcript: text,
119
+ voice: { mode: 'id', id: config?.voice ?? this.voiceId },
120
+ output_format: outputFormat,
121
+ ...(speed != null ? { generation_config: { speed } } : {}),
122
+ ...(config?.providerOptions ?? {}),
123
+ };
124
+ const doFetch = (key) => fetch(`${this.baseUrl}/tts/bytes`, {
125
+ method: 'POST',
126
+ headers: {
127
+ Authorization: `Bearer ${key}`,
128
+ 'Cartesia-Version': CARTESIA_VERSION,
129
+ 'Content-Type': 'application/json',
130
+ },
131
+ body: JSON.stringify(body),
132
+ signal: AbortSignal.timeout(SYNTHESIZE_TIMEOUT_MS),
133
+ });
134
+ const key = this.keyPool.next();
135
+ let res = await doFetch(key);
136
+ if (!res.ok && this.keyPool.size > 1) {
137
+ const errBody = await res.text().catch(() => '');
138
+ if (isQuotaError(res.status, errBody)) {
139
+ this.keyPool.markExhausted(key);
140
+ res = await doFetch(this.keyPool.next());
141
+ }
142
+ else {
143
+ throw new Error(`Cartesia TTS failed: ${res.status} ${errBody.slice(0, 200)}`);
144
+ }
145
+ }
146
+ if (!res.ok) {
147
+ const detail = await res.text().catch(() => '');
148
+ throw new Error(`Cartesia TTS failed: ${res.status} ${detail.slice(0, 200)}`);
149
+ }
150
+ const audio = Buffer.from(await res.arrayBuffer());
151
+ const durationMs = format === 'pcm'
152
+ ? Math.round((audio.byteLength / (this.pcmSampleRate * 2)) * 1000)
153
+ : Math.round((audio.byteLength / BYTES_PER_SEC_MP3) * 1000);
154
+ return {
155
+ audio,
156
+ format,
157
+ durationMs,
158
+ provider: this.providerId,
159
+ ...(applied.length > 0 ? { appliedExpressiveness: applied } : {}),
160
+ };
161
+ }
162
+ }
163
+ //# sourceMappingURL=CartesiaBatchTTS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CartesiaBatchTTS.js","sourceRoot":"","sources":["../../../../src/io/voice-pipeline/providers/CartesiaBatchTTS.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @module voice-pipeline/providers/CartesiaStreamingTTS
3
+ *
4
+ * Streaming text-to-speech via Cartesia's WebSocket API. Implements
5
+ * {@link IStreamingTTS} for realtime token-in / audio-out synthesis.
6
+ *
7
+ * Contract notes (verified 2026-07-08): connect to
8
+ * `wss://api.cartesia.ai/tts/websocket?cartesia_version=…&api_key=…`;
9
+ * incremental transcript messages share a `context_id` and carry
10
+ * `continue: true`; a context is finished with `continue: false` and the
11
+ * server acknowledges completion with a `done` message.
12
+ *
13
+ * ⚠️ Vendor `cancel` is SOFT — it only halts requests that have not begun
14
+ * generating. AgentOS `cancel()` demands immediate stop/discard, so this
15
+ * session implements HARD cancel locally: the cancelled context id joins a
16
+ * dead-set whose inbound chunks are dropped, a best-effort vendor cancel is
17
+ * sent, and the next synthesis rotates to a fresh context id.
18
+ */
19
+ import type { IStreamingTTS, StreamingTTSConfig, StreamingTTSSession } from '../types.js';
20
+ import { type HealthyProvider, type HealthCheckResult, type ProviderCapabilities } from '../HealthyProvider.js';
21
+ /** Configuration for the Cartesia streaming TTS provider. */
22
+ export interface CartesiaStreamingTTSConfig {
23
+ /** Cartesia API key (`sk_car_…`). */
24
+ apiKey: string;
25
+ /** Default voice id. Cartesia voices are ids; there is no vendor default. */
26
+ voiceId: string;
27
+ /** Model identifier. @default 'sonic-3.5' */
28
+ model?: string;
29
+ /** Output sample rate (Hz) for raw pcm chunks. @default 16000 */
30
+ sampleRate?: number;
31
+ /** WebSocket endpoint. @default 'wss://api.cartesia.ai/tts/websocket' */
32
+ wsUrl?: string;
33
+ /** Chain priority. Lower values are tried first. @default 12 */
34
+ priority?: number;
35
+ /** Optional capability overrides. */
36
+ capabilities?: Partial<ProviderCapabilities>;
37
+ }
38
+ /**
39
+ * Streaming TTS provider for Cartesia sonic models.
40
+ *
41
+ * Sessions default to raw pcm_s16le chunks at 16 kHz — directly feedable to
42
+ * realtime avatar consumers — and honor `format: 'mp3'` when asked.
43
+ */
44
+ export declare class CartesiaStreamingTTS implements IStreamingTTS, HealthyProvider {
45
+ readonly providerId = "cartesia-sonic-stream";
46
+ readonly priority: number;
47
+ readonly capabilities: ProviderCapabilities;
48
+ private readonly keyPool;
49
+ private readonly voiceId;
50
+ private readonly model;
51
+ private readonly sampleRate;
52
+ private readonly wsUrl;
53
+ constructor(config: CartesiaStreamingTTSConfig);
54
+ healthCheck(): Promise<HealthCheckResult>;
55
+ startSession(config?: StreamingTTSConfig): Promise<StreamingTTSSession>;
56
+ }
57
+ //# sourceMappingURL=CartesiaStreamingTTS.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CartesiaStreamingTTS.d.ts","sourceRoot":"","sources":["../../../../src/io/voice-pipeline/providers/CartesiaStreamingTTS.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAEV,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAC;AAI/B,6DAA6D;AAC7D,MAAM,WAAW,0BAA0B;IACzC,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,YAAY,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAC9C;AA2JD;;;;;GAKG;AACH,qBAAa,oBAAqB,YAAW,aAAa,EAAE,eAAe;IACzE,QAAQ,CAAC,UAAU,2BAA2B;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;IAE5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,MAAM,EAAE,0BAA0B;IAgBxC,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAmCzC,YAAY,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAmB9E"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * @module voice-pipeline/providers/CartesiaStreamingTTS
3
+ *
4
+ * Streaming text-to-speech via Cartesia's WebSocket API. Implements
5
+ * {@link IStreamingTTS} for realtime token-in / audio-out synthesis.
6
+ *
7
+ * Contract notes (verified 2026-07-08): connect to
8
+ * `wss://api.cartesia.ai/tts/websocket?cartesia_version=…&api_key=…`;
9
+ * incremental transcript messages share a `context_id` and carry
10
+ * `continue: true`; a context is finished with `continue: false` and the
11
+ * server acknowledges completion with a `done` message.
12
+ *
13
+ * ⚠️ Vendor `cancel` is SOFT — it only halts requests that have not begun
14
+ * generating. AgentOS `cancel()` demands immediate stop/discard, so this
15
+ * session implements HARD cancel locally: the cancelled context id joins a
16
+ * dead-set whose inbound chunks are dropped, a best-effort vendor cancel is
17
+ * sent, and the next synthesis rotates to a fresh context id.
18
+ */
19
+ import { EventEmitter } from 'node:events';
20
+ import WebSocket from 'ws';
21
+ import { randomUUID } from 'node:crypto';
22
+ import { ApiKeyPool } from '../../../core/providers/ApiKeyPool.js';
23
+ import { defaultCapabilities, } from '../HealthyProvider.js';
24
+ import { VoicePipelineError } from '../VoicePipelineError.js';
25
+ import { CARTESIA_VERSION } from './CartesiaBatchTTS.js';
26
+ /**
27
+ * One live Cartesia synthesis session over a shared WebSocket. Contexts are
28
+ * rotated on cancel so late chunks from a killed utterance can never leak
29
+ * into the next one.
30
+ */
31
+ class CartesiaStreamingTTSSession extends EventEmitter {
32
+ constructor(opts) {
33
+ super();
34
+ this.opts = opts;
35
+ /** Exposed for tests (mirrors the ElevenLabs session's introspection seam). */
36
+ this.ws = null;
37
+ /** Context id for the CURRENT utterance; rotated on cancel. */
38
+ this.contextId = randomUUID();
39
+ /** Context ids hard-cancelled locally — inbound chunks for these drop. */
40
+ this.deadContexts = new Set();
41
+ /** Resolvers waiting on the current context's `done`. */
42
+ this.flushResolvers = [];
43
+ this.closed = false;
44
+ /** Tokens pushed since the last done/cancel — stamped on each chunk so
45
+ * barge-in handlers can report the interrupted remainder. */
46
+ this.accumulatedText = '';
47
+ }
48
+ /** Open the socket; resolves once the connection is live. */
49
+ async connect() {
50
+ return new Promise((resolve, reject) => {
51
+ const ws = new WebSocket(this.opts.url);
52
+ this.ws = ws;
53
+ ws.on('open', () => resolve());
54
+ ws.on('error', (err) => {
55
+ if (this.closed)
56
+ return;
57
+ this.emit('error', err);
58
+ reject(err);
59
+ });
60
+ ws.on('close', () => {
61
+ if (!this.closed)
62
+ this.emit('close');
63
+ });
64
+ ws.on('message', (raw) => this.onMessage(raw));
65
+ });
66
+ }
67
+ onMessage(raw) {
68
+ let msg;
69
+ try {
70
+ msg = JSON.parse(String(raw));
71
+ }
72
+ catch {
73
+ return; // Non-JSON frames are not part of the documented surface.
74
+ }
75
+ // HARD-cancel enforcement: anything addressed to a dead context drops.
76
+ if (msg.context_id && this.deadContexts.has(msg.context_id))
77
+ return;
78
+ if (msg.type === 'chunk' && typeof msg.data === 'string') {
79
+ const audio = Buffer.from(msg.data, 'base64');
80
+ const chunk = {
81
+ audio,
82
+ format: this.opts.format,
83
+ sampleRate: this.opts.sampleRate,
84
+ durationMs: this.opts.format === 'pcm'
85
+ ? Math.max(1, Math.round((audio.byteLength / (this.opts.sampleRate * 2)) * 1000))
86
+ : Math.max(1, Math.round((audio.byteLength / 16000) * 1000)),
87
+ text: this.accumulatedText,
88
+ };
89
+ this.emit('audio', chunk);
90
+ return;
91
+ }
92
+ if (msg.type === 'done') {
93
+ this.accumulatedText = '';
94
+ const resolvers = this.flushResolvers;
95
+ this.flushResolvers = [];
96
+ this.emit('flush_complete');
97
+ for (const r of resolvers)
98
+ r();
99
+ return;
100
+ }
101
+ if (msg.type === 'error') {
102
+ this.emit('error', new Error(`Cartesia stream error: ${msg.error ?? 'unknown'}`));
103
+ }
104
+ }
105
+ send(payload) {
106
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
107
+ return;
108
+ this.ws.send(JSON.stringify(payload));
109
+ }
110
+ /** Shared request envelope for the current context. */
111
+ envelope() {
112
+ return {
113
+ context_id: this.contextId,
114
+ model_id: this.opts.model,
115
+ voice: { mode: 'id', id: this.opts.voiceId },
116
+ output_format: this.opts.format === 'pcm'
117
+ ? { container: 'raw', encoding: 'pcm_s16le', sample_rate: this.opts.sampleRate }
118
+ : { container: 'mp3', bit_rate: 128000, sample_rate: 44100 },
119
+ };
120
+ }
121
+ pushTokens(tokens) {
122
+ if (this.closed || tokens.length === 0)
123
+ return;
124
+ this.accumulatedText += tokens;
125
+ this.send({ ...this.envelope(), transcript: tokens, continue: true });
126
+ }
127
+ async flush() {
128
+ if (this.closed)
129
+ return;
130
+ const done = new Promise((resolve) => this.flushResolvers.push(resolve));
131
+ // continue:false finishes the context; the server answers with `done`.
132
+ this.send({ ...this.envelope(), transcript: '', continue: false });
133
+ await done;
134
+ }
135
+ cancel() {
136
+ if (this.closed)
137
+ return;
138
+ const dead = this.contextId;
139
+ this.deadContexts.add(dead);
140
+ // Best-effort vendor cancel (only stops not-yet-generating requests);
141
+ // correctness comes from the local dead-context drop + rotation.
142
+ this.send({ context_id: dead, cancel: true });
143
+ this.contextId = randomUUID();
144
+ this.flushResolvers = [];
145
+ this.accumulatedText = '';
146
+ }
147
+ close() {
148
+ if (this.closed)
149
+ return;
150
+ this.closed = true;
151
+ try {
152
+ this.ws?.close();
153
+ }
154
+ catch {
155
+ /* already gone */
156
+ }
157
+ this.emit('close');
158
+ }
159
+ }
160
+ /**
161
+ * Streaming TTS provider for Cartesia sonic models.
162
+ *
163
+ * Sessions default to raw pcm_s16le chunks at 16 kHz — directly feedable to
164
+ * realtime avatar consumers — and honor `format: 'mp3'` when asked.
165
+ */
166
+ export class CartesiaStreamingTTS {
167
+ constructor(config) {
168
+ this.providerId = 'cartesia-sonic-stream';
169
+ this.keyPool = new ApiKeyPool(config.apiKey);
170
+ this.voiceId = config.voiceId;
171
+ this.model = config.model ?? 'sonic-3.5';
172
+ this.sampleRate = config.sampleRate ?? 16000;
173
+ this.wsUrl = config.wsUrl ?? 'wss://api.cartesia.ai/tts/websocket';
174
+ this.priority = config.priority ?? 12;
175
+ this.capabilities = defaultCapabilities({
176
+ languages: ['*'],
177
+ streaming: true,
178
+ costTier: 'standard',
179
+ latencyClass: 'realtime',
180
+ ...(config.capabilities ?? {}),
181
+ });
182
+ }
183
+ async healthCheck() {
184
+ if (!this.keyPool.hasKeys) {
185
+ return { ok: false, error: { class: 'auth', message: 'no api key available' } };
186
+ }
187
+ const start = Date.now();
188
+ try {
189
+ const res = await fetch('https://api.cartesia.ai/voices?limit=1', {
190
+ headers: {
191
+ Authorization: `Bearer ${this.keyPool.next()}`,
192
+ 'Cartesia-Version': CARTESIA_VERSION,
193
+ },
194
+ signal: AbortSignal.timeout(1000),
195
+ });
196
+ if (res.ok)
197
+ return { ok: true, latencyMs: Date.now() - start };
198
+ const classified = VoicePipelineError.classifyError(new Error(`HTTP ${res.status}`), { kind: 'tts', provider: this.providerId });
199
+ return {
200
+ ok: false,
201
+ latencyMs: Date.now() - start,
202
+ error: { class: classified.errorClass, message: `HTTP ${res.status}` },
203
+ };
204
+ }
205
+ catch (err) {
206
+ const classified = VoicePipelineError.classifyError(err, {
207
+ kind: 'tts',
208
+ provider: this.providerId,
209
+ });
210
+ return {
211
+ ok: false,
212
+ error: { class: classified.errorClass, message: classified.message },
213
+ };
214
+ }
215
+ }
216
+ async startSession(config) {
217
+ const key = this.keyPool.next();
218
+ // Browser-style query auth keeps the handshake header-free (the ws
219
+ // package would support headers, but query auth matches the documented
220
+ // access_token/api_key surface and simplifies test doubles).
221
+ const url = `${this.wsUrl}?cartesia_version=${encodeURIComponent(CARTESIA_VERSION)}` +
222
+ `&api_key=${encodeURIComponent(key)}`;
223
+ const format = config?.format === 'mp3' ? 'mp3' : 'pcm';
224
+ const session = new CartesiaStreamingTTSSession({
225
+ url,
226
+ voiceId: config?.voice ?? this.voiceId,
227
+ model: this.model,
228
+ sampleRate: config?.sampleRate ?? this.sampleRate,
229
+ format,
230
+ });
231
+ await session.connect();
232
+ return session;
233
+ }
234
+ }
235
+ //# sourceMappingURL=CartesiaStreamingTTS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CartesiaStreamingTTS.js","sourceRoot":"","sources":["../../../../src/io/voice-pipeline/providers/CartesiaStreamingTTS.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @module voice-pipeline/providers/HumeBatchTTS
3
+ *
4
+ * Batch text-to-speech via Hume's Octave API (`POST /v0/tts`). Implements
5
+ * {@link IBatchTTS} for one-shot expressive narration synthesis.
6
+ *
7
+ * Contract notes (verified 2026-07-08):
8
+ * - Auth is the `X-Hume-Api-Key` header.
9
+ * - `version` is OMITTED by default so requests auto-route: `version: 2`
10
+ * REQUIRES an explicit voice, and a hard-coded default would break
11
+ * voice-less calls. Callers opt in via `providerOptions.version` and own
12
+ * that constraint.
13
+ * - `expressiveness.instructions` renders as the utterance `description`
14
+ * (acting directions) — Hume is the one core provider with a
15
+ * natural-language instruction surface — and is reported in
16
+ * `appliedExpressiveness` when consumed.
17
+ * - Response audio arrives base64-encoded in `generations[].audio` with a
18
+ * vendor-reported `duration` in seconds.
19
+ * - `opus` output is unsupported by the vendor and falls back to mp3, with
20
+ * the produced format reported on the result.
21
+ */
22
+ import type { IBatchTTS, BatchTTSConfig, BatchTTSResult } from '../types.js';
23
+ import { type HealthyProvider, type HealthCheckResult, type ProviderCapabilities } from '../HealthyProvider.js';
24
+ /** A Hume voice reference: by id or by name, from the Hume or custom library. */
25
+ export interface HumeVoiceRef {
26
+ id?: string;
27
+ name?: string;
28
+ /** @default 'HUME_AI' */
29
+ provider?: 'HUME_AI' | 'CUSTOM_VOICE';
30
+ }
31
+ /** Configuration for the Hume batch TTS provider. */
32
+ export interface HumeBatchTTSConfig {
33
+ /** Hume API key. */
34
+ apiKey: string;
35
+ /** Default voice. Optional — voice-less requests auto-generate a voice
36
+ * from the description, which is a first-class Octave behavior. */
37
+ voice?: HumeVoiceRef;
38
+ /** Base URL for the Hume API. @default 'https://api.hume.ai' */
39
+ baseUrl?: string;
40
+ /** Chain priority. Lower values are tried first. @default 86 */
41
+ priority?: number;
42
+ /** Optional capability overrides. */
43
+ capabilities?: Partial<ProviderCapabilities>;
44
+ /** Injectable health probe for tests. */
45
+ healthProbe?: (apiKey: string) => Promise<{
46
+ ok: boolean;
47
+ status: number;
48
+ latencyMs: number;
49
+ }>;
50
+ }
51
+ /**
52
+ * Batch (one-shot) TTS provider using Hume's Octave `/v0/tts` endpoint.
53
+ */
54
+ export declare class HumeBatchTTS implements IBatchTTS, HealthyProvider {
55
+ readonly providerId = "hume-octave";
56
+ readonly priority: number;
57
+ readonly capabilities: ProviderCapabilities;
58
+ /** API key pool for round-robin rotation and quota failover. */
59
+ private readonly keyPool;
60
+ /** Default voice when none is provided in the synthesis config. */
61
+ private readonly voice;
62
+ /** Base URL for all API requests. */
63
+ private readonly baseUrl;
64
+ /** Injectable health probe for tests. */
65
+ private readonly healthProbe;
66
+ constructor(config: HumeBatchTTSConfig);
67
+ healthCheck(): Promise<HealthCheckResult>;
68
+ /** Normalize a voice reference into Hume's `{id|name, provider}` shape. */
69
+ private static toHumeVoice;
70
+ /**
71
+ * Synthesize complete text into audio via Hume's Octave REST API.
72
+ *
73
+ * @param text - The text to synthesize.
74
+ * @param config - Optional synthesis configuration (voice, format, speed,
75
+ * expressiveness.instructions → description).
76
+ * @returns Resolved {@link BatchTTSResult} with the decoded audio buffer.
77
+ * @throws Error if the API returns a non-OK status or an empty generation.
78
+ */
79
+ synthesize(text: string, config?: BatchTTSConfig): Promise<BatchTTSResult>;
80
+ }
81
+ //# sourceMappingURL=HumeBatchTTS.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HumeBatchTTS.d.ts","sourceRoot":"","sources":["../../../../src/io/voice-pipeline/providers/HumeBatchTTS.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7E,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAC;AAS/B,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yBAAyB;IACzB,QAAQ,CAAC,EAAE,SAAS,GAAG,cAAc,CAAC;CACvC;AAWD,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IACjC,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf;wEACoE;IACpE,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,YAAY,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC7C,yCAAyC;IACzC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/F;AAED;;GAEG;AACH,qBAAa,YAAa,YAAW,SAAS,EAAE,eAAe;IAC7D,QAAQ,CAAC,UAAU,iBAAiB;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;IAE5C,gEAAgE;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IAErC,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IAEjD,qCAAqC;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,yCAAyC;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiD;gBAEjE,MAAM,EAAE,kBAAkB;IAehC,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;IA6B/C,2EAA2E;IAC3E,OAAO,CAAC,MAAM,CAAC,WAAW;IAY1B;;;;;;;;OAQG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;CAiFjF"}