@tanstack/ai-gemini 0.20.0 → 0.21.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 (62) 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/image.js +224 -225
  5. package/dist/esm/adapters/image.js.map +1 -1
  6. package/dist/esm/adapters/summarize.js +33 -12
  7. package/dist/esm/adapters/summarize.js.map +1 -1
  8. package/dist/esm/adapters/text.js +574 -650
  9. package/dist/esm/adapters/text.js.map +1 -1
  10. package/dist/esm/adapters/tts.js +197 -178
  11. package/dist/esm/adapters/tts.js.map +1 -1
  12. package/dist/esm/adapters/video.js +456 -409
  13. package/dist/esm/adapters/video.js.map +1 -1
  14. package/dist/esm/experimental/index.js +1 -6
  15. package/dist/esm/experimental/text-interactions/adapter.js +877 -1014
  16. package/dist/esm/experimental/text-interactions/adapter.js.map +1 -1
  17. package/dist/esm/image/image-provider-options.js +74 -54
  18. package/dist/esm/image/image-provider-options.js.map +1 -1
  19. package/dist/esm/index.js +4 -36
  20. package/dist/esm/model-meta.d.ts +53 -5
  21. package/dist/esm/model-meta.js +733 -129
  22. package/dist/esm/model-meta.js.map +1 -1
  23. package/dist/esm/realtime/adapter.js +242 -228
  24. package/dist/esm/realtime/adapter.js.map +1 -1
  25. package/dist/esm/realtime/client.js +314 -381
  26. package/dist/esm/realtime/client.js.map +1 -1
  27. package/dist/esm/realtime/index.js +3 -0
  28. package/dist/esm/realtime/token.js +50 -36
  29. package/dist/esm/realtime/token.js.map +1 -1
  30. package/dist/esm/realtime/utils.js +202 -245
  31. package/dist/esm/realtime/utils.js.map +1 -1
  32. package/dist/esm/tools/code-execution-tool.js +11 -13
  33. package/dist/esm/tools/code-execution-tool.js.map +1 -1
  34. package/dist/esm/tools/computer-use-tool.js +18 -28
  35. package/dist/esm/tools/computer-use-tool.js.map +1 -1
  36. package/dist/esm/tools/file-search-tool.js +11 -14
  37. package/dist/esm/tools/file-search-tool.js.map +1 -1
  38. package/dist/esm/tools/function-declaration-tool.js +11 -25
  39. package/dist/esm/tools/function-declaration-tool.js.map +1 -1
  40. package/dist/esm/tools/google-maps-tool.js +11 -14
  41. package/dist/esm/tools/google-maps-tool.js.map +1 -1
  42. package/dist/esm/tools/google-search-retriveal-tool.js +11 -14
  43. package/dist/esm/tools/google-search-retriveal-tool.js.map +1 -1
  44. package/dist/esm/tools/google-search-tool.js +11 -14
  45. package/dist/esm/tools/google-search-tool.js.map +1 -1
  46. package/dist/esm/tools/index.js +2 -13
  47. package/dist/esm/tools/tool-converter.js +64 -57
  48. package/dist/esm/tools/tool-converter.js.map +1 -1
  49. package/dist/esm/tools/url-context-tool.js +11 -13
  50. package/dist/esm/tools/url-context-tool.js.map +1 -1
  51. package/dist/esm/usage.js +81 -87
  52. package/dist/esm/usage.js.map +1 -1
  53. package/dist/esm/utils/client.js +31 -24
  54. package/dist/esm/utils/client.js.map +1 -1
  55. package/dist/esm/utils/index.js +2 -0
  56. package/dist/esm/video/video-provider-options.js +73 -20
  57. package/dist/esm/video/video-provider-options.js.map +1 -1
  58. package/package.json +7 -7
  59. package/src/model-meta.ts +120 -10
  60. package/dist/esm/experimental/index.js.map +0 -1
  61. package/dist/esm/index.js.map +0 -1
  62. package/dist/esm/tools/index.js.map +0 -1
