@tanstack/ai-gemini 0.20.1 → 0.22.0

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 (70) hide show
  1. package/README.md +15 -1
  2. package/dist/esm/adapters/audio.js +113 -61
  3. package/dist/esm/adapters/audio.js.map +1 -1
  4. package/dist/esm/adapters/embedding.d.ts +77 -0
  5. package/dist/esm/adapters/embedding.js +119 -0
  6. package/dist/esm/adapters/embedding.js.map +1 -0
  7. package/dist/esm/adapters/image.js +224 -225
  8. package/dist/esm/adapters/image.js.map +1 -1
  9. package/dist/esm/adapters/summarize.js +33 -12
  10. package/dist/esm/adapters/summarize.js.map +1 -1
  11. package/dist/esm/adapters/text.js +574 -650
  12. package/dist/esm/adapters/text.js.map +1 -1
  13. package/dist/esm/adapters/tts.js +197 -178
  14. package/dist/esm/adapters/tts.js.map +1 -1
  15. package/dist/esm/adapters/video.js +456 -409
  16. package/dist/esm/adapters/video.js.map +1 -1
  17. package/dist/esm/embedding/embedding-provider-options.d.ts +20 -0
  18. package/dist/esm/experimental/index.js +1 -6
  19. package/dist/esm/experimental/text-interactions/adapter.js +877 -1014
  20. package/dist/esm/experimental/text-interactions/adapter.js.map +1 -1
  21. package/dist/esm/image/image-provider-options.js +74 -54
  22. package/dist/esm/image/image-provider-options.js.map +1 -1
  23. package/dist/esm/index.d.ts +4 -1
  24. package/dist/esm/index.js +5 -36
  25. package/dist/esm/model-meta.d.ts +72 -5
  26. package/dist/esm/model-meta.js +737 -129
  27. package/dist/esm/model-meta.js.map +1 -1
  28. package/dist/esm/realtime/adapter.js +242 -228
  29. package/dist/esm/realtime/adapter.js.map +1 -1
  30. package/dist/esm/realtime/client.js +314 -381
  31. package/dist/esm/realtime/client.js.map +1 -1
  32. package/dist/esm/realtime/index.js +3 -0
  33. package/dist/esm/realtime/token.js +50 -36
  34. package/dist/esm/realtime/token.js.map +1 -1
  35. package/dist/esm/realtime/utils.js +202 -245
  36. package/dist/esm/realtime/utils.js.map +1 -1
  37. package/dist/esm/tools/code-execution-tool.js +11 -13
  38. package/dist/esm/tools/code-execution-tool.js.map +1 -1
  39. package/dist/esm/tools/computer-use-tool.js +18 -28
  40. package/dist/esm/tools/computer-use-tool.js.map +1 -1
  41. package/dist/esm/tools/file-search-tool.js +11 -14
  42. package/dist/esm/tools/file-search-tool.js.map +1 -1
  43. package/dist/esm/tools/function-declaration-tool.js +11 -25
  44. package/dist/esm/tools/function-declaration-tool.js.map +1 -1
  45. package/dist/esm/tools/google-maps-tool.js +11 -14
  46. package/dist/esm/tools/google-maps-tool.js.map +1 -1
  47. package/dist/esm/tools/google-search-retriveal-tool.js +11 -14
  48. package/dist/esm/tools/google-search-retriveal-tool.js.map +1 -1
  49. package/dist/esm/tools/google-search-tool.js +11 -14
  50. package/dist/esm/tools/google-search-tool.js.map +1 -1
  51. package/dist/esm/tools/index.js +2 -13
  52. package/dist/esm/tools/tool-converter.js +64 -57
  53. package/dist/esm/tools/tool-converter.js.map +1 -1
  54. package/dist/esm/tools/url-context-tool.js +11 -13
  55. package/dist/esm/tools/url-context-tool.js.map +1 -1
  56. package/dist/esm/usage.js +81 -87
  57. package/dist/esm/usage.js.map +1 -1
  58. package/dist/esm/utils/client.js +31 -24
  59. package/dist/esm/utils/client.js.map +1 -1
  60. package/dist/esm/utils/index.js +2 -0
  61. package/dist/esm/video/video-provider-options.js +73 -20
  62. package/dist/esm/video/video-provider-options.js.map +1 -1
  63. package/package.json +7 -7
  64. package/src/adapters/embedding.ts +173 -0
  65. package/src/embedding/embedding-provider-options.ts +29 -0
  66. package/src/index.ts +13 -0
  67. package/src/model-meta.ts +143 -10
  68. package/dist/esm/experimental/index.js.map +0 -1
  69. package/dist/esm/index.js.map +0 -1
  70. package/dist/esm/tools/index.js.map +0 -1
