@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.
- package/README.md +15 -1
- package/dist/esm/adapters/audio.js +113 -61
- package/dist/esm/adapters/audio.js.map +1 -1
- package/dist/esm/adapters/image.js +224 -225
- package/dist/esm/adapters/image.js.map +1 -1
- package/dist/esm/adapters/summarize.js +33 -12
- package/dist/esm/adapters/summarize.js.map +1 -1
- package/dist/esm/adapters/text.js +574 -650
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/adapters/tts.js +197 -178
- package/dist/esm/adapters/tts.js.map +1 -1
- package/dist/esm/adapters/video.js +456 -409
- package/dist/esm/adapters/video.js.map +1 -1
- package/dist/esm/experimental/index.js +1 -6
- package/dist/esm/experimental/text-interactions/adapter.js +877 -1014
- package/dist/esm/experimental/text-interactions/adapter.js.map +1 -1
- package/dist/esm/image/image-provider-options.js +74 -54
- package/dist/esm/image/image-provider-options.js.map +1 -1
- package/dist/esm/index.js +4 -36
- package/dist/esm/model-meta.d.ts +53 -5
- package/dist/esm/model-meta.js +733 -129
- package/dist/esm/model-meta.js.map +1 -1
- package/dist/esm/realtime/adapter.js +242 -228
- package/dist/esm/realtime/adapter.js.map +1 -1
- package/dist/esm/realtime/client.js +314 -381
- package/dist/esm/realtime/client.js.map +1 -1
- package/dist/esm/realtime/index.js +3 -0
- package/dist/esm/realtime/token.js +50 -36
- package/dist/esm/realtime/token.js.map +1 -1
- package/dist/esm/realtime/utils.js +202 -245
- package/dist/esm/realtime/utils.js.map +1 -1
- package/dist/esm/tools/code-execution-tool.js +11 -13
- package/dist/esm/tools/code-execution-tool.js.map +1 -1
- package/dist/esm/tools/computer-use-tool.js +18 -28
- package/dist/esm/tools/computer-use-tool.js.map +1 -1
- package/dist/esm/tools/file-search-tool.js +11 -14
- package/dist/esm/tools/file-search-tool.js.map +1 -1
- package/dist/esm/tools/function-declaration-tool.js +11 -25
- package/dist/esm/tools/function-declaration-tool.js.map +1 -1
- package/dist/esm/tools/google-maps-tool.js +11 -14
- package/dist/esm/tools/google-maps-tool.js.map +1 -1
- package/dist/esm/tools/google-search-retriveal-tool.js +11 -14
- package/dist/esm/tools/google-search-retriveal-tool.js.map +1 -1
- package/dist/esm/tools/google-search-tool.js +11 -14
- package/dist/esm/tools/google-search-tool.js.map +1 -1
- package/dist/esm/tools/index.js +2 -13
- package/dist/esm/tools/tool-converter.js +64 -57
- package/dist/esm/tools/tool-converter.js.map +1 -1
- package/dist/esm/tools/url-context-tool.js +11 -13
- package/dist/esm/tools/url-context-tool.js.map +1 -1
- package/dist/esm/usage.js +81 -87
- package/dist/esm/usage.js.map +1 -1
- package/dist/esm/utils/client.js +31 -24
- package/dist/esm/utils/client.js.map +1 -1
- package/dist/esm/utils/index.js +2 -0
- package/dist/esm/video/video-provider-options.js +73 -20
- package/dist/esm/video/video-provider-options.js.map +1 -1
- package/package.json +7 -7
- package/src/model-meta.ts +120 -10
- package/dist/esm/experimental/index.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/tools/index.js.map +0 -1
|
@@ -1,1052 +1,915 @@
|
|
|
1
|
+
import { createGeminiClient, generateId, getGeminiApiKeyFromEnv } from "../../utils/client.js";
|
|
1
2
|
import { EventType } from "@tanstack/ai";
|
|
2
3
|
import { BaseTextAdapter } from "@tanstack/ai/adapters";
|
|
3
4
|
import { parse } from "partial-json";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
return { data: parsed, rawText };
|
|
168
|
-
} catch (error) {
|
|
169
|
-
logger.errors("gemini-text-interactions.structuredOutput fatal", {
|
|
170
|
-
error,
|
|
171
|
-
source: "gemini-text-interactions.structuredOutput"
|
|
172
|
-
});
|
|
173
|
-
throw new Error(
|
|
174
|
-
error instanceof Error ? error.message : "An unknown error occurred during structured output generation.",
|
|
175
|
-
{ cause: error }
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
5
|
+
//#region src/experimental/text-interactions/adapter.ts
|
|
6
|
+
/**
|
|
7
|
+
* Tree-shakeable adapter for Gemini's stateful Interactions API. Routes
|
|
8
|
+
* through `client.interactions.create` and surfaces the server-assigned
|
|
9
|
+
* `interactionId` via an AG-UI `CUSTOM` event with
|
|
10
|
+
* `name: 'gemini.interactionId'` emitted just before `RUN_FINISHED`; pass
|
|
11
|
+
* that id back on the next turn via `modelOptions.previous_interaction_id`
|
|
12
|
+
* to continue the conversation without resending history.
|
|
13
|
+
*
|
|
14
|
+
* The Interactions API does NOT support stateless multi-turn replay —
|
|
15
|
+
* passing more than one message in `messages` without a
|
|
16
|
+
* `previous_interaction_id` throws. For a chat UI that maintains local
|
|
17
|
+
* history (e.g. `useChat`), see the "Wiring with `useChat`" section of
|
|
18
|
+
* `docs/adapters/gemini.md` for the canonical client/server pattern.
|
|
19
|
+
*
|
|
20
|
+
* Supports user-defined function tools and the built-in tools
|
|
21
|
+
* `google_search`, `code_execution`, `url_context`, `file_search`, and
|
|
22
|
+
* `computer_use`. Built-in tool *activity* for the four search/exec
|
|
23
|
+
* variants is surfaced via `CUSTOM` events
|
|
24
|
+
* (`gemini.googleSearchCall` / `gemini.googleSearchResult` and the
|
|
25
|
+
* corresponding per-tool variants) carrying the raw Interactions delta;
|
|
26
|
+
* see {@link GeminiInteractionsCustomEvent}. `computer_use` is accepted
|
|
27
|
+
* in the request but the Interactions API does not currently stream
|
|
28
|
+
* per-delta CUSTOM events for it. `google_search_retrieval`,
|
|
29
|
+
* `google_maps`, and `mcp_server` are not supported on this adapter.
|
|
30
|
+
*
|
|
31
|
+
* @experimental Interactions API is in Beta per Google; shapes may change.
|
|
32
|
+
* @see https://ai.google.dev/gemini-api/docs/interactions
|
|
33
|
+
*/
|
|
34
|
+
var GeminiTextInteractionsAdapter = class extends BaseTextAdapter {
|
|
35
|
+
kind = "text";
|
|
36
|
+
name = "gemini-text-interactions";
|
|
37
|
+
client;
|
|
38
|
+
interactionIdByThread = /* @__PURE__ */ new Map();
|
|
39
|
+
constructor(config, model) {
|
|
40
|
+
super({}, model);
|
|
41
|
+
this.client = createGeminiClient(config);
|
|
42
|
+
}
|
|
43
|
+
async *chatStream(options) {
|
|
44
|
+
const runId = options.runId ?? generateId(this.name);
|
|
45
|
+
const threadId = options.threadId ?? generateId(this.name);
|
|
46
|
+
const timestamp = Date.now();
|
|
47
|
+
const { logger } = options;
|
|
48
|
+
if (!options.modelOptions?.previous_interaction_id && options.messages.length === 1 && options.messages[0]?.role === "user") this.interactionIdByThread.delete(threadId);
|
|
49
|
+
const effectivePreviousInteractionId = options.modelOptions?.previous_interaction_id ?? this.interactionIdByThread.get(threadId);
|
|
50
|
+
let sawTerminalEvent = false;
|
|
51
|
+
let completedTryBlock = false;
|
|
52
|
+
try {
|
|
53
|
+
const request = buildInteractionsRequest({
|
|
54
|
+
...options,
|
|
55
|
+
modelOptions: {
|
|
56
|
+
...options.modelOptions,
|
|
57
|
+
previous_interaction_id: effectivePreviousInteractionId
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
logger.request(`activity=chat provider=gemini-text-interactions model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`, {
|
|
61
|
+
provider: "gemini-text-interactions",
|
|
62
|
+
model: this.model,
|
|
63
|
+
request
|
|
64
|
+
});
|
|
65
|
+
const stream = await this.client.interactions.create({
|
|
66
|
+
...request,
|
|
67
|
+
stream: true
|
|
68
|
+
}, { signal: options.abortController?.signal });
|
|
69
|
+
for await (const chunk of translateInteractionEvents(stream, options.model, runId, threadId, options.parentRunId, timestamp, this.name, logger)) {
|
|
70
|
+
if (chunk.type === EventType.CUSTOM && chunk.name === "gemini.interactionId") {
|
|
71
|
+
const value = chunk.value;
|
|
72
|
+
this.interactionIdByThread.set(threadId, value.interactionId);
|
|
73
|
+
}
|
|
74
|
+
if (chunk.type === EventType.RUN_FINISHED) sawTerminalEvent = true;
|
|
75
|
+
else if (chunk.type === EventType.RUN_ERROR) {
|
|
76
|
+
sawTerminalEvent = true;
|
|
77
|
+
this.interactionIdByThread.delete(threadId);
|
|
78
|
+
}
|
|
79
|
+
yield chunk;
|
|
80
|
+
}
|
|
81
|
+
if (!sawTerminalEvent) {
|
|
82
|
+
this.interactionIdByThread.delete(threadId);
|
|
83
|
+
const message = "Gemini Interactions stream ended without a terminal event (no interaction.complete or error)";
|
|
84
|
+
logger.errors("gemini-text-interactions.chatStream truncated", {
|
|
85
|
+
source: "gemini-text-interactions.chatStream",
|
|
86
|
+
runId,
|
|
87
|
+
threadId
|
|
88
|
+
});
|
|
89
|
+
yield {
|
|
90
|
+
type: EventType.RUN_ERROR,
|
|
91
|
+
runId,
|
|
92
|
+
model: options.model,
|
|
93
|
+
timestamp,
|
|
94
|
+
message,
|
|
95
|
+
error: { message }
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
completedTryBlock = true;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
this.interactionIdByThread.delete(threadId);
|
|
101
|
+
const message = error instanceof Error ? error.message : "An unknown error occurred during the interactions stream.";
|
|
102
|
+
logger.errors("gemini-text-interactions.chatStream fatal", {
|
|
103
|
+
error,
|
|
104
|
+
source: "gemini-text-interactions.chatStream"
|
|
105
|
+
});
|
|
106
|
+
yield {
|
|
107
|
+
type: EventType.RUN_ERROR,
|
|
108
|
+
runId,
|
|
109
|
+
model: options.model,
|
|
110
|
+
timestamp,
|
|
111
|
+
message,
|
|
112
|
+
error: { message }
|
|
113
|
+
};
|
|
114
|
+
} finally {
|
|
115
|
+
if (!completedTryBlock) this.interactionIdByThread.delete(threadId);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async structuredOutput(options) {
|
|
119
|
+
const { chatOptions, outputSchema } = options;
|
|
120
|
+
const { logger } = chatOptions;
|
|
121
|
+
const threadId = chatOptions.threadId;
|
|
122
|
+
const effectivePreviousInteractionId = chatOptions.modelOptions?.previous_interaction_id ?? (threadId ? this.interactionIdByThread.get(threadId) : void 0);
|
|
123
|
+
const request = {
|
|
124
|
+
...buildInteractionsRequest({
|
|
125
|
+
...chatOptions,
|
|
126
|
+
modelOptions: {
|
|
127
|
+
...chatOptions.modelOptions,
|
|
128
|
+
previous_interaction_id: effectivePreviousInteractionId
|
|
129
|
+
}
|
|
130
|
+
}),
|
|
131
|
+
response_format: {
|
|
132
|
+
type: "text",
|
|
133
|
+
mime_type: "application/json",
|
|
134
|
+
schema: outputSchema
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
try {
|
|
138
|
+
logger.request(`activity=chat provider=gemini-text-interactions model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`, {
|
|
139
|
+
provider: "gemini-text-interactions",
|
|
140
|
+
model: this.model,
|
|
141
|
+
request
|
|
142
|
+
});
|
|
143
|
+
const result = await this.client.interactions.create(request, { signal: chatOptions.abortController?.signal });
|
|
144
|
+
const rawText = extractTextFromInteraction(result);
|
|
145
|
+
if (!rawText) throw new Error(`Gemini Interactions returned no text output for structured-output request (status: ${result.status}). The model may have produced only tool calls or non-text content.`);
|
|
146
|
+
let parsed;
|
|
147
|
+
try {
|
|
148
|
+
parsed = JSON.parse(rawText);
|
|
149
|
+
} catch {
|
|
150
|
+
throw new Error(`Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? "..." : ""}`);
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
data: parsed,
|
|
154
|
+
rawText
|
|
155
|
+
};
|
|
156
|
+
} catch (error) {
|
|
157
|
+
logger.errors("gemini-text-interactions.structuredOutput fatal", {
|
|
158
|
+
error,
|
|
159
|
+
source: "gemini-text-interactions.structuredOutput"
|
|
160
|
+
});
|
|
161
|
+
throw new Error(error instanceof Error ? error.message : "An unknown error occurred during structured output generation.", { cause: error });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
/** @experimental Interactions API is in Beta. */
|
|
180
166
|
function createGeminiTextInteractions(model, apiKey, config) {
|
|
181
|
-
|
|
167
|
+
return new GeminiTextInteractionsAdapter({
|
|
168
|
+
apiKey,
|
|
169
|
+
...config
|
|
170
|
+
}, model);
|
|
182
171
|
}
|
|
172
|
+
/** @experimental Interactions API is in Beta. */
|
|
183
173
|
function geminiTextInteractions(model, config) {
|
|
184
|
-
|
|
185
|
-
return createGeminiTextInteractions(model, apiKey, config);
|
|
174
|
+
return createGeminiTextInteractions(model, getGeminiApiKeyFromEnv(), config);
|
|
186
175
|
}
|
|
187
176
|
function buildInteractionsRequest(options) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
store: modelOpts?.store,
|
|
206
|
-
background: modelOpts?.background,
|
|
207
|
-
response_modalities: modelOpts?.response_modalities,
|
|
208
|
-
response_format: modelOpts?.response_format
|
|
209
|
-
};
|
|
177
|
+
const modelOpts = options.modelOptions;
|
|
178
|
+
const systemInstruction = modelOpts?.system_instruction ?? options.systemPrompts?.join("\n");
|
|
179
|
+
const generationConfig = { ...modelOpts?.generation_config };
|
|
180
|
+
const hasGenerationConfig = Object.keys(generationConfig).length > 0;
|
|
181
|
+
const input = convertMessagesToInteractionsInput(options.messages, modelOpts?.previous_interaction_id !== void 0);
|
|
182
|
+
return {
|
|
183
|
+
model: options.model,
|
|
184
|
+
input,
|
|
185
|
+
previous_interaction_id: modelOpts?.previous_interaction_id,
|
|
186
|
+
system_instruction: systemInstruction,
|
|
187
|
+
tools: convertToolsToInteractionsFormat(options.tools),
|
|
188
|
+
generation_config: hasGenerationConfig ? generationConfig : void 0,
|
|
189
|
+
store: modelOpts?.store,
|
|
190
|
+
background: modelOpts?.background,
|
|
191
|
+
response_modalities: modelOpts?.response_modalities,
|
|
192
|
+
response_format: modelOpts?.response_format
|
|
193
|
+
};
|
|
210
194
|
}
|
|
211
195
|
function convertMessagesToInteractionsInput(messages, hasPreviousInteraction) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
return [{ type: "user_input", content }];
|
|
248
|
-
}
|
|
249
|
-
const steps = [];
|
|
250
|
-
for (const msg of source) {
|
|
251
|
-
if (msg.role === "tool" && msg.toolCallId) {
|
|
252
|
-
const result = serializeToolResultContent(msg.content);
|
|
253
|
-
steps.push({
|
|
254
|
-
type: "function_result",
|
|
255
|
-
call_id: msg.toolCallId,
|
|
256
|
-
name: toolCallIdToName.get(msg.toolCallId),
|
|
257
|
-
result
|
|
258
|
-
});
|
|
259
|
-
} else if (msg.role === "user") {
|
|
260
|
-
const content = messageToContentBlocks(msg);
|
|
261
|
-
if (content.length > 0) {
|
|
262
|
-
steps.push({ type: "user_input", content });
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
if (steps.length === 0) {
|
|
267
|
-
throw new Error(
|
|
268
|
-
"Gemini Interactions adapter: messages after the last assistant turn produced no steps to send."
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
return steps;
|
|
196
|
+
const toolCallIdToName = /* @__PURE__ */ new Map();
|
|
197
|
+
for (const msg of messages) if (msg.role === "assistant" && msg.toolCalls) for (const tc of msg.toolCalls) toolCallIdToName.set(tc.id, tc.function.name);
|
|
198
|
+
const source = hasPreviousInteraction ? messagesAfterLastAssistant(messages) : messages;
|
|
199
|
+
if (hasPreviousInteraction && source.length === 0) throw new Error("Gemini Interactions adapter: modelOptions.previous_interaction_id was provided but no new messages were found after the last assistant turn. Append at least one user or tool message before chaining.");
|
|
200
|
+
if (!hasPreviousInteraction) {
|
|
201
|
+
const [only, ...rest] = source;
|
|
202
|
+
if (!only) throw new Error("Gemini Interactions adapter: no messages to send.");
|
|
203
|
+
if (rest.length > 0) throw new Error("Gemini Interactions adapter: cannot send prior conversation history on a fresh interaction. Either set modelOptions.previous_interaction_id to chain prior turns server-side, or trim the message list to a single new user turn. See docs/adapters/gemini.md (\"Wiring with useChat\") for the canonical client/server pattern.");
|
|
204
|
+
if (only.role !== "user") throw new Error(`Gemini Interactions adapter: the first message of a fresh interaction must be a user turn (got role="${only.role}"). Set modelOptions.previous_interaction_id to continue an existing interaction.`);
|
|
205
|
+
const content = messageToContentBlocks(only);
|
|
206
|
+
if (content.length === 0) throw new Error("Gemini Interactions adapter: the user message produced no content blocks to send.");
|
|
207
|
+
return [{
|
|
208
|
+
type: "user_input",
|
|
209
|
+
content
|
|
210
|
+
}];
|
|
211
|
+
}
|
|
212
|
+
const steps = [];
|
|
213
|
+
for (const msg of source) if (msg.role === "tool" && msg.toolCallId) {
|
|
214
|
+
const result = serializeToolResultContent(msg.content);
|
|
215
|
+
steps.push({
|
|
216
|
+
type: "function_result",
|
|
217
|
+
call_id: msg.toolCallId,
|
|
218
|
+
name: toolCallIdToName.get(msg.toolCallId),
|
|
219
|
+
result
|
|
220
|
+
});
|
|
221
|
+
} else if (msg.role === "user") {
|
|
222
|
+
const content = messageToContentBlocks(msg);
|
|
223
|
+
if (content.length > 0) steps.push({
|
|
224
|
+
type: "user_input",
|
|
225
|
+
content
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (steps.length === 0) throw new Error("Gemini Interactions adapter: messages after the last assistant turn produced no steps to send.");
|
|
229
|
+
return steps;
|
|
272
230
|
}
|
|
273
231
|
function serializeToolResultContent(content) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
"Gemini Interactions adapter: tool message has no content. The Interactions API requires a string `result` on function_result steps — return a string from your tool implementation (encode JSON/multimodal output yourself)."
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
throw new Error(
|
|
281
|
-
"Gemini Interactions adapter: tool message content must be a string (got an array of content parts). The Interactions API requires a string `result` on function_result steps — stringify multimodal tool output before returning it from your tool."
|
|
282
|
-
);
|
|
232
|
+
if (typeof content === "string") return content;
|
|
233
|
+
if (content === null || content === void 0) throw new Error("Gemini Interactions adapter: tool message has no content. The Interactions API requires a string `result` on function_result steps — return a string from your tool implementation (encode JSON/multimodal output yourself).");
|
|
234
|
+
throw new Error("Gemini Interactions adapter: tool message content must be a string (got an array of content parts). The Interactions API requires a string `result` on function_result steps — stringify multimodal tool output before returning it from your tool.");
|
|
283
235
|
}
|
|
284
236
|
function messageToContentBlocks(msg) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
return blocks;
|
|
237
|
+
const blocks = [];
|
|
238
|
+
if (Array.isArray(msg.content)) for (const part of msg.content) blocks.push(contentPartToBlock(part));
|
|
239
|
+
else if (typeof msg.content === "string" && msg.content && msg.role !== "tool") blocks.push({
|
|
240
|
+
type: "text",
|
|
241
|
+
text: msg.content
|
|
242
|
+
});
|
|
243
|
+
return blocks;
|
|
294
244
|
}
|
|
295
245
|
function messagesAfterLastAssistant(messages) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
return messages.slice(i + 1);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return messages;
|
|
246
|
+
for (let i = messages.length - 1; i >= 0; i--) if (messages[i]?.role === "assistant") return messages.slice(i + 1);
|
|
247
|
+
return messages;
|
|
302
248
|
}
|
|
303
249
|
function parsePartialToolArguments(raw) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
250
|
+
if (!raw) return void 0;
|
|
251
|
+
try {
|
|
252
|
+
const parsed = parse(raw);
|
|
253
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : void 0;
|
|
254
|
+
} catch {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
311
257
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
];
|
|
319
|
-
const AUDIO_MIME_TYPES = [
|
|
320
|
-
"audio/wav",
|
|
321
|
-
"audio/mp3",
|
|
322
|
-
"audio/aiff",
|
|
323
|
-
"audio/aac",
|
|
324
|
-
"audio/ogg",
|
|
325
|
-
"audio/flac"
|
|
258
|
+
var IMAGE_MIME_TYPES = [
|
|
259
|
+
"image/png",
|
|
260
|
+
"image/jpeg",
|
|
261
|
+
"image/webp",
|
|
262
|
+
"image/heic",
|
|
263
|
+
"image/heif"
|
|
326
264
|
];
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
"video/webm",
|
|
335
|
-
"video/wmv",
|
|
336
|
-
"video/3gpp"
|
|
265
|
+
var AUDIO_MIME_TYPES = [
|
|
266
|
+
"audio/wav",
|
|
267
|
+
"audio/mp3",
|
|
268
|
+
"audio/aiff",
|
|
269
|
+
"audio/aac",
|
|
270
|
+
"audio/ogg",
|
|
271
|
+
"audio/flac"
|
|
337
272
|
];
|
|
338
|
-
|
|
339
|
-
|
|
273
|
+
var VIDEO_MIME_TYPES = [
|
|
274
|
+
"video/mp4",
|
|
275
|
+
"video/mpeg",
|
|
276
|
+
"video/mpg",
|
|
277
|
+
"video/mov",
|
|
278
|
+
"video/avi",
|
|
279
|
+
"video/x-flv",
|
|
280
|
+
"video/webm",
|
|
281
|
+
"video/wmv",
|
|
282
|
+
"video/3gpp"
|
|
340
283
|
];
|
|
284
|
+
var DOCUMENT_MIME_TYPES = ["application/pdf"];
|
|
341
285
|
function validateMime(allowed, value, kind) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
throw new Error(
|
|
347
|
-
`Unsupported ${kind} mime type "${value}" for the Gemini Interactions API. Allowed: ${allowed.join(", ")}.`
|
|
348
|
-
);
|
|
286
|
+
if (value === void 0) return void 0;
|
|
287
|
+
if (allowed.includes(value)) return value;
|
|
288
|
+
throw new Error(`Unsupported ${kind} mime type "${value}" for the Gemini Interactions API. Allowed: ${allowed.join(", ")}.`);
|
|
349
289
|
}
|
|
350
290
|
function contentPartToBlock(part) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
291
|
+
if (part.type === "text") return {
|
|
292
|
+
type: "text",
|
|
293
|
+
text: part.content
|
|
294
|
+
};
|
|
295
|
+
const isData = part.source.type === "data";
|
|
296
|
+
switch (part.type) {
|
|
297
|
+
case "image": {
|
|
298
|
+
const mime_type = validateMime(IMAGE_MIME_TYPES, part.source.mimeType, "image");
|
|
299
|
+
return isData ? {
|
|
300
|
+
type: "image",
|
|
301
|
+
data: part.source.value,
|
|
302
|
+
mime_type
|
|
303
|
+
} : {
|
|
304
|
+
type: "image",
|
|
305
|
+
uri: part.source.value,
|
|
306
|
+
mime_type
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
case "audio": {
|
|
310
|
+
const mime_type = validateMime(AUDIO_MIME_TYPES, part.source.mimeType, "audio");
|
|
311
|
+
return isData ? {
|
|
312
|
+
type: "audio",
|
|
313
|
+
data: part.source.value,
|
|
314
|
+
mime_type
|
|
315
|
+
} : {
|
|
316
|
+
type: "audio",
|
|
317
|
+
uri: part.source.value,
|
|
318
|
+
mime_type
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
case "video": {
|
|
322
|
+
const mime_type = validateMime(VIDEO_MIME_TYPES, part.source.mimeType, "video");
|
|
323
|
+
return isData ? {
|
|
324
|
+
type: "video",
|
|
325
|
+
data: part.source.value,
|
|
326
|
+
mime_type
|
|
327
|
+
} : {
|
|
328
|
+
type: "video",
|
|
329
|
+
uri: part.source.value,
|
|
330
|
+
mime_type
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
case "document": {
|
|
334
|
+
const mime_type = validateMime(DOCUMENT_MIME_TYPES, part.source.mimeType, "document");
|
|
335
|
+
return isData ? {
|
|
336
|
+
type: "document",
|
|
337
|
+
data: part.source.value,
|
|
338
|
+
mime_type
|
|
339
|
+
} : {
|
|
340
|
+
type: "document",
|
|
341
|
+
uri: part.source.value,
|
|
342
|
+
mime_type
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
389
346
|
}
|
|
390
347
|
function convertToolsToInteractionsFormat(tools) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
);
|
|
445
|
-
case "mcp_server":
|
|
446
|
-
throw new Error(
|
|
447
|
-
"`mcp_server` is not yet supported on the `geminiTextInteractions()` adapter."
|
|
448
|
-
);
|
|
449
|
-
default: {
|
|
450
|
-
if (!tool.description) {
|
|
451
|
-
throw new Error(
|
|
452
|
-
`Tool ${tool.name} requires a description for the Gemini Interactions adapter`
|
|
453
|
-
);
|
|
454
|
-
}
|
|
455
|
-
result.push({
|
|
456
|
-
type: "function",
|
|
457
|
-
name: tool.name,
|
|
458
|
-
description: tool.description,
|
|
459
|
-
parameters: sanitizeToolParameters(
|
|
460
|
-
tool.inputSchema ?? { type: "object", properties: {} }
|
|
461
|
-
)
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
return result;
|
|
348
|
+
if (!tools || tools.length === 0) return void 0;
|
|
349
|
+
const result = [];
|
|
350
|
+
for (const tool of tools) switch (tool.name) {
|
|
351
|
+
case "google_search": {
|
|
352
|
+
const metadata = tool.metadata ?? {};
|
|
353
|
+
result.push({
|
|
354
|
+
type: "google_search",
|
|
355
|
+
...metadata.search_types ? { search_types: metadata.search_types } : {}
|
|
356
|
+
});
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
case "code_execution":
|
|
360
|
+
result.push({ type: "code_execution" });
|
|
361
|
+
break;
|
|
362
|
+
case "url_context":
|
|
363
|
+
result.push({ type: "url_context" });
|
|
364
|
+
break;
|
|
365
|
+
case "file_search": {
|
|
366
|
+
const metadata = tool.metadata ?? {};
|
|
367
|
+
result.push({
|
|
368
|
+
type: "file_search",
|
|
369
|
+
...metadata.fileSearchStoreNames ? { file_search_store_names: metadata.fileSearchStoreNames } : {},
|
|
370
|
+
...metadata.topK !== void 0 ? { top_k: metadata.topK } : {},
|
|
371
|
+
...metadata.metadataFilter !== void 0 ? { metadata_filter: metadata.metadataFilter } : {}
|
|
372
|
+
});
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
case "computer_use": {
|
|
376
|
+
const metadata = tool.metadata ?? {};
|
|
377
|
+
if (metadata.environment && metadata.environment !== "browser") throw new Error(`computer_use environment "${metadata.environment}" is not supported on the Gemini Interactions API. Only "browser" is accepted.`);
|
|
378
|
+
result.push({
|
|
379
|
+
type: "computer_use",
|
|
380
|
+
...metadata.environment ? { environment: metadata.environment } : {},
|
|
381
|
+
...metadata.excludedPredefinedFunctions ? { excludedPredefinedFunctions: metadata.excludedPredefinedFunctions } : {}
|
|
382
|
+
});
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
case "google_search_retrieval": throw new Error("`google_search_retrieval` is not supported on the Gemini Interactions API. Use `googleSearchTool()` (`google_search`) with `geminiTextInteractions()`, or call `geminiText()` for the legacy retrieval tool.");
|
|
386
|
+
case "google_maps": throw new Error("`google_maps` is not yet supported on the Gemini Interactions API. Use `geminiText()` for Google Maps grounding.");
|
|
387
|
+
case "mcp_server": throw new Error("`mcp_server` is not yet supported on the `geminiTextInteractions()` adapter.");
|
|
388
|
+
default:
|
|
389
|
+
if (!tool.description) throw new Error(`Tool ${tool.name} requires a description for the Gemini Interactions adapter`);
|
|
390
|
+
result.push({
|
|
391
|
+
type: "function",
|
|
392
|
+
name: tool.name,
|
|
393
|
+
description: tool.description,
|
|
394
|
+
parameters: sanitizeToolParameters(tool.inputSchema ?? {
|
|
395
|
+
type: "object",
|
|
396
|
+
properties: {}
|
|
397
|
+
})
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
return result;
|
|
467
401
|
}
|
|
468
402
|
function statusToFinishReason(status, sawFunctionCall) {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
403
|
+
if (status === "requires_action") return "tool_calls";
|
|
404
|
+
if (status === "incomplete") return "length";
|
|
405
|
+
if (sawFunctionCall) return "tool_calls";
|
|
406
|
+
return "stop";
|
|
473
407
|
}
|
|
474
408
|
function statusIsError(status) {
|
|
475
|
-
|
|
409
|
+
return status === "failed" || status === "cancelled";
|
|
476
410
|
}
|
|
477
411
|
async function* translateInteractionEvents(stream, model, runId, threadId, parentRunId, timestamp, adapterName, logger) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
type: EventType.RUN_ERROR,
|
|
959
|
-
runId,
|
|
960
|
-
model,
|
|
961
|
-
timestamp,
|
|
962
|
-
message,
|
|
963
|
-
code: status,
|
|
964
|
-
error: { message, code: status }
|
|
965
|
-
};
|
|
966
|
-
return;
|
|
967
|
-
}
|
|
968
|
-
const usage = event.interaction.usage;
|
|
969
|
-
const finishReason = statusToFinishReason(status, sawFunctionCall);
|
|
970
|
-
if (interactionId) {
|
|
971
|
-
yield {
|
|
972
|
-
type: EventType.CUSTOM,
|
|
973
|
-
name: "gemini.interactionId",
|
|
974
|
-
value: { interactionId },
|
|
975
|
-
model,
|
|
976
|
-
timestamp
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
yield {
|
|
980
|
-
type: EventType.RUN_FINISHED,
|
|
981
|
-
runId,
|
|
982
|
-
threadId,
|
|
983
|
-
model,
|
|
984
|
-
timestamp,
|
|
985
|
-
finishReason,
|
|
986
|
-
usage: usage ? {
|
|
987
|
-
promptTokens: usage.total_input_tokens ?? 0,
|
|
988
|
-
completionTokens: usage.total_output_tokens ?? 0,
|
|
989
|
-
totalTokens: usage.total_tokens ?? 0
|
|
990
|
-
} : void 0
|
|
991
|
-
};
|
|
992
|
-
return;
|
|
993
|
-
}
|
|
994
|
-
case "error": {
|
|
995
|
-
yield* closeOpenState();
|
|
996
|
-
const rawMessage = event.error?.message;
|
|
997
|
-
const message = typeof rawMessage === "string" && rawMessage.length > 0 ? rawMessage : `Gemini Interactions error (no message): ${JSON.stringify(event.error ?? {})}`;
|
|
998
|
-
const rawCode = event.error?.code;
|
|
999
|
-
const code = typeof rawCode === "string" || typeof rawCode === "number" ? String(rawCode) : void 0;
|
|
1000
|
-
yield {
|
|
1001
|
-
type: EventType.RUN_ERROR,
|
|
1002
|
-
runId,
|
|
1003
|
-
model,
|
|
1004
|
-
timestamp,
|
|
1005
|
-
message,
|
|
1006
|
-
code,
|
|
1007
|
-
error: { message, code }
|
|
1008
|
-
};
|
|
1009
|
-
return;
|
|
1010
|
-
}
|
|
1011
|
-
default:
|
|
1012
|
-
logger.provider(`gemini-text-interactions unhandled event_type`, {
|
|
1013
|
-
event
|
|
1014
|
-
});
|
|
1015
|
-
break;
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
yield* closeOpenState();
|
|
412
|
+
const messageId = generateId(adapterName);
|
|
413
|
+
let hasEmittedRunStarted = false;
|
|
414
|
+
let hasEmittedTextMessageStart = false;
|
|
415
|
+
let textAccumulated = "";
|
|
416
|
+
let interactionId;
|
|
417
|
+
let sawFunctionCall = false;
|
|
418
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
419
|
+
let nextToolIndex = 0;
|
|
420
|
+
let thinkingStepId = null;
|
|
421
|
+
let thinkingAccumulated = "";
|
|
422
|
+
let reasoningMessageId = null;
|
|
423
|
+
let hasClosedReasoning = false;
|
|
424
|
+
const indexToToolCallId = /* @__PURE__ */ new Map();
|
|
425
|
+
const argStringByToolCallId = /* @__PURE__ */ new Map();
|
|
426
|
+
const closeReasoningIfNeeded = function* () {
|
|
427
|
+
if (reasoningMessageId && !hasClosedReasoning) {
|
|
428
|
+
hasClosedReasoning = true;
|
|
429
|
+
yield {
|
|
430
|
+
type: EventType.REASONING_MESSAGE_END,
|
|
431
|
+
messageId: reasoningMessageId,
|
|
432
|
+
model,
|
|
433
|
+
timestamp
|
|
434
|
+
};
|
|
435
|
+
yield {
|
|
436
|
+
type: EventType.REASONING_END,
|
|
437
|
+
messageId: reasoningMessageId,
|
|
438
|
+
model,
|
|
439
|
+
timestamp
|
|
440
|
+
};
|
|
441
|
+
thinkingStepId = null;
|
|
442
|
+
reasoningMessageId = null;
|
|
443
|
+
hasClosedReasoning = false;
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
const closeOpenState = function* () {
|
|
447
|
+
yield* closeReasoningIfNeeded();
|
|
448
|
+
for (const [toolCallId, state] of toolCalls) {
|
|
449
|
+
if (state.ended) continue;
|
|
450
|
+
state.ended = true;
|
|
451
|
+
yield {
|
|
452
|
+
type: EventType.TOOL_CALL_END,
|
|
453
|
+
toolCallId,
|
|
454
|
+
toolName: state.name,
|
|
455
|
+
model,
|
|
456
|
+
timestamp,
|
|
457
|
+
input: state.args
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
if (hasEmittedTextMessageStart) {
|
|
461
|
+
hasEmittedTextMessageStart = false;
|
|
462
|
+
yield {
|
|
463
|
+
type: EventType.TEXT_MESSAGE_END,
|
|
464
|
+
messageId,
|
|
465
|
+
model,
|
|
466
|
+
timestamp
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
const emitRunStartedIfNeeded = function* () {
|
|
471
|
+
if (!hasEmittedRunStarted) {
|
|
472
|
+
hasEmittedRunStarted = true;
|
|
473
|
+
yield {
|
|
474
|
+
type: EventType.RUN_STARTED,
|
|
475
|
+
runId,
|
|
476
|
+
threadId,
|
|
477
|
+
model,
|
|
478
|
+
timestamp,
|
|
479
|
+
parentRunId
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
for await (const event of stream) {
|
|
484
|
+
logger.provider(`provider=gemini-text-interactions`, { event });
|
|
485
|
+
switch (event.event_type) {
|
|
486
|
+
case "interaction.created":
|
|
487
|
+
interactionId = event.interaction.id;
|
|
488
|
+
yield* emitRunStartedIfNeeded();
|
|
489
|
+
break;
|
|
490
|
+
case "step.start": {
|
|
491
|
+
yield* emitRunStartedIfNeeded();
|
|
492
|
+
const step = event.step;
|
|
493
|
+
const index = event.index;
|
|
494
|
+
switch (step.type) {
|
|
495
|
+
case "function_call": {
|
|
496
|
+
yield* closeReasoningIfNeeded();
|
|
497
|
+
sawFunctionCall = true;
|
|
498
|
+
const toolCallId = step.id;
|
|
499
|
+
indexToToolCallId.set(index, toolCallId);
|
|
500
|
+
const initialArgs = step.arguments;
|
|
501
|
+
const state = {
|
|
502
|
+
name: step.name,
|
|
503
|
+
args: { ...initialArgs },
|
|
504
|
+
index: nextToolIndex++,
|
|
505
|
+
started: true,
|
|
506
|
+
ended: false
|
|
507
|
+
};
|
|
508
|
+
toolCalls.set(toolCallId, state);
|
|
509
|
+
argStringByToolCallId.set(toolCallId, Object.keys(initialArgs).length > 0 ? JSON.stringify(initialArgs) : "");
|
|
510
|
+
yield {
|
|
511
|
+
type: EventType.TOOL_CALL_START,
|
|
512
|
+
toolCallId,
|
|
513
|
+
toolCallName: state.name,
|
|
514
|
+
toolName: state.name,
|
|
515
|
+
parentMessageId: messageId,
|
|
516
|
+
model,
|
|
517
|
+
timestamp,
|
|
518
|
+
index: state.index
|
|
519
|
+
};
|
|
520
|
+
if (Object.keys(initialArgs).length > 0) {
|
|
521
|
+
const argsJson = JSON.stringify(initialArgs);
|
|
522
|
+
yield {
|
|
523
|
+
type: EventType.TOOL_CALL_ARGS,
|
|
524
|
+
toolCallId,
|
|
525
|
+
model,
|
|
526
|
+
timestamp,
|
|
527
|
+
delta: argsJson,
|
|
528
|
+
args: argsJson
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
case "thought":
|
|
534
|
+
if (thinkingStepId === null || reasoningMessageId === null) {
|
|
535
|
+
thinkingStepId = generateId(adapterName);
|
|
536
|
+
reasoningMessageId = generateId(adapterName);
|
|
537
|
+
yield {
|
|
538
|
+
type: EventType.REASONING_START,
|
|
539
|
+
messageId: reasoningMessageId,
|
|
540
|
+
model,
|
|
541
|
+
timestamp
|
|
542
|
+
};
|
|
543
|
+
yield {
|
|
544
|
+
type: EventType.REASONING_MESSAGE_START,
|
|
545
|
+
messageId: reasoningMessageId,
|
|
546
|
+
role: "reasoning",
|
|
547
|
+
model,
|
|
548
|
+
timestamp
|
|
549
|
+
};
|
|
550
|
+
yield {
|
|
551
|
+
type: EventType.STEP_STARTED,
|
|
552
|
+
stepName: thinkingStepId,
|
|
553
|
+
stepId: thinkingStepId,
|
|
554
|
+
model,
|
|
555
|
+
timestamp,
|
|
556
|
+
stepType: "thinking"
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
for (const part of step.summary ?? []) {
|
|
560
|
+
if (part.type !== "text" || !part.text) continue;
|
|
561
|
+
thinkingAccumulated += part.text;
|
|
562
|
+
yield {
|
|
563
|
+
type: EventType.REASONING_MESSAGE_CONTENT,
|
|
564
|
+
messageId: reasoningMessageId,
|
|
565
|
+
delta: part.text,
|
|
566
|
+
model,
|
|
567
|
+
timestamp
|
|
568
|
+
};
|
|
569
|
+
yield {
|
|
570
|
+
type: EventType.STEP_FINISHED,
|
|
571
|
+
stepName: thinkingStepId,
|
|
572
|
+
stepId: thinkingStepId,
|
|
573
|
+
model,
|
|
574
|
+
timestamp,
|
|
575
|
+
delta: part.text,
|
|
576
|
+
content: thinkingAccumulated
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
break;
|
|
580
|
+
case "model_output":
|
|
581
|
+
yield* closeReasoningIfNeeded();
|
|
582
|
+
for (const part of step.content ?? []) {
|
|
583
|
+
if (part.type !== "text" || !part.text) continue;
|
|
584
|
+
if (!hasEmittedTextMessageStart) {
|
|
585
|
+
hasEmittedTextMessageStart = true;
|
|
586
|
+
yield {
|
|
587
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
588
|
+
messageId,
|
|
589
|
+
model,
|
|
590
|
+
timestamp,
|
|
591
|
+
role: "assistant"
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
textAccumulated += part.text;
|
|
595
|
+
yield {
|
|
596
|
+
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
597
|
+
messageId,
|
|
598
|
+
model,
|
|
599
|
+
timestamp,
|
|
600
|
+
delta: part.text,
|
|
601
|
+
content: textAccumulated
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
break;
|
|
605
|
+
case "google_search_call":
|
|
606
|
+
yield* closeReasoningIfNeeded();
|
|
607
|
+
yield {
|
|
608
|
+
type: EventType.CUSTOM,
|
|
609
|
+
name: "gemini.googleSearchCall",
|
|
610
|
+
value: step,
|
|
611
|
+
model,
|
|
612
|
+
timestamp
|
|
613
|
+
};
|
|
614
|
+
break;
|
|
615
|
+
case "google_search_result":
|
|
616
|
+
yield* closeReasoningIfNeeded();
|
|
617
|
+
yield {
|
|
618
|
+
type: EventType.CUSTOM,
|
|
619
|
+
name: "gemini.googleSearchResult",
|
|
620
|
+
value: step,
|
|
621
|
+
model,
|
|
622
|
+
timestamp
|
|
623
|
+
};
|
|
624
|
+
break;
|
|
625
|
+
case "code_execution_call":
|
|
626
|
+
yield* closeReasoningIfNeeded();
|
|
627
|
+
yield {
|
|
628
|
+
type: EventType.CUSTOM,
|
|
629
|
+
name: "gemini.codeExecutionCall",
|
|
630
|
+
value: step,
|
|
631
|
+
model,
|
|
632
|
+
timestamp
|
|
633
|
+
};
|
|
634
|
+
break;
|
|
635
|
+
case "code_execution_result":
|
|
636
|
+
yield* closeReasoningIfNeeded();
|
|
637
|
+
yield {
|
|
638
|
+
type: EventType.CUSTOM,
|
|
639
|
+
name: "gemini.codeExecutionResult",
|
|
640
|
+
value: step,
|
|
641
|
+
model,
|
|
642
|
+
timestamp
|
|
643
|
+
};
|
|
644
|
+
break;
|
|
645
|
+
case "url_context_call":
|
|
646
|
+
yield* closeReasoningIfNeeded();
|
|
647
|
+
yield {
|
|
648
|
+
type: EventType.CUSTOM,
|
|
649
|
+
name: "gemini.urlContextCall",
|
|
650
|
+
value: step,
|
|
651
|
+
model,
|
|
652
|
+
timestamp
|
|
653
|
+
};
|
|
654
|
+
break;
|
|
655
|
+
case "url_context_result":
|
|
656
|
+
yield* closeReasoningIfNeeded();
|
|
657
|
+
yield {
|
|
658
|
+
type: EventType.CUSTOM,
|
|
659
|
+
name: "gemini.urlContextResult",
|
|
660
|
+
value: step,
|
|
661
|
+
model,
|
|
662
|
+
timestamp
|
|
663
|
+
};
|
|
664
|
+
break;
|
|
665
|
+
case "file_search_call":
|
|
666
|
+
yield* closeReasoningIfNeeded();
|
|
667
|
+
yield {
|
|
668
|
+
type: EventType.CUSTOM,
|
|
669
|
+
name: "gemini.fileSearchCall",
|
|
670
|
+
value: step,
|
|
671
|
+
model,
|
|
672
|
+
timestamp
|
|
673
|
+
};
|
|
674
|
+
break;
|
|
675
|
+
case "file_search_result":
|
|
676
|
+
yield* closeReasoningIfNeeded();
|
|
677
|
+
yield {
|
|
678
|
+
type: EventType.CUSTOM,
|
|
679
|
+
name: "gemini.fileSearchResult",
|
|
680
|
+
value: step,
|
|
681
|
+
model,
|
|
682
|
+
timestamp
|
|
683
|
+
};
|
|
684
|
+
break;
|
|
685
|
+
default:
|
|
686
|
+
logger.provider(`gemini-text-interactions unhandled step.start`, { step });
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
break;
|
|
690
|
+
}
|
|
691
|
+
case "step.delta": {
|
|
692
|
+
yield* emitRunStartedIfNeeded();
|
|
693
|
+
const delta = event.delta;
|
|
694
|
+
const index = event.index;
|
|
695
|
+
switch (delta.type) {
|
|
696
|
+
case "text":
|
|
697
|
+
yield* closeReasoningIfNeeded();
|
|
698
|
+
if (!hasEmittedTextMessageStart) {
|
|
699
|
+
hasEmittedTextMessageStart = true;
|
|
700
|
+
yield {
|
|
701
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
702
|
+
messageId,
|
|
703
|
+
model,
|
|
704
|
+
timestamp,
|
|
705
|
+
role: "assistant"
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
textAccumulated += delta.text;
|
|
709
|
+
yield {
|
|
710
|
+
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
711
|
+
messageId,
|
|
712
|
+
model,
|
|
713
|
+
timestamp,
|
|
714
|
+
delta: delta.text,
|
|
715
|
+
content: textAccumulated
|
|
716
|
+
};
|
|
717
|
+
break;
|
|
718
|
+
case "arguments_delta": {
|
|
719
|
+
const toolCallId = indexToToolCallId.get(index);
|
|
720
|
+
if (!toolCallId) {
|
|
721
|
+
logger.provider(`gemini-text-interactions arguments_delta for unknown step index`, {
|
|
722
|
+
index,
|
|
723
|
+
delta
|
|
724
|
+
});
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
const state = toolCalls.get(toolCallId);
|
|
728
|
+
if (!state) break;
|
|
729
|
+
const fragment = delta.arguments ?? "";
|
|
730
|
+
const buffer = (argStringByToolCallId.get(toolCallId) ?? "") + fragment;
|
|
731
|
+
argStringByToolCallId.set(toolCallId, buffer);
|
|
732
|
+
const parsed = parsePartialToolArguments(buffer);
|
|
733
|
+
if (parsed) state.args = parsed;
|
|
734
|
+
yield {
|
|
735
|
+
type: EventType.TOOL_CALL_ARGS,
|
|
736
|
+
toolCallId,
|
|
737
|
+
model,
|
|
738
|
+
timestamp,
|
|
739
|
+
delta: fragment,
|
|
740
|
+
args: buffer
|
|
741
|
+
};
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
744
|
+
case "thought_summary": {
|
|
745
|
+
const thoughtText = delta.content && "text" in delta.content ? delta.content.text : "";
|
|
746
|
+
if (!thoughtText) break;
|
|
747
|
+
if (thinkingStepId === null || reasoningMessageId === null) {
|
|
748
|
+
thinkingStepId = generateId(adapterName);
|
|
749
|
+
reasoningMessageId = generateId(adapterName);
|
|
750
|
+
yield {
|
|
751
|
+
type: EventType.REASONING_START,
|
|
752
|
+
messageId: reasoningMessageId,
|
|
753
|
+
model,
|
|
754
|
+
timestamp
|
|
755
|
+
};
|
|
756
|
+
yield {
|
|
757
|
+
type: EventType.REASONING_MESSAGE_START,
|
|
758
|
+
messageId: reasoningMessageId,
|
|
759
|
+
role: "reasoning",
|
|
760
|
+
model,
|
|
761
|
+
timestamp
|
|
762
|
+
};
|
|
763
|
+
yield {
|
|
764
|
+
type: EventType.STEP_STARTED,
|
|
765
|
+
stepName: thinkingStepId,
|
|
766
|
+
stepId: thinkingStepId,
|
|
767
|
+
model,
|
|
768
|
+
timestamp,
|
|
769
|
+
stepType: "thinking"
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
thinkingAccumulated += thoughtText;
|
|
773
|
+
yield {
|
|
774
|
+
type: EventType.REASONING_MESSAGE_CONTENT,
|
|
775
|
+
messageId: reasoningMessageId,
|
|
776
|
+
delta: thoughtText,
|
|
777
|
+
model,
|
|
778
|
+
timestamp
|
|
779
|
+
};
|
|
780
|
+
yield {
|
|
781
|
+
type: EventType.STEP_FINISHED,
|
|
782
|
+
stepName: thinkingStepId,
|
|
783
|
+
stepId: thinkingStepId,
|
|
784
|
+
model,
|
|
785
|
+
timestamp,
|
|
786
|
+
delta: thoughtText,
|
|
787
|
+
content: thinkingAccumulated
|
|
788
|
+
};
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
default:
|
|
792
|
+
logger.provider(`gemini-text-interactions unhandled step.delta type`, { delta });
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
795
|
+
break;
|
|
796
|
+
}
|
|
797
|
+
case "step.stop": {
|
|
798
|
+
const toolCallId = indexToToolCallId.get(event.index);
|
|
799
|
+
if (toolCallId) {
|
|
800
|
+
const state = toolCalls.get(toolCallId);
|
|
801
|
+
if (state && !state.ended) {
|
|
802
|
+
state.ended = true;
|
|
803
|
+
yield {
|
|
804
|
+
type: EventType.TOOL_CALL_END,
|
|
805
|
+
toolCallId,
|
|
806
|
+
toolName: state.name,
|
|
807
|
+
model,
|
|
808
|
+
timestamp,
|
|
809
|
+
input: state.args
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
indexToToolCallId.delete(event.index);
|
|
813
|
+
}
|
|
814
|
+
break;
|
|
815
|
+
}
|
|
816
|
+
case "interaction.status_update": break;
|
|
817
|
+
case "interaction.completed": {
|
|
818
|
+
if (event.interaction.id) interactionId = event.interaction.id;
|
|
819
|
+
yield* closeOpenState();
|
|
820
|
+
const status = event.interaction.status;
|
|
821
|
+
if (statusIsError(status)) {
|
|
822
|
+
const message = `Gemini Interactions ${status}: the interaction ended without a usable response.`;
|
|
823
|
+
logger.errors("gemini-text-interactions.translateInteractionEvents non-success status", {
|
|
824
|
+
source: "gemini-text-interactions.chatStream",
|
|
825
|
+
status,
|
|
826
|
+
interactionId
|
|
827
|
+
});
|
|
828
|
+
yield {
|
|
829
|
+
type: EventType.RUN_ERROR,
|
|
830
|
+
runId,
|
|
831
|
+
model,
|
|
832
|
+
timestamp,
|
|
833
|
+
message,
|
|
834
|
+
code: status,
|
|
835
|
+
error: {
|
|
836
|
+
message,
|
|
837
|
+
code: status
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
const usage = event.interaction.usage;
|
|
843
|
+
const finishReason = statusToFinishReason(status, sawFunctionCall);
|
|
844
|
+
if (interactionId) yield {
|
|
845
|
+
type: EventType.CUSTOM,
|
|
846
|
+
name: "gemini.interactionId",
|
|
847
|
+
value: { interactionId },
|
|
848
|
+
model,
|
|
849
|
+
timestamp
|
|
850
|
+
};
|
|
851
|
+
yield {
|
|
852
|
+
type: EventType.RUN_FINISHED,
|
|
853
|
+
runId,
|
|
854
|
+
threadId,
|
|
855
|
+
model,
|
|
856
|
+
timestamp,
|
|
857
|
+
finishReason,
|
|
858
|
+
usage: usage ? {
|
|
859
|
+
promptTokens: usage.total_input_tokens ?? 0,
|
|
860
|
+
completionTokens: usage.total_output_tokens ?? 0,
|
|
861
|
+
totalTokens: usage.total_tokens ?? 0
|
|
862
|
+
} : void 0
|
|
863
|
+
};
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
case "error": {
|
|
867
|
+
yield* closeOpenState();
|
|
868
|
+
const rawMessage = event.error?.message;
|
|
869
|
+
const message = typeof rawMessage === "string" && rawMessage.length > 0 ? rawMessage : `Gemini Interactions error (no message): ${JSON.stringify(event.error ?? {})}`;
|
|
870
|
+
const rawCode = event.error?.code;
|
|
871
|
+
const code = typeof rawCode === "string" || typeof rawCode === "number" ? String(rawCode) : void 0;
|
|
872
|
+
yield {
|
|
873
|
+
type: EventType.RUN_ERROR,
|
|
874
|
+
runId,
|
|
875
|
+
model,
|
|
876
|
+
timestamp,
|
|
877
|
+
message,
|
|
878
|
+
code,
|
|
879
|
+
error: {
|
|
880
|
+
message,
|
|
881
|
+
code
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
default:
|
|
887
|
+
logger.provider(`gemini-text-interactions unhandled event_type`, { event });
|
|
888
|
+
break;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
yield* closeOpenState();
|
|
1019
892
|
}
|
|
1020
893
|
function extractTextFromInteraction(interaction) {
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
if (part.type === "text") {
|
|
1029
|
-
text += part.text;
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
return text;
|
|
894
|
+
if (typeof interaction.output_text === "string" && interaction.output_text) return interaction.output_text;
|
|
895
|
+
let text = "";
|
|
896
|
+
for (const step of interaction.steps ?? []) {
|
|
897
|
+
if (step.type !== "model_output" || !step.content) continue;
|
|
898
|
+
for (const part of step.content) if (part.type === "text") text += part.text;
|
|
899
|
+
}
|
|
900
|
+
return text;
|
|
1034
901
|
}
|
|
1035
902
|
function sanitizeToolParameters(schema) {
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
}
|
|
1045
|
-
return out;
|
|
903
|
+
if (!schema || typeof schema !== "object") return schema;
|
|
904
|
+
if (Array.isArray(schema)) return schema.map(sanitizeToolParameters);
|
|
905
|
+
const out = {};
|
|
906
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
907
|
+
if (key === "required" && Array.isArray(value) && value.length === 0) continue;
|
|
908
|
+
out[key] = sanitizeToolParameters(value);
|
|
909
|
+
}
|
|
910
|
+
return out;
|
|
1046
911
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
};
|
|
1052
|
-
//# sourceMappingURL=adapter.js.map
|
|
912
|
+
//#endregion
|
|
913
|
+
export { GeminiTextInteractionsAdapter, createGeminiTextInteractions, geminiTextInteractions };
|
|
914
|
+
|
|
915
|
+
//# sourceMappingURL=adapter.js.map
|