@@ -1,389 +1,322 @@
1
+ import { ActivityHandling, EndSensitivity, Modality, StartSensitivity, TurnCoverage } from "@google/genai";
1
2
  import { convertSchemaToJsonSchema } from "@tanstack/ai";
2
- import { Modality, StartSensitivity, EndSensitivity, ActivityHandling, TurnCoverage } from "@google/genai";
3
+ //#region src/realtime/client.ts
4
+ /** Build a Gemini FunctionDeclaration from an isomorphic client tool (Zod). */
3
5
  function clientToolToDeclaration(tool) {
4
- return {
5
- name: tool.name,
6
- description: tool.description,
7
- parametersJsonSchema: convertSchemaToJsonSchema(tool.inputSchema),
8
- responseJsonSchema: convertSchemaToJsonSchema(tool.outputSchema)
9
- };
6
+ return {
7
+ name: tool.name,
8
+ description: tool.description,
9
+ parametersJsonSchema: convertSchemaToJsonSchema(tool.inputSchema),
10
+ responseJsonSchema: convertSchemaToJsonSchema(tool.outputSchema)
11
+ };
10
12
  }
13
+ /** Build a Gemini FunctionDeclaration from an already-serialized tool config. */
11
14
  function toolConfigToDeclaration(tool) {
12
- return {
13
- name: tool.name,
14
- description: tool.description,
15
- parametersJsonSchema: tool.inputSchema,
16
- responseJsonSchema: tool.outputSchema
17
- };
15
+ return {
16
+ name: tool.name,
17
+ description: tool.description,
18
+ parametersJsonSchema: tool.inputSchema,
19
+ responseJsonSchema: tool.outputSchema
20
+ };
18
21
  }