@@ -1,233 +1,247 @@
1
- import { createRealtimeEventEmitter } from "@tanstack/ai";
2
- import { AudioPlayer, base64ToArrayBuffer, AudioStreamer } from "./utils.js";
1
+ import { AudioPlayer, AudioStreamer, base64ToArrayBuffer } from "./utils.js";
3
2
  import { GeminiLiveClient } from "./client.js";
3
+ import { createRealtimeEventEmitter } from "@tanstack/ai";
4
+ //#region src/realtime/adapter.ts
5
+ /**
6
+ * Creates a Gemini realtime adapter for client-side use.
7
+ *
8
+ * @param options - Optional configuration
9
+ * @returns A RealtimeAdapter for use with RealtimeClient
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { RealtimeClient } from '@tanstack/ai-client'
14
+ * import { geminiRealtime } from '@tanstack/ai-gemini'
15
+ *
16
+ * const client = new RealtimeClient({
17
+ * getToken: () => fetch('/api/realtime-token').then(r => r.json()),
18
+ * adapter: geminiRealtime(),
19
+ * onGoAway: () => client.updateSession({ ... }) // Resume session with new config (available only for Gemini Live adapter)
20
+ * })
21
+ *
22
+ * ```
23
+ */
4
24
  function geminiRealtime(options = {}) {
5
- return {
6
- provider: "gemini",
7
- connect(token, clientTools) {
8
- return createWebSocketConnection(token, options.model, clientTools);
9
- }
10
- };
25
+ return {
26
+ provider: "gemini",
27
+ connect(token, clientTools) {
28
+ return createWebSocketConnection(token, options.model, clientTools);
29
+ }
30
+ };
11
31
  }
