@tanstack/ai-elevenlabs 0.2.33 → 0.2.35

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.
@@ -1,218 +1,213 @@
1
1
  import { Conversation } from "@elevenlabs/client";
2
2
  import { resolveDebugOption } from "@tanstack/ai/adapter-internals";
3
+ //#region src/realtime/adapter.ts
4
+ /**
5
+ * Creates an ElevenLabs realtime adapter for client-side use.
6
+ *
7
+ * Wraps the @elevenlabs/client SDK for voice conversations.
8
+ *
9
+ * @param options - Optional configuration
10
+ * @returns A RealtimeAdapter for use with RealtimeClient
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { RealtimeClient } from '@tanstack/ai-client'
15
+ * import { elevenlabsRealtime } from '@tanstack/ai-elevenlabs'
16
+ *
17
+ * const client = new RealtimeClient({
18
+ * getToken: () => fetch('/api/realtime-token').then(r => r.json()),
19
+ * adapter: elevenlabsRealtime(),
20
+ * })
21
+ * ```
22
+ */
3
23
  function elevenlabsRealtime(options = {}) {
4
- const logger = resolveDebugOption(options.debug);
5
- return {
6
- provider: "elevenlabs",
7
- async connect(token, clientToolDefs) {
8
- logger.request(`activity=realtime provider=elevenlabs`, {
9
- provider: "elevenlabs"
10
- });
11
- return createElevenLabsConnection(token, options, logger, clientToolDefs);
12
- }
13
- };
24
+ const logger = resolveDebugOption(options.debug);
25
+ return {
26
+ provider: "elevenlabs",
27
+ async connect(token, clientToolDefs) {
28
+ logger.request(`activity=realtime provider=elevenlabs`, { provider: "elevenlabs" });
29
+ return createElevenLabsConnection(token, options, logger, clientToolDefs);
30
+ }
31
+ };
14
32
  }