22
+ /**
23
+ * Parses response messages from the Gemini Live API
24
+ */
25
+ /**
26
+ * Parses ALL response types from a single server message.
27
+ * The server can now bundle multiple fields (e.g. audio + transcription)
28
+ * in the same message. Returns an array of response objects.
29
+ */
19
30
  function parseResponseMessages(data) {
20
- const responses = [];
21
- const serverContent = data.serverContent;
22
- const parts = serverContent?.modelTurn?.parts;
23
- if (data.setupComplete) {
24
- responses.push({ type: "setup_complete", data: "", endOfTurn: false });
25
- return responses;
26
- }
27
- if (data.toolCall) {
28
- responses.push({
29
- type: "tool_call",
30
- data: data.toolCall,
31
- endOfTurn: false
32
- });
33
- return responses;
34
- }
35
- if (data.sessionResumptionUpdate) {
36
- responses.push({
37
- type: "session_resumption_update",
38
- data: data.sessionResumptionUpdate,
39
- endOfTurn: false
40
- });
41
- }
42
- if (data.goAway) {
43
- responses.push({ type: "go_away", data: data.goAway, endOfTurn: false });
44
- }
45
- if (data.usageMetadata) {
46
- responses.push({
47
- type: "usage_metadata",
48
- data: data.usageMetadata,
49
- endOfTurn: false
50
- });
51
- }
52
- if (parts?.length) {
53
- for (const part of parts) {
54
- if (part.inlineData?.data) {
55
- responses.push({
56
- type: "audio",
57
- data: {
58
- audioData: part.inlineData.data,
59
- // The transcription is independent to the model turn, which means it doesn't imply any ordering between transcription and model turn.
60
- transcript: ""
61
- },
62
- endOfTurn: false
63
- });
64
- } else if (part.text) {
65
- responses.push({
66
- type: part.thought ? "thought" : "text",
67
- data: part.text,
68
- endOfTurn: false
69
- });
70
- }
71
- }
72
- }
73
- if (serverContent?.inputTranscription) {
74
- responses.push({
75
- type: "input_transcription",
76
- data: {
77
- text: serverContent.inputTranscription.text || "",
78
- finished: serverContent.inputTranscription.finished || false
79
- },
80
- endOfTurn: false
81
- });
82
- }
83
- if (serverContent?.outputTranscription) {
84
- responses.push({
85
- type: "output_transcription",
86
- data: {
87
- text: serverContent.outputTranscription.text || "",
88
- finished: serverContent.outputTranscription.finished || false
89
- },
90
- endOfTurn: false
91
- });
92
- }
93
- if (serverContent?.interrupted) {
94
- responses.push({ type: "interrupted", data: "", endOfTurn: false });
95
- }
96
- if (serverContent?.turnComplete) {
97
- responses.push({ type: "turn_complete", data: "", endOfTurn: true });
98
- }
99
- return responses;
31
+ const responses = [];
32
+ const serverContent = data.serverContent;
33
+ const parts = serverContent?.modelTurn?.parts;
34
+ if (data.setupComplete) {
35
+ responses.push({
36
+ type: "setup_complete",
37
+ data: "",
38
+ endOfTurn: false
39
+ });
40
+ return responses;
41
+ }
42
+ if (data.toolCall) {
43
+ responses.push({
44
+ type: "tool_call",
45
+ data: data.toolCall,
46
+ endOfTurn: false
47
+ });
48
+ return responses;
49
+ }
50
+ if (data.sessionResumptionUpdate) responses.push({
51
+ type: "session_resumption_update",
52
+ data: data.sessionResumptionUpdate,
53
+ endOfTurn: false
54
+ });
55
+ if (data.goAway) responses.push({
56
+ type: "go_away",
57
+ data: data.goAway,
58
+ endOfTurn: false
59
+ });
60
+ if (data.usageMetadata) responses.push({
61
+ type: "usage_metadata",
62
+ data: data.usageMetadata,
63
+ endOfTurn: false
64
+ });
65
+ if (parts?.length) {
66
+ for (const part of parts) if (part.inlineData?.data) responses.push({
67
+ type: "audio",
68
+ data: {
69
+ audioData: part.inlineData.data,
70
+ transcript: ""
71
+ },
72
+ endOfTurn: false
73
+ });
74
+ else if (part.text) responses.push({
75
+ type: part.thought ? "thought" : "text",
76
+ data: part.text,
77
+ endOfTurn: false
78
+ });
79
+ }
80
+ if (serverContent?.inputTranscription) responses.push({
81
+ type: "input_transcription",
82
+ data: {
83
+ text: serverContent.inputTranscription.text || "",
84
+ finished: serverContent.inputTranscription.finished || false
85
+ },
86
+ endOfTurn: false
87
+ });
88
+ if (serverContent?.outputTranscription) responses.push({
89
+ type: "output_transcription",
90
+ data: {
91
+ text: serverContent.outputTranscription.text || "",
92
+ finished: serverContent.outputTranscription.finished || false
93
+ },
94
+ endOfTurn: false
95
+ });
96
+ if (serverContent?.interrupted) responses.push({
97
+ type: "interrupted",
98
+ data: "",
99
+ endOfTurn: false
100
+ });
101
+ if (serverContent?.turnComplete) responses.push({
102
+ type: "turn_complete",
103
+ data: "",
104
+ endOfTurn: true
105
+ });
106
+ return responses;
100
107
  }