32
+ /**
33
+ * Creates a WebSocket connection to Gemini's realtime API
34
+ */
12
35
  async function createWebSocketConnection(token, model = "gemini-3.1-flash-live-preview", tools) {
13
- const { emit, on: realtimeEventEmitterOn } = createRealtimeEventEmitter();
14
- let currentMode = "idle";
15
- let currentMessageId = null;
16
- let messageIdCounter = 0;
17
- function generateMessageId() {
18
- return `gemini-msg-${Date.now()}-${++messageIdCounter}`;
19
- }
20
- const client = new GeminiLiveClient(token.token, model, tools);
21
- let message = {
22
- id: "",
23
- role: "assistant",
24
- timestamp: 0,
25
- parts: []
26
- };
27
- let pendingAssistantResponse = "";
28
- client.onClose = () => {
29
- emit("status_change", { status: "idle" });
30
- emit("mode_change", { mode: "idle" });
31
- };
32
- client.onError = (error) => {
33
- emit("error", { error });
34
- emit("status_change", { status: "error" });
35
- emit("mode_change", { mode: "idle" });
36
- };
37
- client.onReceiveResponse = (response) => {
38
- switch (response.type) {
39
- case "text":
40
- message.parts.push({
41
- type: "text",
42
- content: response.data
43
- });
44
- break;
45
- case "audio": {
46
- const pcm = base64ToArrayBuffer(response.data.audioData);
47
- message.parts.push({
48
- type: "audio",
49
- transcript: response.data.transcript,
50
- audioData: pcm
51
- });
52
- if (currentMode !== "speaking") {
53
- currentMode = "speaking";
54
- emit("mode_change", { mode: "speaking" });
55
- }
56
- audioPlayer.play(pcm).catch((error) => emit("error", { error }));
57
- break;
58
- }
59
- case "go_away":
60
- emit("go_away", { timeLeft: response.data.timeLeft });
61
- break;
62
- case "usage_metadata":
63
- emit("usage", {
64
- completionTokens: response.data.responseTokenCount ?? 0,
65
- promptTokens: response.data.promptTokenCount ?? 0,
66
- totalTokens: response.data.totalTokenCount ?? 0
67
- });
68
- break;
69
- case "input_transcription":
70
- if (response.data.finished && currentMode !== "thinking") {
71
- currentMode = "thinking";
72
- emit("mode_change", { mode: "thinking" });
73
- }
74
- emit("transcript", {
75
- isFinal: response.data.finished,
76
- transcript: response.data.text,
77
- role: "user"
78
- });
79
- break;
80
- case "output_transcription":
81
- pendingAssistantResponse += response.data.text;
82
- emit("transcript", {
83
- isFinal: response.data.finished,
84
- transcript: pendingAssistantResponse,
85
- role: "assistant"
86
- });
87
- break;
88
- case "interrupted":
89
- audioPlayer.interrupt();
90
- currentMode = "listening";
91
- emit("mode_change", { mode: "listening" });
92
- emit("interrupted", { messageId: currentMessageId ?? void 0 });
93
- break;
94
- case "tool_call":
95
- for (const tool of response.data.functionCalls || []) {
96
- if (tool.id && tool.name) {
97
- emit("tool_call", {
98
- toolCallId: tool.id,
99
- input: tool.args,
100
- toolName: tool.name
101
- });
102
- }
103
- }
104
- break;
105
- case "turn_complete":
106
- currentMessageId = generateMessageId();
107
- message.id = currentMessageId;
108
- message.timestamp = Date.now();
109
- message.parts.push({
110
- type: "text",
111
- content: pendingAssistantResponse
112
- });
113
- emit("message_complete", { message });
114
- pendingAssistantResponse = "";
115
- message = {
116
- id: "",
117
- role: "assistant",
118
- timestamp: 0,
119
- parts: []
120
- };
121
- currentMode = "listening";
122
- emit("mode_change", { mode: "listening" });
123
- break;
124
- case "setup_complete":
125
- emit("status_change", { status: "connected" });
126
- break;
127
- case "error":
128
- emit("error", {
129
- error: new Error(response.data)
130
- });
131
- break;
132
- }
133
- };
134
- await client.connect();
135
- const audioStreamer = new AudioStreamer(client);
136
- const audioPlayer = new AudioPlayer();
137
- try {
138
- await audioPlayer.init();
139
- await audioStreamer.start();
140
- } catch (error) {
141
- audioStreamer.stop();
142
- audioPlayer.destroy();
143
- client.disconnect();
144
- throw error;
145
- }
146
- const connection = {
147
- async disconnect() {
148
- audioStreamer.stop();
149
- audioPlayer.destroy();
150
- client.disconnect();
151
- currentMode = "idle";
152
- emit("status_change", { status: "idle" });
153
- },
154
- async startAudioCapture() {
155
- audioStreamer.startAudioCapture();
156
- currentMode = "listening";
157
- emit("mode_change", { mode: "listening" });
158
- },
159
- stopAudioCapture() {
160
- audioStreamer.stopAudioCapture();
161
- currentMode = "idle";
162
- emit("mode_change", { mode: "idle" });
163
- },
164
- sendText(text) {
165
- client.sendTextMessage(text);
166
- currentMode = "thinking";
167
- emit("mode_change", { mode: "thinking" });
168
- },
169
- sendImage(imageData, mimeType) {
170
- client.sendImageMessage(imageData, mimeType);
171
- currentMode = "thinking";
172
- emit("mode_change", { mode: "thinking" });
173
- },
174
- sendToolResult(callId, result) {
175
- client.sendToolResponse([
176
- {
177
- id: callId,
178
- response: {
179
- result
180
- }
181
- }
182
- ]);
183
- },
184
- updateSession(config) {
185
- client.updateSession(config).catch((error) => emit("error", { error }));
186
- emit("status_change", { status: "reconnecting" });
187
- },
188
- updateToken(newToken) {
189
- client.updateToken(newToken);
190
- emit("status_change", { status: "reconnecting" });
191
- },
192
- interrupt() {
193
- audioPlayer.interrupt();
194
- currentMode = "listening";
195
- emit("mode_change", { mode: "listening" });
196
- emit("interrupted", { messageId: currentMessageId ?? void 0 });
197
- },
198
- on: realtimeEventEmitterOn,
199
- getAudioVisualization() {
200
- return {
201
- get inputLevel() {
202
- return audioStreamer.inputLevel;
203
- },
204
- get outputLevel() {
205
- return audioPlayer.outputLevel;
206
- },
207
- getInputFrequencyData() {
208
- return audioStreamer.inputFrequencyData;
209
- },
210
- getOutputFrequencyData() {
211
- return audioPlayer.outputFrequencyData;
212
- },
213
- getInputTimeDomainData() {
214
- return audioStreamer.inputTimeDomainData;
215
- },
216
- getOutputTimeDomainData() {
217
- return audioPlayer.outputTimeDomainData;
218
- },
219
- get inputSampleRate() {
220
- return audioStreamer.inputSampleRate;
221
- },
222
- get outputSampleRate() {
223
- return audioPlayer.outputSampleRate;
224
- }
225
- };
226
- }
227
- };
228
- return connection;
36
+ const { emit, on: realtimeEventEmitterOn } = createRealtimeEventEmitter();
37
+ let currentMode = "idle";
38
+ let currentMessageId = null;
39
+ let messageIdCounter = 0;
40
+ function generateMessageId() {
41
+ return `gemini-msg-${Date.now()}-${++messageIdCounter}`;
42
+ }
43
+ const client = new GeminiLiveClient(token.token, model, tools);
44
+ let message = {
45
+ id: "",
46
+ role: "assistant",
47
+ timestamp: 0,
48
+ parts: []
49
+ };
50
+ let pendingAssistantResponse = "";
51
+ client.onClose = () => {
52
+ emit("status_change", { status: "idle" });
53
+ emit("mode_change", { mode: "idle" });
54
+ };
55
+ client.onError = (error) => {
56
+ emit("error", { error });
57
+ emit("status_change", { status: "error" });
58
+ emit("mode_change", { mode: "idle" });
59
+ };
60
+ client.onReceiveResponse = (response) => {
61
+ switch (response.type) {
62
+ case "text":
63
+ message.parts.push({
64
+ type: "text",
65
+ content: response.data
66
+ });
67
+ break;
68
+ case "audio": {
69
+ const pcm = base64ToArrayBuffer(response.data.audioData);
70
+ message.parts.push({
71
+ type: "audio",
72
+ transcript: response.data.transcript,
73
+ audioData: pcm
74
+ });
75
+ if (currentMode !== "speaking") {
76
+ currentMode = "speaking";
77
+ emit("mode_change", { mode: "speaking" });
78
+ }
79
+ audioPlayer.play(pcm).catch((error) => emit("error", { error }));
80
+ break;
81
+ }
82
+ case "go_away":
83
+ emit("go_away", { timeLeft: response.data.timeLeft });
84
+ break;
85
+ case "usage_metadata":
86
+ emit("usage", {
87
+ completionTokens: response.data.responseTokenCount ?? 0,
88
+ promptTokens: response.data.promptTokenCount ?? 0,
89
+ totalTokens: response.data.totalTokenCount ?? 0
90
+ });
91
+ break;
92
+ case "input_transcription":
93
+ if (response.data.finished && currentMode !== "thinking") {
94
+ currentMode = "thinking";
95
+ emit("mode_change", { mode: "thinking" });
96
+ }
97
+ emit("transcript", {
98
+ isFinal: response.data.finished,
99
+ transcript: response.data.text,
100
+ role: "user"
101
+ });
102
+ break;
103
+ case "output_transcription":
104
+ pendingAssistantResponse += response.data.text;
105
+ emit("transcript", {
106
+ isFinal: response.data.finished,
107
+ transcript: pendingAssistantResponse,
108
+ role: "assistant"
109
+ });
110
+ break;
111
+ case "interrupted":
112
+ audioPlayer.interrupt();
113
+ currentMode = "listening";
114
+ emit("mode_change", { mode: "listening" });
115
+ emit("interrupted", { messageId: currentMessageId ?? void 0 });
116
+ break;
117
+ case "tool_call":
118
+ for (const tool of response.data.functionCalls || []) if (tool.id && tool.name) emit("tool_call", {
119
+ toolCallId: tool.id,
120
+ input: tool.args,
121
+ toolName: tool.name
122
+ });
123
+ break;
124
+ case "turn_complete":
125
+ currentMessageId = generateMessageId();
126
+ message.id = currentMessageId;
127
+ message.timestamp = Date.now();
128
+ message.parts.push({
129
+ type: "text",
130
+ content: pendingAssistantResponse
131
+ });
132
+ emit("message_complete", { message });
133
+ pendingAssistantResponse = "";
134
+ message = {
135
+ id: "",
136
+ role: "assistant",
137
+ timestamp: 0,
138
+ parts: []
139
+ };
140
+ currentMode = "listening";
141
+ emit("mode_change", { mode: "listening" });
142
+ break;
143
+ case "setup_complete":
144
+ emit("status_change", { status: "connected" });
145
+ break;
146
+ case "error":
147
+ emit("error", { error: new Error(response.data) });
148
+ break;
149
+ case "thought":
150
+ case "session_resumption_update": break;
151
+ }
152
+ };
153
+ await client.connect();
154
+ const audioStreamer = new AudioStreamer(client);
155
+ const audioPlayer = new AudioPlayer();
156
+ try {
157
+ await audioPlayer.init();
158
+ await audioStreamer.start();
159
+ } catch (error) {
160
+ audioStreamer.stop();
161
+ audioPlayer.destroy();
162
+ client.disconnect();
163
+ throw error;
164
+ }
165
+ return {
166
+ async disconnect() {
167
+ audioStreamer.stop();
168
+ audioPlayer.destroy();
169
+ client.disconnect();
170
+ currentMode = "idle";
171
+ emit("status_change", { status: "idle" });
172
+ },
173
+ async startAudioCapture() {
174
+ audioStreamer.startAudioCapture();
175
+ currentMode = "listening";
176
+ emit("mode_change", { mode: "listening" });
177
+ },
178
+ stopAudioCapture() {
179
+ audioStreamer.stopAudioCapture();
180
+ currentMode = "idle";
181
+ emit("mode_change", { mode: "idle" });
182
+ },
183
+ sendText(text) {
184
+ client.sendTextMessage(text);
185
+ currentMode = "thinking";
186
+ emit("mode_change", { mode: "thinking" });
187
+ },
188
+ sendImage(imageData, mimeType) {
189
+ client.sendImageMessage(imageData, mimeType);
190
+ currentMode = "thinking";
191
+ emit("mode_change", { mode: "thinking" });
192
+ },
193
+ sendToolResult(callId, result) {
194
+ client.sendToolResponse([{
195
+ id: callId,
196
+ response: { result }
197
+ }]);
198
+ },
199
+ updateSession(config) {
200
+ client.updateSession(config).catch((error) => emit("error", { error }));
201
+ emit("status_change", { status: "reconnecting" });
202
+ },
203
+ updateToken(newToken) {
204
+ client.updateToken(newToken);
205
+ emit("status_change", { status: "reconnecting" });
206
+ },
207
+ interrupt() {
208
+ audioPlayer.interrupt();
209
+ currentMode = "listening";
210
+ emit("mode_change", { mode: "listening" });
211
+ emit("interrupted", { messageId: currentMessageId ?? void 0 });
212
+ },
213
+ on: realtimeEventEmitterOn,
214
+ getAudioVisualization() {
215
+ return {
216
+ get inputLevel() {
217
+ return audioStreamer.inputLevel;
218
+ },
219
+ get outputLevel() {
220
+ return audioPlayer.outputLevel;
221
+ },
222
+ getInputFrequencyData() {
223
+ return audioStreamer.inputFrequencyData;
224
+ },
225
+ getOutputFrequencyData() {
226
+ return audioPlayer.outputFrequencyData;
227
+ },
228
+ getInputTimeDomainData() {
229
+ return audioStreamer.inputTimeDomainData;
230
+ },
231
+ getOutputTimeDomainData() {
232
+ return audioPlayer.outputTimeDomainData;
233
+ },
234
+ get inputSampleRate() {
235
+ return audioStreamer.inputSampleRate;
236
+ },
237
+ get outputSampleRate() {
238
+ return audioPlayer.outputSampleRate;
239
+ }
240
+ };
241
+ }
242
+ };
229
243
  }