33
+ /**
34
+ * Creates a connection to ElevenLabs conversational AI
35
+ */
15
36
  async function createElevenLabsConnection(token, _options, logger, clientToolDefs) {
16
- const eventHandlers = /* @__PURE__ */ new Map();
17
- let conversation = null;
18
- let messageIdCounter = 0;
19
- const emptyFrequencyData = new Uint8Array(128);
20
- const emptyTimeDomainData = new Uint8Array(128).fill(128);
21
- function emit(event, payload) {
22
- const handlers = eventHandlers.get(event);
23
- if (handlers) {
24
- for (const handler of handlers) {
25
- handler(payload);
26
- }
27
- }
28
- }
29
- function generateMessageId() {
30
- return `el-msg-${Date.now()}-${++messageIdCounter}`;
31
- }
32
- const elevenLabsClientTools = {};
33
- if (clientToolDefs) {
34
- for (const tool of clientToolDefs) {
35
- elevenLabsClientTools[tool.name] = async (params) => {
36
- if (tool.execute) {
37
- const result = await tool.execute(params);
38
- return typeof result === "string" ? result : JSON.stringify(result);
39
- }
40
- return JSON.stringify({
41
- error: `No execute function for tool ${tool.name}`
42
- });
43
- };
44
- }
45
- }
46
- const sessionOptions = {
47
- signedUrl: token.token,
48
- onConnect: () => {
49
- logger.provider(`provider=elevenlabs direction=in type=connect`, {
50
- frame: { type: "connect" }
51
- });
52
- emit("status_change", { status: "connected" });
53
- emit("mode_change", { mode: "listening" });
54
- },
55
- onDisconnect: () => {
56
- logger.provider(`provider=elevenlabs direction=in type=disconnect`, {
57
- frame: { type: "disconnect" }
58
- });
59
- emit("status_change", { status: "idle" });
60
- emit("mode_change", { mode: "idle" });
61
- },
62
- onModeChange: ({ mode }) => {
63
- logger.provider(`provider=elevenlabs direction=in type=mode_change`, {
64
- frame: { type: "mode_change", mode }
65
- });
66
- const mappedMode = mode === "speaking" ? "speaking" : "listening";
67
- emit("mode_change", { mode: mappedMode });
68
- },
69
- onMessage: ({ message, source }) => {
70
- logger.provider(`provider=elevenlabs direction=in type=message`, {
71
- frame: { type: "message", source, message }
72
- });
73
- const role = source === "user" ? "user" : "assistant";
74
- if (role === "user") {
75
- emit("transcript", {
76
- role,
77
- transcript: message,
78
- isFinal: true
79
- });
80
- } else {
81
- const realtimeMessage = {
82
- id: generateMessageId(),
83
- role,
84
- timestamp: Date.now(),
85
- parts: [{ type: "audio", transcript: message }]
86
- };
87
- emit("message_complete", { message: realtimeMessage });
88
- }
89
- },
90
- onError: (error) => {
91
- logger.errors("elevenlabs.realtime fatal", {
92
- error,
93
- source: "elevenlabs.realtime"
94
- });
95
- emit("error", {
96
- error: new Error(
97
- typeof error === "string" ? error : error.message || "Unknown error"
98
- )
99
- });
100
- }
101
- };
102
- if (Object.keys(elevenLabsClientTools).length > 0) {
103
- sessionOptions.clientTools = elevenLabsClientTools;
104
- }
105
- try {
106
- conversation = await Conversation.startSession(
107
- sessionOptions
108
- );
109
- } catch (error) {
110
- logger.errors("elevenlabs.realtime fatal", {
111
- error,
112
- source: "elevenlabs.realtime"
113
- });
114
- throw error;
115
- }
116
- const connection = {
117
- async disconnect() {
118
- if (conversation) {
119
- await conversation.endSession();
120
- conversation = null;
121
- }
122
- emit("status_change", { status: "idle" });
123
- },
124
- async startAudioCapture() {
125
- emit("mode_change", { mode: "listening" });
126
- },
127
- stopAudioCapture() {
128
- emit("mode_change", { mode: "idle" });
129
- },
130
- sendText(text) {
131
- if (!conversation) return;
132
- logger.provider(`provider=elevenlabs direction=out type=user_message`, {
133
- frame: { type: "user_message", text }
134
- });
135
- conversation.sendUserMessage(text);
136
- },
137
- sendImage(_imageData, _mimeType) {
138
- logger.errors("elevenlabs.realtime sendImage not supported", {
139
- source: "elevenlabs.realtime"
140
- });
141
- },
142
- sendToolResult(_callId, _result) {
143
- },
144
- updateSession(_config) {
145
- logger.errors("elevenlabs.realtime updateSession not supported", {
146
- source: "elevenlabs.realtime"
147
- });
148
- },
149
- interrupt() {
150
- emit("mode_change", { mode: "listening" });
151
- emit("interrupted", {});
152
- },
153
- on(event, handler) {
154
- let handlers = eventHandlers.get(event);
155
- if (!handlers) {
156
- handlers = /* @__PURE__ */ new Set();
157
- eventHandlers.set(event, handlers);
158
- }
159
- handlers.add(handler);
160
- return () => {
161
- eventHandlers.get(event)?.delete(handler);
162
- };
163
- },
164
- getAudioVisualization() {
165
- return {
166
- get inputLevel() {
167
- if (!conversation) return 0;
168
- try {
169
- return conversation.getInputVolume();
170
- } catch {
171
- return 0;
172
- }
173
- },
174
- get outputLevel() {
175
- if (!conversation) return 0;
176
- try {
177
- return conversation.getOutputVolume();
178
- } catch {
179
- return 0;
180
- }
181
- },
182
- getInputFrequencyData() {
183
- if (!conversation) return emptyFrequencyData;
184
- try {
185
- return conversation.getInputByteFrequencyData();
186
- } catch {
187
- return emptyFrequencyData;
188
- }
189
- },
190
- getOutputFrequencyData() {
191
- if (!conversation) return emptyFrequencyData;
192
- try {
193
- return conversation.getOutputByteFrequencyData();
194
- } catch {
195
- return emptyFrequencyData;
196
- }
197
- },
198
- getInputTimeDomainData() {
199
- return emptyTimeDomainData;
200
- },
201
- getOutputTimeDomainData() {
202
- return emptyTimeDomainData;
203
- },
204
- get inputSampleRate() {
205
- return 16e3;
206
- },
207
- get outputSampleRate() {
208
- return 16e3;
209
- }
210
- };
211
- }
212
- };
213
- return connection;
37
+ const eventHandlers = /* @__PURE__ */ new Map();
38
+ let conversation = null;
39
+ let messageIdCounter = 0;
40
+ const emptyFrequencyData = /* @__PURE__ */ new Uint8Array(128);
41
+ const emptyTimeDomainData = (/* @__PURE__ */ new Uint8Array(128)).fill(128);
42
+ function emit(event, payload) {
43
+ const handlers = eventHandlers.get(event);
44
+ if (handlers) for (const handler of handlers) handler(payload);
45
+ }
46
+ function generateMessageId() {
47
+ return `el-msg-${Date.now()}-${++messageIdCounter}`;
48
+ }
49
+ const elevenLabsClientTools = {};
50
+ if (clientToolDefs) for (const tool of clientToolDefs) elevenLabsClientTools[tool.name] = async (params) => {
51
+ if (tool.execute) {
52
+ const result = await tool.execute(params);
53
+ return typeof result === "string" ? result : JSON.stringify(result);
54
+ }
55
+ return JSON.stringify({ error: `No execute function for tool ${tool.name}` });
56
+ };
57
+ const sessionOptions = {
58
+ signedUrl: token.token,
59
+ onConnect: () => {
60
+ logger.provider(`provider=elevenlabs direction=in type=connect`, { frame: { type: "connect" } });
61
+ emit("status_change", { status: "connected" });
62
+ emit("mode_change", { mode: "listening" });
63
+ },
64
+ onDisconnect: () => {
65
+ logger.provider(`provider=elevenlabs direction=in type=disconnect`, { frame: { type: "disconnect" } });
66
+ emit("status_change", { status: "idle" });
67
+ emit("mode_change", { mode: "idle" });
68
+ },
69
+ onModeChange: ({ mode }) => {
70
+ logger.provider(`provider=elevenlabs direction=in type=mode_change`, { frame: {
71
+ type: "mode_change",
72
+ mode
73
+ } });
74
+ emit("mode_change", { mode: mode === "speaking" ? "speaking" : "listening" });
75
+ },
76
+ onMessage: ({ message, source }) => {
77
+ logger.provider(`provider=elevenlabs direction=in type=message`, { frame: {
78
+ type: "message",
79
+ source,
80
+ message
81
+ } });
82
+ const role = source === "user" ? "user" : "assistant";
83
+ if (role === "user") emit("transcript", {
84
+ role,
85
+ transcript: message,
86
+ isFinal: true
87
+ });
88
+ else emit("message_complete", { message: {
89
+ id: generateMessageId(),
90
+ role,
91
+ timestamp: Date.now(),
92
+ parts: [{
93
+ type: "audio",
94
+ transcript: message
95
+ }]
96
+ } });
97
+ },
98
+ onError: (error) => {
99
+ logger.errors("elevenlabs.realtime fatal", {
100
+ error,
101
+ source: "elevenlabs.realtime"
102
+ });
103
+ emit("error", { error: new Error(typeof error === "string" ? error : error.message || "Unknown error") });
104
+ }
105
+ };
106
+ if (Object.keys(elevenLabsClientTools).length > 0) sessionOptions.clientTools = elevenLabsClientTools;
107
+ try {
108
+ conversation = await Conversation.startSession(sessionOptions);
109
+ } catch (error) {
110
+ logger.errors("elevenlabs.realtime fatal", {
111
+ error,
112
+ source: "elevenlabs.realtime"
113
+ });
114
+ throw error;
115
+ }
116
+ return {
117
+ async disconnect() {
118
+ if (conversation) {
119
+ await conversation.endSession();
120
+ conversation = null;
121
+ }
122
+ emit("status_change", { status: "idle" });
123
+ },
124
+ async startAudioCapture() {
125
+ emit("mode_change", { mode: "listening" });
126
+ },
127
+ stopAudioCapture() {
128
+ emit("mode_change", { mode: "idle" });
129
+ },
130
+ sendText(text) {
131
+ if (!conversation) return;
132
+ logger.provider(`provider=elevenlabs direction=out type=user_message`, { frame: {
133
+ type: "user_message",
134
+ text
135
+ } });
136
+ conversation.sendUserMessage(text);
137
+ },
138
+ sendImage(_imageData, _mimeType) {
139
+ logger.errors("elevenlabs.realtime sendImage not supported", { source: "elevenlabs.realtime" });
140
+ },
141
+ sendToolResult(_callId, _result) {},
142
+ updateSession(_config) {
143
+ logger.errors("elevenlabs.realtime updateSession not supported", { source: "elevenlabs.realtime" });
144
+ },
145
+ interrupt() {
146
+ emit("mode_change", { mode: "listening" });
147
+ emit("interrupted", {});
148
+ },
149
+ on(event, handler) {
150
+ let handlers = eventHandlers.get(event);
151
+ if (!handlers) {
152
+ handlers = /* @__PURE__ */ new Set();
153
+ eventHandlers.set(event, handlers);
154
+ }
155
+ handlers.add(handler);
156
+ return () => {
157
+ eventHandlers.get(event)?.delete(handler);
158
+ };
159
+ },
160
+ getAudioVisualization() {
161
+ return {
162
+ get inputLevel() {
163
+ if (!conversation) return 0;
164
+ try {
165
+ return conversation.getInputVolume();
166
+ } catch {
167
+ return 0;
168
+ }
169
+ },
170
+ get outputLevel() {
171
+ if (!conversation) return 0;
172
+ try {
173
+ return conversation.getOutputVolume();
174
+ } catch {
175
+ return 0;
176
+ }
177
+ },
178
+ getInputFrequencyData() {
179
+ if (!conversation) return emptyFrequencyData;
180
+ try {
181
+ return conversation.getInputByteFrequencyData();
182
+ } catch {
183
+ return emptyFrequencyData;
184
+ }
185
+ },
186
+ getOutputFrequencyData() {
187
+ if (!conversation) return emptyFrequencyData;
188
+ try {
189
+ return conversation.getOutputByteFrequencyData();
190
+ } catch {
191
+ return emptyFrequencyData;
192
+ }
193
+ },
194
+ getInputTimeDomainData() {
195
+ return emptyTimeDomainData;
196
+ },
197
+ getOutputTimeDomainData() {
198
+ return emptyTimeDomainData;
199
+ },
200
+ get inputSampleRate() {
201
+ return 16e3;
202
+ },
203
+ get outputSampleRate() {
204
+ return 16e3;
205
+ }
206
+ };
207
+ }
208
+ };
214
209
  }