101
- class GeminiLiveClient {
102
- token = null;
103
- model = null;
104
- responseModalities = [Modality.AUDIO];
105
- systemInstructions = "";
106
- googleGrounding = false;
107
- voiceName = "Puck";
108
- temperature = 1;
109
- inputAudioTranscription = false;
110
- outputAudioTranscription = false;
111
- contextWindowCompression;
112
- proactiveAudio = false;
113
- enableAffectiveDialog = false;
114
- thinkingConfig;
115
- speechLanguageCode;
116
- maxOutputTokens;
117
- functionDeclarations = [];
118
- automaticActivityDetection = {
119
- disabled: false,
120
- silence_duration_ms: 2e3,
121
- prefix_padding_ms: 500,
122
- end_of_speech_sensitivity: EndSensitivity.END_SENSITIVITY_UNSPECIFIED,
123
- start_of_speech_sensitivity: StartSensitivity.START_SENSITIVITY_UNSPECIFIED
124
- };
125
- activityHandling = ActivityHandling.ACTIVITY_HANDLING_UNSPECIFIED;
126
- webSocket = null;
127
- lastResumptionUpdate = null;
128
- setupComplete = false;
129
- connected = false;
130
- onReceiveResponse = () => {
131
- };
132
- onOpen = () => {
133
- };
134
- onClose = () => {
135
- };
136
- onError = () => {
137
- };
138
- constructor(token, model, tools) {
139
- this.token = token;
140
- this.model = model;
141
- if (tools) {
142
- this.functionDeclarations = tools.map(clientToolToDeclaration);
143
- }
144
- }
145
- get isConnected() {
146
- return this.connected;
147
- }
148
- get isSetupComplete() {
149
- return this.setupComplete;
150
- }
151
- /**
152
- * Connection management
153
- */
154
- connect() {
155
- return new Promise((resolve, reject) => {
156
- const socket = new WebSocket(
157
- `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContentConstrained?access_token=${this.token}`
158
- );
159
- this.webSocket = socket;
160
- let errored = false;
161
- socket.onclose = () => {
162
- this.connected = false;
163
- this.setupComplete = false;
164
- if (!errored) this.onClose();
165
- reject(new Error("WebSocket closed before setup completed"));
166
- };
167
- socket.onerror = () => {
168
- errored = true;
169
- this.connected = false;
170
- this.setupComplete = false;
171
- const error = new Error("Gemini realtime WebSocket connection error");
172
- this.onError(error);
173
- reject(error);
174
- };
175
- socket.onopen = () => {
176
- this.connected = true;
177
- this.onOpen();
178
- resolve();
179
- };
180
- socket.onmessage = (event) => {
181
- void this.onReceiveMessage(event);
182
- };
183
- });
184
- }
185
- disconnect() {
186
- if (this.webSocket) {
187
- this.webSocket.onclose = null;
188
- this.webSocket.onerror = null;
189
- this.webSocket.onopen = null;
190
- this.webSocket.onmessage = null;
191
- this.webSocket.close();
192
- this.webSocket = null;
193
- }
194
- this.connected = false;
195
- this.setupComplete = false;
196
- }
197
- /**
198
- * Session management
199
- */
200
- sendInitialSetupMessage(resume = false) {
201
- const tools = this.functionDeclarations;
202
- const setup = {
203
- model: `models/${this.model}`,
204
- generationConfig: {
205
- responseModalities: this.responseModalities,
206
- temperature: this.temperature,
207
- speechConfig: {
208
- languageCode: this.speechLanguageCode,
209
- voiceConfig: {
210
- prebuiltVoiceConfig: {
211
- voiceName: this.voiceName
212
- }
213
- }
214
- },
215
- enableAffectiveDialog: this.enableAffectiveDialog,
216
- maxOutputTokens: this.maxOutputTokens,
217
- thinkingConfig: this.thinkingConfig
218
- },
219
- sessionResumption: {
220
- transparent: true,
221
- handle: resume ? this.lastResumptionUpdate?.newHandle : void 0
222
- },
223
- contextWindowCompression: this.contextWindowCompression,
224
- proactivity: {
225
- proactiveAudio: this.proactiveAudio
226
- },
227
- systemInstruction: { parts: [{ text: this.systemInstructions }] },
228
- tools: [{ functionDeclarations: tools }],
229
- realtimeInputConfig: {
230
- automaticActivityDetection: {
231
- disabled: this.automaticActivityDetection.disabled,
232
- silenceDurationMs: this.automaticActivityDetection.silence_duration_ms,
233
- prefixPaddingMs: this.automaticActivityDetection.prefix_padding_ms,
234
- endOfSpeechSensitivity: this.automaticActivityDetection.end_of_speech_sensitivity,
235
- startOfSpeechSensitivity: this.automaticActivityDetection.start_of_speech_sensitivity
236
- },
237
- activityHandling: this.activityHandling,
238
- turnCoverage: TurnCoverage.TURN_INCLUDES_ONLY_ACTIVITY
239
- }
240
- };
241
- if (this.inputAudioTranscription) {
242
- setup.inputAudioTranscription = {};
243
- }
244
- if (this.outputAudioTranscription) {
245
- setup.outputAudioTranscription = {};
246
- }
247
- if (this.googleGrounding) {
248
- console.warn(
249
- "Google Grounding enabled, removing custom function calls if any."
250
- );
251
- setup.tools = [{ googleSearch: {} }];
252
- }
253
- this.sendMessage({ setup });
254
- }
255
- async restartSession(resume = false) {
256
- this.disconnect();
257
- await this.connect();
258
- this.sendInitialSetupMessage(resume);
259
- }
260
- updateToken(token) {
261
- this.token = token.token;
262
- const resume = !(token.config.model && this.model != token.config.model);
263
- if (!resume) {
264
- this.model = token.config.model;
265
- }
266
- this.restartSession(resume).catch(
267
- (err) => this.onError(err instanceof Error ? err : new Error(String(err)))
268
- );
269
- }
270
- async updateSession(config) {
271
- if (config.model && !this.setupComplete) {
272
- this.model = config.model;
273
- }
274
- if (config.instructions) {
275
- this.systemInstructions = config.instructions;
276
- }
277
- if (config.tools) {
278
- this.functionDeclarations = config.tools.map(toolConfigToDeclaration);
279
- }
280
- if (config.maxOutputTokens) {
281
- this.maxOutputTokens = typeof config.maxOutputTokens === "number" ? config.maxOutputTokens : void 0;
282
- }
283
- if (config.temperature) {
284
- this.temperature = config.temperature;
285
- }
286
- if (config.voice) {
287
- this.voiceName = config.voice;
288
- }
289
- const providerOptions = config.providerOptions;
290
- if (providerOptions?.googleGrounding) {
291
- this.googleGrounding = providerOptions.googleGrounding;
292
- }
293
- if (providerOptions?.proactiveAudio) {
294
- this.proactiveAudio = providerOptions.proactiveAudio;
295
- }
296
- if (providerOptions?.enableAffectiveDialog) {
297
- this.enableAffectiveDialog = providerOptions.enableAffectiveDialog;
298
- }
299
- if (providerOptions?.contextWindowCompression) {
300
- this.contextWindowCompression = providerOptions.contextWindowCompression;
301
- }
302
- if (providerOptions?.thinkingConfig) {
303
- this.thinkingConfig = providerOptions.thinkingConfig;
304
- }
305
- if (providerOptions?.languageCode) {
306
- this.speechLanguageCode = providerOptions.languageCode;
307
- }
308
- const includeTranscription = config.outputModalities?.includes("text") || false;
309
- this.inputAudioTranscription = includeTranscription;
310
- this.outputAudioTranscription = includeTranscription;
311
- if (!this.setupComplete) {
312
- this.sendInitialSetupMessage();
313
- } else {
314
- return this.restartSession(true);
315
- }
316
- }
317
- /**
318
- * Message transmission & receiving
319
- */
320
- sendMessage(message) {
321
- if (this.webSocket?.readyState === WebSocket.OPEN) {
322
- this.webSocket.send(JSON.stringify(message));
323
- } else {
324
- this.onError(
325
- new Error("Cannot send message: Gemini realtime socket is not open")
326
- );
327
- }
328
- }
329
- async onReceiveMessage(messageEvent) {
330
- let jsonData;
331
- if (messageEvent.data instanceof Blob) {
332
- jsonData = await messageEvent.data.text();
333
- } else if (messageEvent.data instanceof ArrayBuffer) {
334
- jsonData = new TextDecoder().decode(messageEvent.data);
335
- } else {
336
- jsonData = messageEvent.data;
337
- }
338
- try {
339
- const messageData = JSON.parse(jsonData);
340
- const responses = parseResponseMessages(messageData);
341
- for (const response of responses) {
342
- if (response.type === "session_resumption_update" && response.data.resumable) {
343
- this.lastResumptionUpdate = response.data;
344
- }
345
- if (response.type === "setup_complete") {
346
- this.setupComplete = true;
347
- }
348
- this.onReceiveResponse(response);
349
- }
350
- } catch (err) {
351
- this.onError(err instanceof Error ? err : new Error(String(err)));
352
- }
353
- }
354
- sendRealtimeInputMessage(data, mimeType) {
355
- const blob = { mimeType, data };
356
- if (mimeType.startsWith("audio/")) {
357
- this.sendMessage({ realtimeInput: { audio: blob } });
358
- } else if (mimeType.startsWith("image/") || mimeType.startsWith("video/")) {
359
- this.sendMessage({ realtimeInput: { video: blob } });
360
- }
361
- }
362
- sendAudioMessage(base64PCM) {
363
- this.sendRealtimeInputMessage(base64PCM, "audio/pcm");
364
- }
365
- sendImageMessage(base64, mimeType = "image/jpeg") {
366
- this.sendRealtimeInputMessage(base64, mimeType);
367
- }
368
- sendTextMessage(text) {
369
- const message = {
370
- realtimeInput: {
371
- text
372
- }
373
- };
374
- this.sendMessage(message);
375
- }
376
- sendToolResponse(functionResponses) {
377
- const message = {
378
- toolResponse: {
379
- functionResponses
380
- }
381
- };
382
- this.sendMessage(message);
383
- }
384
- }
385
- export {
386
- GeminiLiveClient,
387
- parseResponseMessages
108
+ var GeminiLiveClient = class {
109
+ token = null;
110
+ model = null;
111
+ responseModalities = [Modality.AUDIO];
112
+ systemInstructions = "";
113
+ googleGrounding = false;
114
+ voiceName = "Puck";
115
+ temperature = 1;
116
+ inputAudioTranscription = false;
117
+ outputAudioTranscription = false;
118
+ contextWindowCompression;
119
+ proactiveAudio = false;
120
+ enableAffectiveDialog = false;
121
+ thinkingConfig;
122
+ speechLanguageCode;
123
+ maxOutputTokens;
124
+ functionDeclarations = [];
125
+ automaticActivityDetection = {
126
+ disabled: false,
127
+ silence_duration_ms: 2e3,
128
+ prefix_padding_ms: 500,
129
+ end_of_speech_sensitivity: EndSensitivity.END_SENSITIVITY_UNSPECIFIED,
130
+ start_of_speech_sensitivity: StartSensitivity.START_SENSITIVITY_UNSPECIFIED
131
+ };
132
+ activityHandling = ActivityHandling.ACTIVITY_HANDLING_UNSPECIFIED;
133
+ webSocket = null;
134
+ lastResumptionUpdate = null;
135
+ setupComplete = false;
136
+ connected = false;
137
+ onReceiveResponse = () => {};
138
+ onOpen = () => {};
139
+ onClose = () => {};
140
+ onError = () => {};
141
+ constructor(token, model, tools) {
142
+ this.token = token;
143
+ this.model = model;
144
+ if (tools) this.functionDeclarations = tools.map(clientToolToDeclaration);
145
+ }
146
+ get isConnected() {
147
+ return this.connected;
148
+ }
149
+ get isSetupComplete() {
150
+ return this.setupComplete;
151
+ }
152
+ /**
153
+ * Connection management
154
+ */
155
+ connect() {
156
+ return new Promise((resolve, reject) => {
157
+ const socket = new WebSocket(`wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContentConstrained?access_token=${this.token}`);
158
+ this.webSocket = socket;
159
+ let errored = false;
160
+ socket.onclose = () => {
161
+ this.connected = false;
162
+ this.setupComplete = false;
163
+ if (!errored) this.onClose();
164
+ reject(/* @__PURE__ */ new Error("WebSocket closed before setup completed"));
165
+ };
166
+ socket.onerror = () => {
167
+ errored = true;
168
+ this.connected = false;
169
+ this.setupComplete = false;
170
+ const error = /* @__PURE__ */ new Error("Gemini realtime WebSocket connection error");
171
+ this.onError(error);
172
+ reject(error);
173
+ };
174
+ socket.onopen = () => {
175
+ this.connected = true;
176
+ this.onOpen();
177
+ resolve();
178
+ };
179
+ socket.onmessage = (event) => {
180
+ this.onReceiveMessage(event);
181
+ };
182
+ });
183
+ }
184
+ disconnect() {
185
+ if (this.webSocket) {
186
+ this.webSocket.onclose = null;
187
+ this.webSocket.onerror = null;
188
+ this.webSocket.onopen = null;
189
+ this.webSocket.onmessage = null;
190
+ this.webSocket.close();
191
+ this.webSocket = null;
192
+ }
193
+ this.connected = false;
194
+ this.setupComplete = false;
195
+ }
196
+ /**
197
+ * Session management
198
+ */
199
+ sendInitialSetupMessage(resume = false) {
200
+ const tools = this.functionDeclarations;
201
+ const setup = {
202
+ model: `models/${this.model}`,
203
+ generationConfig: {
204
+ responseModalities: this.responseModalities,
205
+ temperature: this.temperature,
206
+ speechConfig: {
207
+ languageCode: this.speechLanguageCode,
208
+ voiceConfig: { prebuiltVoiceConfig: { voiceName: this.voiceName } }
209
+ },
210
+ enableAffectiveDialog: this.enableAffectiveDialog,
211
+ maxOutputTokens: this.maxOutputTokens,
212
+ thinkingConfig: this.thinkingConfig
213
+ },
214
+ sessionResumption: {
215
+ transparent: true,
216
+ handle: resume ? this.lastResumptionUpdate?.newHandle : void 0
217
+ },
218
+ contextWindowCompression: this.contextWindowCompression,
219
+ proactivity: { proactiveAudio: this.proactiveAudio },
220
+ systemInstruction: { parts: [{ text: this.systemInstructions }] },
221
+ tools: [{ functionDeclarations: tools }],
222
+ realtimeInputConfig: {
223
+ automaticActivityDetection: {
224
+ disabled: this.automaticActivityDetection.disabled,
225
+ silenceDurationMs: this.automaticActivityDetection.silence_duration_ms,
226
+ prefixPaddingMs: this.automaticActivityDetection.prefix_padding_ms,
227
+ endOfSpeechSensitivity: this.automaticActivityDetection.end_of_speech_sensitivity,
228
+ startOfSpeechSensitivity: this.automaticActivityDetection.start_of_speech_sensitivity
229
+ },
230
+ activityHandling: this.activityHandling,
231
+ turnCoverage: TurnCoverage.TURN_INCLUDES_ONLY_ACTIVITY
232
+ }
233
+ };
234
+ if (this.inputAudioTranscription) setup.inputAudioTranscription = {};
235
+ if (this.outputAudioTranscription) setup.outputAudioTranscription = {};
236
+ if (this.googleGrounding) {
237
+ console.warn("Google Grounding enabled, removing custom function calls if any.");
238
+ setup.tools = [{ googleSearch: {} }];
239
+ }
240
+ this.sendMessage({ setup });
241
+ }
242
+ async restartSession(resume = false) {
243
+ this.disconnect();
244
+ await this.connect();
245
+ this.sendInitialSetupMessage(resume);
246
+ }
247
+ updateToken(token) {
248
+ this.token = token.token;
249
+ const resume = !(token.config.model && this.model != token.config.model);
250
+ if (!resume) this.model = token.config.model;
251
+ this.restartSession(resume).catch((err) => this.onError(err instanceof Error ? err : new Error(String(err))));
252
+ }
253
+ async updateSession(config) {
254
+ if (config.model && !this.setupComplete) this.model = config.model;
255
+ if (config.instructions) this.systemInstructions = config.instructions;
256
+ if (config.tools) this.functionDeclarations = config.tools.map(toolConfigToDeclaration);
257
+ if (config.maxOutputTokens) this.maxOutputTokens = typeof config.maxOutputTokens === "number" ? config.maxOutputTokens : void 0;
258
+ if (config.temperature) this.temperature = config.temperature;
259
+ if (config.voice) this.voiceName = config.voice;
260
+ const providerOptions = config.providerOptions;
261
+ if (providerOptions?.googleGrounding) this.googleGrounding = providerOptions.googleGrounding;
262
+ if (providerOptions?.proactiveAudio) this.proactiveAudio = providerOptions.proactiveAudio;
263
+ if (providerOptions?.enableAffectiveDialog) this.enableAffectiveDialog = providerOptions.enableAffectiveDialog;
264
+ if (providerOptions?.contextWindowCompression) this.contextWindowCompression = providerOptions.contextWindowCompression;
265
+ if (providerOptions?.thinkingConfig) this.thinkingConfig = providerOptions.thinkingConfig;
266
+ if (providerOptions?.languageCode) this.speechLanguageCode = providerOptions.languageCode;
267
+ const includeTranscription = config.outputModalities?.includes("text") || false;
268
+ this.inputAudioTranscription = includeTranscription;
269
+ this.outputAudioTranscription = includeTranscription;
270
+ if (!this.setupComplete) this.sendInitialSetupMessage();
271
+ else return this.restartSession(true);
272
+ }
273
+ /**
274
+ * Message transmission & receiving
275
+ */
276
+ sendMessage(message) {
277
+ if (this.webSocket?.readyState === WebSocket.OPEN) this.webSocket.send(JSON.stringify(message));
278
+ else this.onError(/* @__PURE__ */ new Error("Cannot send message: Gemini realtime socket is not open"));
279
+ }
280
+ async onReceiveMessage(messageEvent) {
281
+ let jsonData;
282
+ if (messageEvent.data instanceof Blob) jsonData = await messageEvent.data.text();
283
+ else if (messageEvent.data instanceof ArrayBuffer) jsonData = new TextDecoder().decode(messageEvent.data);
284
+ else jsonData = messageEvent.data;
285
+ try {
286
+ const responses = parseResponseMessages(JSON.parse(jsonData));
287
+ for (const response of responses) {
288
+ if (response.type === "session_resumption_update" && response.data.resumable) this.lastResumptionUpdate = response.data;
289
+ if (response.type === "setup_complete") this.setupComplete = true;
290
+ this.onReceiveResponse(response);
291
+ }
292
+ } catch (err) {
293
+ this.onError(err instanceof Error ? err : new Error(String(err)));
294
+ }
295
+ }
296
+ sendRealtimeInputMessage(data, mimeType) {
297
+ const blob = {
298
+ mimeType,
299
+ data
300
+ };
301
+ if (mimeType.startsWith("audio/")) this.sendMessage({ realtimeInput: { audio: blob } });
302
+ else if (mimeType.startsWith("image/") || mimeType.startsWith("video/")) this.sendMessage({ realtimeInput: { video: blob } });
303
+ }
304
+ sendAudioMessage(base64PCM) {
305
+ this.sendRealtimeInputMessage(base64PCM, "audio/pcm");
306
+ }
307
+ sendImageMessage(base64, mimeType = "image/jpeg") {
308
+ this.sendRealtimeInputMessage(base64, mimeType);
309
+ }
310
+ sendTextMessage(text) {
311
+ const message = { realtimeInput: { text } };
312
+ this.sendMessage(message);
313
+ }
314
+ sendToolResponse(functionResponses) {
315
+ const message = { toolResponse: { functionResponses } };
316
+ this.sendMessage(message);
317
+ }
388
318
  };
389
- //# sourceMappingURL=client.js.map
319
+ //#endregion
320
+ export { GeminiLiveClient, parseResponseMessages };
321
+
322
+ //# sourceMappingURL=client.js.map