230
- export {
231
- geminiRealtime
232
- };
233
- //# sourceMappingURL=adapter.js.map
244
+ //#endregion
245
+ export { geminiRealtime };
246
+
247
+ //# sourceMappingURL=adapter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","sources":["../../../src/realtime/adapter.ts"],"sourcesContent":["import { createRealtimeEventEmitter } from '@tanstack/ai'\nimport { AudioPlayer, AudioStreamer, base64ToArrayBuffer } from './utils'\nimport { GeminiLiveClient } from './client'\nimport type { LiveResponse } from './client'\nimport type {\n AnyClientTool,\n AudioVisualization,\n RealtimeAdapter,\n RealtimeConnection,\n RealtimeMessage,\n RealtimeMode,\n RealtimeToken,\n} from '@tanstack/ai'\nimport type { GeminiRealtimeModel, GeminiRealtimeOptions } from './types'\n\n/**\n * Creates a Gemini realtime adapter for client-side use.\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 { geminiRealtime } from '@tanstack/ai-gemini'\n *\n * const client = new RealtimeClient({\n * getToken: () => fetch('/api/realtime-token').then(r => r.json()),\n * adapter: geminiRealtime(),\n * onGoAway: () => client.updateSession({ ... }) // Resume session with new config (available only for Gemini Live adapter)\n * })\n *\n * ```\n */\nexport function geminiRealtime(\n options: GeminiRealtimeOptions = {},\n): RealtimeAdapter {\n return {\n provider: 'gemini',\n\n connect(\n token: RealtimeToken,\n clientTools?: ReadonlyArray<AnyClientTool>,\n ): Promise<RealtimeConnection> {\n return createWebSocketConnection(token, options.model, clientTools)\n },\n }\n}\n\n/**\n * Creates a WebSocket connection to Gemini's realtime API\n */\nasync function createWebSocketConnection(\n token: RealtimeToken,\n model: GeminiRealtimeModel = 'gemini-3.1-flash-live-preview',\n tools?: ReadonlyArray<AnyClientTool>,\n): Promise<RealtimeConnection> {\n const { emit, on: realtimeEventEmitterOn } = createRealtimeEventEmitter()\n\n // Current state\n let currentMode: RealtimeMode = 'idle'\n let currentMessageId: string | null = null\n let messageIdCounter = 0\n\n function generateMessageId(): string {\n return `gemini-msg-${Date.now()}-${++messageIdCounter}`\n }\n\n const client = new GeminiLiveClient(token.token, model, tools)\n\n let message: RealtimeMessage = {\n id: '',\n role: 'assistant',\n timestamp: 0,\n parts: [],\n }\n\n let pendingAssistantResponse = ''\n\n client.onClose = () => {\n emit('status_change', { status: 'idle' })\n emit('mode_change', { mode: 'idle' })\n }\n\n client.onError = (error) => {\n emit('error', { error })\n emit('status_change', { status: 'error' })\n emit('mode_change', { mode: 'idle' })\n }\n\n client.onReceiveResponse = (response: LiveResponse) => {\n switch (response.type) {\n case 'text':\n message.parts.push({\n type: 'text',\n content: response.data,\n })\n break\n case 'audio': {\n // Decode once, then reuse for both the stored message part and playback.\n const pcm = base64ToArrayBuffer(response.data.audioData)\n message.parts.push({\n type: 'audio',\n transcript: response.data.transcript,\n audioData: pcm,\n })\n if (currentMode !== 'speaking') {\n currentMode = 'speaking'\n emit('mode_change', { mode: 'speaking' })\n }\n audioPlayer.play(pcm).catch((error) => emit('error', { error }))\n break\n }\n case 'go_away':\n emit('go_away', { timeLeft: response.data.timeLeft })\n break\n case 'usage_metadata':\n emit('usage', {\n completionTokens: response.data.responseTokenCount ?? 0,\n promptTokens: response.data.promptTokenCount ?? 0,\n totalTokens: response.data.totalTokenCount ?? 0,\n })\n break\n case 'input_transcription':\n if (response.data.finished && currentMode !== 'thinking') {\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n }\n emit('transcript', {\n isFinal: response.data.finished,\n transcript: response.data.text,\n role: 'user',\n })\n break\n case 'output_transcription':\n pendingAssistantResponse += response.data.text\n emit('transcript', {\n isFinal: response.data.finished,\n transcript: pendingAssistantResponse,\n role: 'assistant',\n })\n break\n case 'interrupted':\n audioPlayer.interrupt()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n emit('interrupted', { messageId: currentMessageId ?? undefined })\n break\n case 'tool_call':\n for (const tool of response.data.functionCalls || []) {\n if (tool.id && tool.name) {\n emit('tool_call', {\n toolCallId: tool.id,\n input: tool.args,\n toolName: tool.name,\n })\n }\n }\n break\n case 'turn_complete':\n currentMessageId = generateMessageId()\n message.id = currentMessageId\n message.timestamp = Date.now()\n message.parts.push({\n type: 'text',\n content: pendingAssistantResponse,\n })\n emit('message_complete', { message })\n\n pendingAssistantResponse = ''\n message = {\n id: '',\n role: 'assistant',\n timestamp: 0,\n parts: [],\n }\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n break\n case 'setup_complete':\n emit('status_change', { status: 'connected' })\n break\n case 'error':\n emit('error', {\n error: new Error(response.data),\n })\n break\n case 'thought':\n case 'session_resumption_update':\n // Handled internally by GeminiLiveClient; not surfaced to the client.\n break\n }\n }\n\n await client.connect()\n\n const audioStreamer = new AudioStreamer(client)\n const audioPlayer = new AudioPlayer()\n try {\n await audioPlayer.init()\n await audioStreamer.start()\n } catch (error) {\n // Tear down the socket + audio graph if mic/worklet setup fails (e.g. the\n // user denied microphone access) rather than leaking an open connection.\n audioStreamer.stop()\n audioPlayer.destroy()\n client.disconnect()\n throw error\n }\n\n const connection: RealtimeConnection = {\n async disconnect() {\n audioStreamer.stop()\n audioPlayer.destroy()\n client.disconnect()\n currentMode = 'idle'\n emit('status_change', { status: 'idle' })\n },\n\n async startAudioCapture() {\n // Audio capture is established during connection setup\n audioStreamer.startAudioCapture()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n },\n\n stopAudioCapture() {\n audioStreamer.stopAudioCapture()\n currentMode = 'idle'\n emit('mode_change', { mode: 'idle' })\n },\n\n sendText(text: string) {\n client.sendTextMessage(text)\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n },\n\n sendImage(imageData: string, mimeType: string) {\n client.sendImageMessage(imageData, mimeType)\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n },\n\n sendToolResult(callId: string, result: string) {\n client.sendToolResponse([\n {\n id: callId,\n response: {\n result,\n },\n },\n ])\n },\n\n updateSession(config) {\n client.updateSession(config).catch((error) => emit('error', { error }))\n emit('status_change', { status: 'reconnecting' })\n },\n\n updateToken(newToken) {\n client.updateToken(newToken)\n emit('status_change', { status: 'reconnecting' })\n },\n\n interrupt() {\n audioPlayer.interrupt()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n emit('interrupted', { messageId: currentMessageId ?? undefined })\n },\n on: realtimeEventEmitterOn,\n getAudioVisualization(): AudioVisualization {\n return {\n get inputLevel() {\n return audioStreamer.inputLevel\n },\n\n get outputLevel() {\n return audioPlayer.outputLevel\n },\n\n getInputFrequencyData() {\n return audioStreamer.inputFrequencyData\n },\n\n getOutputFrequencyData() {\n return audioPlayer.outputFrequencyData\n },\n\n getInputTimeDomainData() {\n return audioStreamer.inputTimeDomainData\n },\n\n getOutputTimeDomainData() {\n return audioPlayer.outputTimeDomainData\n },\n\n get inputSampleRate() {\n return audioStreamer.inputSampleRate\n },\n\n get outputSampleRate() {\n return audioPlayer.outputSampleRate\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 { createRealtimeEventEmitter } from '@tanstack/ai'\nimport { AudioPlayer, AudioStreamer, base64ToArrayBuffer } from './utils'\nimport { GeminiLiveClient } from './client'\nimport type { LiveResponse } from './client'\nimport type {\n AnyClientTool,\n AudioVisualization,\n RealtimeAdapter,\n RealtimeConnection,\n RealtimeMessage,\n RealtimeMode,\n RealtimeToken,\n} from '@tanstack/ai'\nimport type { GeminiRealtimeModel, GeminiRealtimeOptions } from './types'\n\n/**\n * Creates a Gemini realtime adapter for client-side use.\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 { geminiRealtime } from '@tanstack/ai-gemini'\n *\n * const client = new RealtimeClient({\n * getToken: () => fetch('/api/realtime-token').then(r => r.json()),\n * adapter: geminiRealtime(),\n * onGoAway: () => client.updateSession({ ... }) // Resume session with new config (available only for Gemini Live adapter)\n * })\n *\n * ```\n */\nexport function geminiRealtime(\n options: GeminiRealtimeOptions = {},\n): RealtimeAdapter {\n return {\n provider: 'gemini',\n\n connect(\n token: RealtimeToken,\n clientTools?: ReadonlyArray<AnyClientTool>,\n ): Promise<RealtimeConnection> {\n return createWebSocketConnection(token, options.model, clientTools)\n },\n }\n}\n\n/**\n * Creates a WebSocket connection to Gemini's realtime API\n */\nasync function createWebSocketConnection(\n token: RealtimeToken,\n model: GeminiRealtimeModel = 'gemini-3.1-flash-live-preview',\n tools?: ReadonlyArray<AnyClientTool>,\n): Promise<RealtimeConnection> {\n const { emit, on: realtimeEventEmitterOn } = createRealtimeEventEmitter()\n\n // Current state\n let currentMode: RealtimeMode = 'idle'\n let currentMessageId: string | null = null\n let messageIdCounter = 0\n\n function generateMessageId(): string {\n return `gemini-msg-${Date.now()}-${++messageIdCounter}`\n }\n\n const client = new GeminiLiveClient(token.token, model, tools)\n\n let message: RealtimeMessage = {\n id: '',\n role: 'assistant',\n timestamp: 0,\n parts: [],\n }\n\n let pendingAssistantResponse = ''\n\n client.onClose = () => {\n emit('status_change', { status: 'idle' })\n emit('mode_change', { mode: 'idle' })\n }\n\n client.onError = (error) => {\n emit('error', { error })\n emit('status_change', { status: 'error' })\n emit('mode_change', { mode: 'idle' })\n }\n\n client.onReceiveResponse = (response: LiveResponse) => {\n switch (response.type) {\n case 'text':\n message.parts.push({\n type: 'text',\n content: response.data,\n })\n break\n case 'audio': {\n // Decode once, then reuse for both the stored message part and playback.\n const pcm = base64ToArrayBuffer(response.data.audioData)\n message.parts.push({\n type: 'audio',\n transcript: response.data.transcript,\n audioData: pcm,\n })\n if (currentMode !== 'speaking') {\n currentMode = 'speaking'\n emit('mode_change', { mode: 'speaking' })\n }\n audioPlayer.play(pcm).catch((error) => emit('error', { error }))\n break\n }\n case 'go_away':\n emit('go_away', { timeLeft: response.data.timeLeft })\n break\n case 'usage_metadata':\n emit('usage', {\n completionTokens: response.data.responseTokenCount ?? 0,\n promptTokens: response.data.promptTokenCount ?? 0,\n totalTokens: response.data.totalTokenCount ?? 0,\n })\n break\n case 'input_transcription':\n if (response.data.finished && currentMode !== 'thinking') {\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n }\n emit('transcript', {\n isFinal: response.data.finished,\n transcript: response.data.text,\n role: 'user',\n })\n break\n case 'output_transcription':\n pendingAssistantResponse += response.data.text\n emit('transcript', {\n isFinal: response.data.finished,\n transcript: pendingAssistantResponse,\n role: 'assistant',\n })\n break\n case 'interrupted':\n audioPlayer.interrupt()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n emit('interrupted', { messageId: currentMessageId ?? undefined })\n break\n case 'tool_call':\n for (const tool of response.data.functionCalls || []) {\n if (tool.id && tool.name) {\n emit('tool_call', {\n toolCallId: tool.id,\n input: tool.args,\n toolName: tool.name,\n })\n }\n }\n break\n case 'turn_complete':\n currentMessageId = generateMessageId()\n message.id = currentMessageId\n message.timestamp = Date.now()\n message.parts.push({\n type: 'text',\n content: pendingAssistantResponse,\n })\n emit('message_complete', { message })\n\n pendingAssistantResponse = ''\n message = {\n id: '',\n role: 'assistant',\n timestamp: 0,\n parts: [],\n }\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n break\n case 'setup_complete':\n emit('status_change', { status: 'connected' })\n break\n case 'error':\n emit('error', {\n error: new Error(response.data),\n })\n break\n case 'thought':\n case 'session_resumption_update':\n // Handled internally by GeminiLiveClient; not surfaced to the client.\n break\n }\n }\n\n await client.connect()\n\n const audioStreamer = new AudioStreamer(client)\n const audioPlayer = new AudioPlayer()\n try {\n await audioPlayer.init()\n await audioStreamer.start()\n } catch (error) {\n // Tear down the socket + audio graph if mic/worklet setup fails (e.g. the\n // user denied microphone access) rather than leaking an open connection.\n audioStreamer.stop()\n audioPlayer.destroy()\n client.disconnect()\n throw error\n }\n\n const connection: RealtimeConnection = {\n async disconnect() {\n audioStreamer.stop()\n audioPlayer.destroy()\n client.disconnect()\n currentMode = 'idle'\n emit('status_change', { status: 'idle' })\n },\n\n async startAudioCapture() {\n // Audio capture is established during connection setup\n audioStreamer.startAudioCapture()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n },\n\n stopAudioCapture() {\n audioStreamer.stopAudioCapture()\n currentMode = 'idle'\n emit('mode_change', { mode: 'idle' })\n },\n\n sendText(text: string) {\n client.sendTextMessage(text)\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n },\n\n sendImage(imageData: string, mimeType: string) {\n client.sendImageMessage(imageData, mimeType)\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n },\n\n sendToolResult(callId: string, result: string) {\n client.sendToolResponse([\n {\n id: callId,\n response: {\n result,\n },\n },\n ])\n },\n\n updateSession(config) {\n client.updateSession(config).catch((error) => emit('error', { error }))\n emit('status_change', { status: 'reconnecting' })\n },\n\n updateToken(newToken) {\n client.updateToken(newToken)\n emit('status_change', { status: 'reconnecting' })\n },\n\n interrupt() {\n audioPlayer.interrupt()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n emit('interrupted', { messageId: currentMessageId ?? undefined })\n },\n on: realtimeEventEmitterOn,\n getAudioVisualization(): AudioVisualization {\n return {\n get inputLevel() {\n return audioStreamer.inputLevel\n },\n\n get outputLevel() {\n return audioPlayer.outputLevel\n },\n\n getInputFrequencyData() {\n return audioStreamer.inputFrequencyData\n },\n\n getOutputFrequencyData() {\n return audioPlayer.outputFrequencyData\n },\n\n getInputTimeDomainData() {\n return audioStreamer.inputTimeDomainData\n },\n\n getOutputTimeDomainData() {\n return audioPlayer.outputTimeDomainData\n },\n\n get inputSampleRate() {\n return audioStreamer.inputSampleRate\n },\n\n get outputSampleRate() {\n return audioPlayer.outputSampleRate\n },\n }\n },\n }\n\n return connection\n}\n"],"mappings":"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"}