215
- export {
216
- elevenlabsRealtime
217
- };
218
- //# sourceMappingURL=adapter.js.map
210
+ //#endregion
211
+ export { elevenlabsRealtime };
212
+
213
+ //# sourceMappingURL=adapter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","sources":["../../../src/realtime/adapter.ts"],"sourcesContent":["import { Conversation } from '@elevenlabs/client'\nimport { resolveDebugOption } from '@tanstack/ai/adapter-internals'\nimport type {\n AnyClientTool,\n AudioVisualization,\n RealtimeAdapter,\n RealtimeConnection,\n RealtimeEvent,\n RealtimeEventHandler,\n RealtimeMessage,\n RealtimeMode,\n RealtimeSessionConfig,\n RealtimeToken,\n} from '@tanstack/ai'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type { ElevenLabsRealtimeOptions } from './types'\n\n/**\n * Creates an ElevenLabs realtime adapter for client-side use.\n *\n * Wraps the @elevenlabs/client SDK for voice conversations.\n *\n * @param options - Optional configuration\n * @returns A RealtimeAdapter for use with RealtimeClient\n *\n * @example\n * ```typescript\n * import { RealtimeClient } from '@tanstack/ai-client'\n * import { elevenlabsRealtime } from '@tanstack/ai-elevenlabs'\n *\n * const client = new RealtimeClient({\n * getToken: () => fetch('/api/realtime-token').then(r => r.json()),\n * adapter: elevenlabsRealtime(),\n * })\n * ```\n */\nexport function elevenlabsRealtime(\n options: ElevenLabsRealtimeOptions = {},\n): RealtimeAdapter {\n const logger = resolveDebugOption(options.debug)\n\n return {\n provider: 'elevenlabs',\n\n async connect(\n token: RealtimeToken,\n clientToolDefs?: ReadonlyArray<AnyClientTool>,\n ): Promise<RealtimeConnection> {\n logger.request(`activity=realtime provider=elevenlabs`, {\n provider: 'elevenlabs',\n })\n return createElevenLabsConnection(token, options, logger, clientToolDefs)\n },\n }\n}\n\n/**\n * Creates a connection to ElevenLabs conversational AI\n */\nasync function createElevenLabsConnection(\n token: RealtimeToken,\n _options: ElevenLabsRealtimeOptions,\n logger: InternalLogger,\n clientToolDefs?: ReadonlyArray<AnyClientTool>,\n): Promise<RealtimeConnection> {\n const eventHandlers = new Map<RealtimeEvent, Set<RealtimeEventHandler<any>>>()\n let conversation: Awaited<\n ReturnType<typeof Conversation.startSession>\n > | null = null\n let messageIdCounter = 0\n\n // Empty arrays for when visualization isn't available\n const emptyFrequencyData = new Uint8Array(128)\n const emptyTimeDomainData = new Uint8Array(128).fill(128)\n\n // Helper to emit events\n function emit<TEvent extends RealtimeEvent>(\n event: TEvent,\n payload: Parameters<RealtimeEventHandler<TEvent>>[0],\n ) {\n const handlers = eventHandlers.get(event)\n if (handlers) {\n for (const handler of handlers) {\n handler(payload)\n }\n }\n }\n\n function generateMessageId(): string {\n return `el-msg-${Date.now()}-${++messageIdCounter}`\n }\n\n // Convert TanStack tool definitions to ElevenLabs clientTools format.\n // @elevenlabs/client expects plain async functions, not objects.\n const elevenLabsClientTools: Record<\n string,\n (params: unknown) => Promise<string>\n > = {}\n\n if (clientToolDefs) {\n for (const tool of clientToolDefs) {\n elevenLabsClientTools[tool.name] = async (params: unknown) => {\n if (tool.execute) {\n const result = await tool.execute(params)\n return typeof result === 'string' ? result : JSON.stringify(result)\n }\n return JSON.stringify({\n error: `No execute function for tool ${tool.name}`,\n })\n }\n }\n }\n\n // Build session options\n const sessionOptions: Record<string, unknown> = {\n signedUrl: token.token,\n\n onConnect: () => {\n logger.provider(`provider=elevenlabs direction=in type=connect`, {\n frame: { type: 'connect' },\n })\n emit('status_change', { status: 'connected' })\n emit('mode_change', { mode: 'listening' })\n },\n\n onDisconnect: () => {\n logger.provider(`provider=elevenlabs direction=in type=disconnect`, {\n frame: { type: 'disconnect' },\n })\n emit('status_change', { status: 'idle' })\n emit('mode_change', { mode: 'idle' })\n },\n\n onModeChange: ({ mode }: { mode: string }) => {\n logger.provider(`provider=elevenlabs direction=in type=mode_change`, {\n frame: { type: 'mode_change', mode },\n })\n const mappedMode: RealtimeMode =\n mode === 'speaking' ? 'speaking' : 'listening'\n emit('mode_change', { mode: mappedMode })\n },\n\n onMessage: ({ message, source }: { message: string; source: string }) => {\n logger.provider(`provider=elevenlabs direction=in type=message`, {\n frame: { type: 'message', source, message },\n })\n const role = source === 'user' ? 'user' : 'assistant'\n\n if (role === 'user') {\n // User transcripts: only emit transcript — the RealtimeClient\n // creates the user message when it receives a final transcript\n emit('transcript', {\n role,\n transcript: message,\n isFinal: true,\n })\n } else {\n // Assistant messages: only emit message_complete — the canonical\n // event for finalized assistant messages\n const realtimeMessage: RealtimeMessage = {\n id: generateMessageId(),\n role,\n timestamp: Date.now(),\n parts: [{ type: 'audio', transcript: message }],\n }\n emit('message_complete', { message: realtimeMessage })\n }\n },\n\n onError: (error: string | Error) => {\n logger.errors('elevenlabs.realtime fatal', {\n error,\n source: 'elevenlabs.realtime',\n })\n emit('error', {\n error: new Error(\n typeof error === 'string' ? error : error.message || 'Unknown error',\n ),\n })\n },\n }\n\n // Only add clientTools if we have any\n if (Object.keys(elevenLabsClientTools).length > 0) {\n sessionOptions.clientTools = elevenLabsClientTools\n }\n\n // Start the conversation session\n try {\n conversation = await Conversation.startSession(\n sessionOptions as Parameters<typeof Conversation.startSession>[0],\n )\n } catch (error) {\n logger.errors('elevenlabs.realtime fatal', {\n error,\n source: 'elevenlabs.realtime',\n })\n throw error\n }\n\n // Connection implementation\n const connection: RealtimeConnection = {\n async disconnect() {\n if (conversation) {\n await conversation.endSession()\n conversation = null\n }\n emit('status_change', { status: 'idle' })\n },\n\n async startAudioCapture() {\n // ElevenLabs SDK handles audio capture automatically\n // This is called when the session starts\n emit('mode_change', { mode: 'listening' })\n },\n\n stopAudioCapture() {\n // ElevenLabs SDK handles this\n emit('mode_change', { mode: 'idle' })\n },\n\n sendText(text: string) {\n if (!conversation) return\n logger.provider(`provider=elevenlabs direction=out type=user_message`, {\n frame: { type: 'user_message', text },\n })\n conversation.sendUserMessage(text)\n },\n\n sendImage(_imageData: string, _mimeType: string) {\n // ElevenLabs does not support direct image input in the conversation API\n logger.errors('elevenlabs.realtime sendImage not supported', {\n source: 'elevenlabs.realtime',\n })\n },\n\n sendToolResult(_callId: string, _result: string) {\n // ElevenLabs client tools are handled via the clientTools handlers\n // registered at session start — results are returned automatically\n },\n\n updateSession(_config: Partial<RealtimeSessionConfig>) {\n // ElevenLabs session config is set at creation time\n logger.errors('elevenlabs.realtime updateSession not supported', {\n source: 'elevenlabs.realtime',\n })\n },\n\n interrupt() {\n // ElevenLabs handles interruption automatically via barge-in\n // No explicit API to call\n emit('mode_change', { mode: 'listening' })\n emit('interrupted', {})\n },\n\n on<TEvent extends RealtimeEvent>(\n event: TEvent,\n handler: RealtimeEventHandler<TEvent>,\n ): () => void {\n let handlers = eventHandlers.get(event)\n if (!handlers) {\n handlers = new Set()\n eventHandlers.set(event, handlers)\n }\n handlers.add(handler)\n\n return () => {\n eventHandlers.get(event)?.delete(handler)\n }\n },\n\n getAudioVisualization(): AudioVisualization {\n return {\n get inputLevel() {\n if (!conversation) return 0\n try {\n return conversation.getInputVolume()\n } catch {\n return 0\n }\n },\n\n get outputLevel() {\n if (!conversation) return 0\n try {\n return conversation.getOutputVolume()\n } catch {\n return 0\n }\n },\n\n getInputFrequencyData() {\n if (!conversation) return emptyFrequencyData\n try {\n return conversation.getInputByteFrequencyData()\n } catch {\n return emptyFrequencyData\n }\n },\n\n getOutputFrequencyData() {\n if (!conversation) return emptyFrequencyData\n try {\n return conversation.getOutputByteFrequencyData()\n } catch {\n return emptyFrequencyData\n }\n },\n\n getInputTimeDomainData() {\n // ElevenLabs SDK doesn't expose time domain data\n return emptyTimeDomainData\n },\n\n getOutputTimeDomainData() {\n // ElevenLabs SDK doesn't expose time domain data\n return emptyTimeDomainData\n },\n\n get inputSampleRate() {\n return 16000\n },\n\n get outputSampleRate() {\n return 16000\n },\n }\n },\n }\n\n return connection\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.js","names":[],"sources":["../../../src/realtime/adapter.ts"],"sourcesContent":["import { Conversation } from '@elevenlabs/client'\nimport { resolveDebugOption } from '@tanstack/ai/adapter-internals'\nimport type {\n AnyClientTool,\n AudioVisualization,\n RealtimeAdapter,\n RealtimeConnection,\n RealtimeEvent,\n RealtimeEventHandler,\n RealtimeMessage,\n RealtimeMode,\n RealtimeSessionConfig,\n RealtimeToken,\n} from '@tanstack/ai'\nimport type { InternalLogger } from '@tanstack/ai/adapter-internals'\nimport type { ElevenLabsRealtimeOptions } from './types'\n\n/**\n * Creates an ElevenLabs realtime adapter for client-side use.\n *\n * Wraps the @elevenlabs/client SDK for voice conversations.\n *\n * @param options - Optional configuration\n * @returns A RealtimeAdapter for use with RealtimeClient\n *\n * @example\n * ```typescript\n * import { RealtimeClient } from '@tanstack/ai-client'\n * import { elevenlabsRealtime } from '@tanstack/ai-elevenlabs'\n *\n * const client = new RealtimeClient({\n * getToken: () => fetch('/api/realtime-token').then(r => r.json()),\n * adapter: elevenlabsRealtime(),\n * })\n * ```\n */\nexport function elevenlabsRealtime(\n options: ElevenLabsRealtimeOptions = {},\n): RealtimeAdapter {\n const logger = resolveDebugOption(options.debug)\n\n return {\n provider: 'elevenlabs',\n\n async connect(\n token: RealtimeToken,\n clientToolDefs?: ReadonlyArray<AnyClientTool>,\n ): Promise<RealtimeConnection> {\n logger.request(`activity=realtime provider=elevenlabs`, {\n provider: 'elevenlabs',\n })\n return createElevenLabsConnection(token, options, logger, clientToolDefs)\n },\n }\n}\n\n/**\n * Creates a connection to ElevenLabs conversational AI\n */\nasync function createElevenLabsConnection(\n token: RealtimeToken,\n _options: ElevenLabsRealtimeOptions,\n logger: InternalLogger,\n clientToolDefs?: ReadonlyArray<AnyClientTool>,\n): Promise<RealtimeConnection> {\n const eventHandlers = new Map<RealtimeEvent, Set<RealtimeEventHandler<any>>>()\n let conversation: Awaited<\n ReturnType<typeof Conversation.startSession>\n > | null = null\n let messageIdCounter = 0\n\n // Empty arrays for when visualization isn't available\n const emptyFrequencyData = new Uint8Array(128)\n const emptyTimeDomainData = new Uint8Array(128).fill(128)\n\n // Helper to emit events\n function emit<TEvent extends RealtimeEvent>(\n event: TEvent,\n payload: Parameters<RealtimeEventHandler<TEvent>>[0],\n ) {\n const handlers = eventHandlers.get(event)\n if (handlers) {\n for (const handler of handlers) {\n handler(payload)\n }\n }\n }\n\n function generateMessageId(): string {\n return `el-msg-${Date.now()}-${++messageIdCounter}`\n }\n\n // Convert TanStack tool definitions to ElevenLabs clientTools format.\n // @elevenlabs/client expects plain async functions, not objects.\n const elevenLabsClientTools: Record<\n string,\n (params: unknown) => Promise<string>\n > = {}\n\n if (clientToolDefs) {\n for (const tool of clientToolDefs) {\n elevenLabsClientTools[tool.name] = async (params: unknown) => {\n if (tool.execute) {\n const result = await tool.execute(params)\n return typeof result === 'string' ? result : JSON.stringify(result)\n }\n return JSON.stringify({\n error: `No execute function for tool ${tool.name}`,\n })\n }\n }\n }\n\n // Build session options\n const sessionOptions: Record<string, unknown> = {\n signedUrl: token.token,\n\n onConnect: () => {\n logger.provider(`provider=elevenlabs direction=in type=connect`, {\n frame: { type: 'connect' },\n })\n emit('status_change', { status: 'connected' })\n emit('mode_change', { mode: 'listening' })\n },\n\n onDisconnect: () => {\n logger.provider(`provider=elevenlabs direction=in type=disconnect`, {\n frame: { type: 'disconnect' },\n })\n emit('status_change', { status: 'idle' })\n emit('mode_change', { mode: 'idle' })\n },\n\n onModeChange: ({ mode }: { mode: string }) => {\n logger.provider(`provider=elevenlabs direction=in type=mode_change`, {\n frame: { type: 'mode_change', mode },\n })\n const mappedMode: RealtimeMode =\n mode === 'speaking' ? 'speaking' : 'listening'\n emit('mode_change', { mode: mappedMode })\n },\n\n onMessage: ({ message, source }: { message: string; source: string }) => {\n logger.provider(`provider=elevenlabs direction=in type=message`, {\n frame: { type: 'message', source, message },\n })\n const role = source === 'user' ? 'user' : 'assistant'\n\n if (role === 'user') {\n // User transcripts: only emit transcript — the RealtimeClient\n // creates the user message when it receives a final transcript\n emit('transcript', {\n role,\n transcript: message,\n isFinal: true,\n })\n } else {\n // Assistant messages: only emit message_complete — the canonical\n // event for finalized assistant messages\n const realtimeMessage: RealtimeMessage = {\n id: generateMessageId(),\n role,\n timestamp: Date.now(),\n parts: [{ type: 'audio', transcript: message }],\n }\n emit('message_complete', { message: realtimeMessage })\n }\n },\n\n onError: (error: string | Error) => {\n logger.errors('elevenlabs.realtime fatal', {\n error,\n source: 'elevenlabs.realtime',\n })\n emit('error', {\n error: new Error(\n typeof error === 'string' ? error : error.message || 'Unknown error',\n ),\n })\n },\n }\n\n // Only add clientTools if we have any\n if (Object.keys(elevenLabsClientTools).length > 0) {\n sessionOptions.clientTools = elevenLabsClientTools\n }\n\n // Start the conversation session\n try {\n conversation = await Conversation.startSession(\n sessionOptions as Parameters<typeof Conversation.startSession>[0],\n )\n } catch (error) {\n logger.errors('elevenlabs.realtime fatal', {\n error,\n source: 'elevenlabs.realtime',\n })\n throw error\n }\n\n // Connection implementation\n const connection: RealtimeConnection = {\n async disconnect() {\n if (conversation) {\n await conversation.endSession()\n conversation = null\n }\n emit('status_change', { status: 'idle' })\n },\n\n async startAudioCapture() {\n // ElevenLabs SDK handles audio capture automatically\n // This is called when the session starts\n emit('mode_change', { mode: 'listening' })\n },\n\n stopAudioCapture() {\n // ElevenLabs SDK handles this\n emit('mode_change', { mode: 'idle' })\n },\n\n sendText(text: string) {\n if (!conversation) return\n logger.provider(`provider=elevenlabs direction=out type=user_message`, {\n frame: { type: 'user_message', text },\n })\n conversation.sendUserMessage(text)\n },\n\n sendImage(_imageData: string, _mimeType: string) {\n // ElevenLabs does not support direct image input in the conversation API\n logger.errors('elevenlabs.realtime sendImage not supported', {\n source: 'elevenlabs.realtime',\n })\n },\n\n sendToolResult(_callId: string, _result: string) {\n // ElevenLabs client tools are handled via the clientTools handlers\n // registered at session start — results are returned automatically\n },\n\n updateSession(_config: Partial<RealtimeSessionConfig>) {\n // ElevenLabs session config is set at creation time\n logger.errors('elevenlabs.realtime updateSession not supported', {\n source: 'elevenlabs.realtime',\n })\n },\n\n interrupt() {\n // ElevenLabs handles interruption automatically via barge-in\n // No explicit API to call\n emit('mode_change', { mode: 'listening' })\n emit('interrupted', {})\n },\n\n on<TEvent extends RealtimeEvent>(\n event: TEvent,\n handler: RealtimeEventHandler<TEvent>,\n ): () => void {\n let handlers = eventHandlers.get(event)\n if (!handlers) {\n handlers = new Set()\n eventHandlers.set(event, handlers)\n }\n handlers.add(handler)\n\n return () => {\n eventHandlers.get(event)?.delete(handler)\n }\n },\n\n getAudioVisualization(): AudioVisualization {\n return {\n get inputLevel() {\n if (!conversation) return 0\n try {\n return conversation.getInputVolume()\n } catch {\n return 0\n }\n },\n\n get outputLevel() {\n if (!conversation) return 0\n try {\n return conversation.getOutputVolume()\n } catch {\n return 0\n }\n },\n\n getInputFrequencyData() {\n if (!conversation) return emptyFrequencyData\n try {\n return conversation.getInputByteFrequencyData()\n } catch {\n return emptyFrequencyData\n }\n },\n\n getOutputFrequencyData() {\n if (!conversation) return emptyFrequencyData\n try {\n return conversation.getOutputByteFrequencyData()\n } catch {\n return emptyFrequencyData\n }\n },\n\n getInputTimeDomainData() {\n // ElevenLabs SDK doesn't expose time domain data\n return emptyTimeDomainData\n },\n\n getOutputTimeDomainData() {\n // ElevenLabs SDK doesn't expose time domain data\n return emptyTimeDomainData\n },\n\n get inputSampleRate() {\n return 16000\n },\n\n get outputSampleRate() {\n return 16000\n },\n }\n },\n }\n\n return connection\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { elevenlabsRealtimeToken } from "./token.js";
2
+ import { elevenlabsRealtime } from "./adapter.js";
3
+ export { elevenlabsRealtime, elevenlabsRealtimeToken };
@@ -1,39 +1,57 @@
1
1
  import { createElevenLabsClient, getElevenLabsAgentIdFromEnv } from "../utils/client.js";
2
+ //#region src/realtime/token.ts
3
+ /**
4
+ * Creates an ElevenLabs realtime token adapter.
5
+ *
6
+ * Uses the official `@elevenlabs/elevenlabs-js` SDK to request a signed URL
7
+ * for client-side conversation connections. The signed URL is valid for
8
+ * 30 minutes.
9
+ *
10
+ * @param options - Configuration. `agentId` falls back to
11
+ * `ELEVENLABS_AGENT_ID` in the environment when omitted.
12
+ * @returns A RealtimeTokenAdapter for use with realtimeToken()
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { realtimeToken } from '@tanstack/ai'
17
+ * import { elevenlabsRealtimeToken } from '@tanstack/ai-elevenlabs'
18
+ *
19
+ * // Reads ELEVENLABS_AGENT_ID from env:
20
+ * const token = await realtimeToken({ adapter: elevenlabsRealtimeToken() })
21
+ *
22
+ * // Or pass explicitly:
23
+ * const token = await realtimeToken({
24
+ * adapter: elevenlabsRealtimeToken({ agentId: 'your-agent-id' }),
25
+ * })
26
+ * ```
27
+ */
2
28
  function elevenlabsRealtimeToken(options = {}) {
3
- const client = createElevenLabsClient();
4
- return {
5
- provider: "elevenlabs",
6
- async generateToken() {
7
- const { overrides } = options;
8
- const agentId = options.agentId ?? getElevenLabsAgentIdFromEnv();
9
- const response = await client.conversationalAi.conversations.getSignedUrl(
10
- { agentId }
11
- );
12
- const expiresAt = Date.now() + 30 * 60 * 1e3;
13
- return {
14
- provider: "elevenlabs",
15
- token: response.signedUrl,
16
- expiresAt,
17
- config: {
18
- ...overrides?.voiceId !== void 0 && { voice: overrides.voiceId },
19
- ...overrides?.systemPrompt !== void 0 && {
20
- instructions: overrides.systemPrompt
21
- },
22
- providerOptions: {
23
- agentId,
24
- ...overrides?.firstMessage !== void 0 && {
25
- firstMessage: overrides.firstMessage
26
- },
27
- ...overrides?.language !== void 0 && {
28
- language: overrides.language
29
- }
30
- }
31
- }
32
- };
33
- }
34
- };
29
+ const client = createElevenLabsClient();
30
+ return {
31
+ provider: "elevenlabs",
32
+ async generateToken() {
33
+ const { overrides } = options;
34
+ const agentId = options.agentId ?? getElevenLabsAgentIdFromEnv();
35
+ const response = await client.conversationalAi.conversations.getSignedUrl({ agentId });
36
+ const expiresAt = Date.now() + 1800 * 1e3;
37
+ return {
38
+ provider: "elevenlabs",
39
+ token: response.signedUrl,
40
+ expiresAt,
41
+ config: {
42
+ ...overrides?.voiceId !== void 0 && { voice: overrides.voiceId },
43
+ ...overrides?.systemPrompt !== void 0 && { instructions: overrides.systemPrompt },
44
+ providerOptions: {
45
+ agentId,
46
+ ...overrides?.firstMessage !== void 0 && { firstMessage: overrides.firstMessage },
47
+ ...overrides?.language !== void 0 && { language: overrides.language }
48
+ }
49
+ }
50
+ };
51
+ }
52
+ };
35
53
  }
36
- export {
37
- elevenlabsRealtimeToken
38
- };
39
- //# sourceMappingURL=token.js.map
54
+ //#endregion
55
+ export { elevenlabsRealtimeToken };
56
+
57
+ //# sourceMappingURL=token.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"token.js","sources":["../../../src/realtime/token.ts"],"sourcesContent":["import {\n createElevenLabsClient,\n getElevenLabsAgentIdFromEnv,\n} from '../utils/client'\nimport type { RealtimeToken, RealtimeTokenAdapter } from '@tanstack/ai'\nimport type { ElevenLabsRealtimeTokenOptions } from './types'\n\n/**\n * Creates an ElevenLabs realtime token adapter.\n *\n * Uses the official `@elevenlabs/elevenlabs-js` SDK to request a signed URL\n * for client-side conversation connections. The signed URL is valid for\n * 30 minutes.\n *\n * @param options - Configuration. `agentId` falls back to\n * `ELEVENLABS_AGENT_ID` in the environment when omitted.\n * @returns A RealtimeTokenAdapter for use with realtimeToken()\n *\n * @example\n * ```typescript\n * import { realtimeToken } from '@tanstack/ai'\n * import { elevenlabsRealtimeToken } from '@tanstack/ai-elevenlabs'\n *\n * // Reads ELEVENLABS_AGENT_ID from env:\n * const token = await realtimeToken({ adapter: elevenlabsRealtimeToken() })\n *\n * // Or pass explicitly:\n * const token = await realtimeToken({\n * adapter: elevenlabsRealtimeToken({ agentId: 'your-agent-id' }),\n * })\n * ```\n */\nexport function elevenlabsRealtimeToken(\n options: ElevenLabsRealtimeTokenOptions = {},\n): RealtimeTokenAdapter {\n const client = createElevenLabsClient()\n\n return {\n provider: 'elevenlabs',\n\n async generateToken(): Promise<RealtimeToken> {\n const { overrides } = options\n const agentId = options.agentId ?? getElevenLabsAgentIdFromEnv()\n\n const response = await client.conversationalAi.conversations.getSignedUrl(\n { agentId },\n )\n\n // Signed URLs are valid for 30 minutes\n const expiresAt = Date.now() + 30 * 60 * 1000\n\n // Conditionally spread optional fields — the vendor target type\n // (`RealtimeSessionConfig` in `@tanstack/ai`) uses strict\n // `field?: T` semantics under `exactOptionalPropertyTypes`, so we\n // omit keys entirely when the override is absent rather than\n // setting them to `undefined`.\n return {\n provider: 'elevenlabs',\n token: response.signedUrl,\n expiresAt,\n config: {\n ...(overrides?.voiceId !== undefined && { voice: overrides.voiceId }),\n ...(overrides?.systemPrompt !== undefined && {\n instructions: overrides.systemPrompt,\n }),\n providerOptions: {\n agentId,\n ...(overrides?.firstMessage !== undefined && {\n firstMessage: overrides.firstMessage,\n }),\n ...(overrides?.language !== undefined && {\n language: overrides.language,\n }),\n },\n },\n }\n },\n }\n}\n"],"names":[],"mappings":";AAgCO,SAAS,wBACd,UAA0C,IACpB;AACtB,QAAM,SAAS,uBAAA;AAEf,SAAO;AAAA,IACL,UAAU;AAAA,IAEV,MAAM,gBAAwC;AAC5C,YAAM,EAAE,cAAc;AACtB,YAAM,UAAU,QAAQ,WAAW,4BAAA;AAEnC,YAAM,WAAW,MAAM,OAAO,iBAAiB,cAAc;AAAA,QAC3D,EAAE,QAAA;AAAA,MAAQ;AAIZ,YAAM,YAAY,KAAK,IAAA,IAAQ,KAAK,KAAK;AAOzC,aAAO;AAAA,QACL,UAAU;AAAA,QACV,OAAO,SAAS;AAAA,QAChB;AAAA,QACA,QAAQ;AAAA,UACN,GAAI,WAAW,YAAY,UAAa,EAAE,OAAO,UAAU,QAAA;AAAA,UAC3D,GAAI,WAAW,iBAAiB,UAAa;AAAA,YAC3C,cAAc,UAAU;AAAA,UAAA;AAAA,UAE1B,iBAAiB;AAAA,YACf;AAAA,YACA,GAAI,WAAW,iBAAiB,UAAa;AAAA,cAC3C,cAAc,UAAU;AAAA,YAAA;AAAA,YAE1B,GAAI,WAAW,aAAa,UAAa;AAAA,cACvC,UAAU,UAAU;AAAA,YAAA;AAAA,UACtB;AAAA,QACF;AAAA,MACF;AAAA,IAEJ;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"token.js","names":[],"sources":["../../../src/realtime/token.ts"],"sourcesContent":["import {\n createElevenLabsClient,\n getElevenLabsAgentIdFromEnv,\n} from '../utils/client'\nimport type { RealtimeToken, RealtimeTokenAdapter } from '@tanstack/ai'\nimport type { ElevenLabsRealtimeTokenOptions } from './types'\n\n/**\n * Creates an ElevenLabs realtime token adapter.\n *\n * Uses the official `@elevenlabs/elevenlabs-js` SDK to request a signed URL\n * for client-side conversation connections. The signed URL is valid for\n * 30 minutes.\n *\n * @param options - Configuration. `agentId` falls back to\n * `ELEVENLABS_AGENT_ID` in the environment when omitted.\n * @returns A RealtimeTokenAdapter for use with realtimeToken()\n *\n * @example\n * ```typescript\n * import { realtimeToken } from '@tanstack/ai'\n * import { elevenlabsRealtimeToken } from '@tanstack/ai-elevenlabs'\n *\n * // Reads ELEVENLABS_AGENT_ID from env:\n * const token = await realtimeToken({ adapter: elevenlabsRealtimeToken() })\n *\n * // Or pass explicitly:\n * const token = await realtimeToken({\n * adapter: elevenlabsRealtimeToken({ agentId: 'your-agent-id' }),\n * })\n * ```\n */\nexport function elevenlabsRealtimeToken(\n options: ElevenLabsRealtimeTokenOptions = {},\n): RealtimeTokenAdapter {\n const client = createElevenLabsClient()\n\n return {\n provider: 'elevenlabs',\n\n async generateToken(): Promise<RealtimeToken> {\n const { overrides } = options\n const agentId = options.agentId ?? getElevenLabsAgentIdFromEnv()\n\n const response = await client.conversationalAi.conversations.getSignedUrl(\n { agentId },\n )\n\n // Signed URLs are valid for 30 minutes\n const expiresAt = Date.now() + 30 * 60 * 1000\n\n // Conditionally spread optional fields — the vendor target type\n // (`RealtimeSessionConfig` in `@tanstack/ai`) uses strict\n // `field?: T` semantics under `exactOptionalPropertyTypes`, so we\n // omit keys entirely when the override is absent rather than\n // setting them to `undefined`.\n return {\n provider: 'elevenlabs',\n token: response.signedUrl,\n expiresAt,\n config: {\n ...(overrides?.voiceId !== undefined && { voice: overrides.voiceId }),\n ...(overrides?.systemPrompt !== undefined && {\n instructions: overrides.systemPrompt,\n }),\n providerOptions: {\n agentId,\n ...(overrides?.firstMessage !== undefined && {\n firstMessage: overrides.firstMessage,\n }),\n ...(overrides?.language !== undefined && {\n language: overrides.language,\n }),\n },\n },\n }\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,wBACd,UAA0C,CAAC,GACrB;CACtB,MAAM,SAAS,uBAAuB;CAEtC,OAAO;EACL,UAAU;EAEV,MAAM,gBAAwC;GAC5C,MAAM,EAAE,cAAc;GACtB,MAAM,UAAU,QAAQ,WAAW,4BAA4B;GAE/D,MAAM,WAAW,MAAM,OAAO,iBAAiB,cAAc,aAC3D,EAAE,QAAQ,CACZ;GAGA,MAAM,YAAY,KAAK,IAAI,IAAI,OAAU;GAOzC,OAAO;IACL,UAAU;IACV,OAAO,SAAS;IAChB;IACA,QAAQ;KACN,GAAI,WAAW,YAAY,KAAA,KAAa,EAAE,OAAO,UAAU,QAAQ;KACnE,GAAI,WAAW,iBAAiB,KAAA,KAAa,EAC3C,cAAc,UAAU,aAC1B;KACA,iBAAiB;MACf;MACA,GAAI,WAAW,iBAAiB,KAAA,KAAa,EAC3C,cAAc,UAAU,aAC1B;MACA,GAAI,WAAW,aAAa,KAAA,KAAa,EACvC,UAAU,UAAU,SACtB;KACF;IACF;GACF;EACF;CACF;AACF"}