@ai-sdk/google 4.0.0-beta.7 → 4.0.0-beta.82
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/CHANGELOG.md +614 -5
- package/README.md +6 -4
- package/dist/index.d.ts +301 -50
- package/dist/index.js +5410 -639
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +100 -26
- package/dist/internal/index.js +1653 -451
- package/dist/internal/index.js.map +1 -1
- package/docs/{15-google-generative-ai.mdx → 15-google.mdx} +784 -69
- package/package.json +16 -17
- package/src/{convert-google-generative-ai-usage.ts → convert-google-usage.ts} +13 -5
- package/src/convert-json-schema-to-openapi-schema.ts +1 -1
- package/src/convert-to-google-messages.ts +647 -0
- package/src/{google-generative-ai-embedding-options.ts → google-embedding-model-options.ts} +9 -2
- package/src/{google-generative-ai-embedding-model.ts → google-embedding-model.ts} +31 -18
- package/src/google-error.ts +1 -1
- package/src/google-files.ts +225 -0
- package/src/google-image-model-options.ts +35 -0
- package/src/{google-generative-ai-image-model.ts → google-image-model.ts} +116 -65
- package/src/{google-generative-ai-image-settings.ts → google-image-settings.ts} +2 -2
- package/src/google-json-accumulator.ts +371 -0
- package/src/{google-generative-ai-options.ts → google-language-model-options.ts} +50 -5
- package/src/{google-generative-ai-language-model.ts → google-language-model.ts} +701 -219
- package/src/google-prepare-tools.ts +72 -12
- package/src/google-prompt.ts +86 -0
- package/src/google-provider.ts +157 -53
- package/src/google-speech-api.ts +36 -0
- package/src/google-speech-model-options.ts +48 -0
- package/src/google-speech-model.ts +311 -0
- package/src/google-video-model-options.ts +43 -0
- package/src/{google-generative-ai-video-model.ts → google-video-model.ts} +25 -60
- package/src/{google-generative-ai-video-settings.ts → google-video-settings.ts} +2 -1
- package/src/index.ts +40 -9
- package/src/interactions/build-google-interactions-stream-transform.ts +818 -0
- package/src/interactions/cancel-google-interaction.ts +60 -0
- package/src/interactions/convert-google-interactions-usage.ts +47 -0
- package/src/interactions/convert-to-google-interactions-input.ts +557 -0
- package/src/interactions/extract-google-interactions-sources.ts +252 -0
- package/src/interactions/google-interactions-agent.ts +15 -0
- package/src/interactions/google-interactions-api.ts +530 -0
- package/src/interactions/google-interactions-language-model-options.ts +262 -0
- package/src/interactions/google-interactions-language-model.ts +776 -0
- package/src/interactions/google-interactions-prompt.ts +582 -0
- package/src/interactions/google-interactions-provider-metadata.ts +23 -0
- package/src/interactions/map-google-interactions-finish-reason.ts +31 -0
- package/src/interactions/parse-google-interactions-outputs.ts +252 -0
- package/src/interactions/poll-google-interactions.ts +129 -0
- package/src/interactions/prepare-google-interactions-tools.ts +245 -0
- package/src/interactions/stream-google-interactions.ts +242 -0
- package/src/interactions/synthesize-google-interactions-agent-stream.ts +185 -0
- package/src/internal/index.ts +3 -2
- package/src/{map-google-generative-ai-finish-reason.ts → map-google-finish-reason.ts} +3 -3
- package/src/realtime/google-realtime-event-mapper.ts +383 -0
- package/src/realtime/google-realtime-model-options.ts +3 -0
- package/src/realtime/google-realtime-model.ts +160 -0
- package/src/realtime/index.ts +2 -0
- package/src/tool/code-execution.ts +2 -2
- package/src/tool/enterprise-web-search.ts +9 -3
- package/src/tool/file-search.ts +5 -7
- package/src/tool/google-maps.ts +3 -2
- package/src/tool/google-search.ts +11 -12
- package/src/tool/url-context.ts +4 -2
- package/src/tool/vertex-rag-store.ts +9 -6
- package/dist/index.d.mts +0 -376
- package/dist/index.mjs +0 -2517
- package/dist/index.mjs.map +0 -1
- package/dist/internal/index.d.mts +0 -284
- package/dist/internal/index.mjs +0 -1706
- package/dist/internal/index.mjs.map +0 -1
- package/src/convert-to-google-generative-ai-messages.ts +0 -239
- package/src/google-generative-ai-prompt.ts +0 -38
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import type { JSONValue, LanguageModelV4Content } from '@ai-sdk/provider';
|
|
2
|
+
import type { GoogleInteractionsStep } from './google-interactions-api';
|
|
3
|
+
import {
|
|
4
|
+
annotationsToSources,
|
|
5
|
+
builtinToolResultToSources,
|
|
6
|
+
} from './extract-google-interactions-sources';
|
|
7
|
+
import type {
|
|
8
|
+
GoogleInteractionsAnnotation,
|
|
9
|
+
GoogleInteractionsBuiltinToolResultContent,
|
|
10
|
+
} from './google-interactions-prompt';
|
|
11
|
+
|
|
12
|
+
export type ParseGoogleInteractionsOutputsResult = {
|
|
13
|
+
content: Array<LanguageModelV4Content>;
|
|
14
|
+
hasFunctionCall: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* Builds a `providerMetadata.google` payload for an output part so the
|
|
19
|
+
* Interactions converter on the next turn can read both the per-step
|
|
20
|
+
* `signature` (round-trip) and the parent `interactionId` (history compaction
|
|
21
|
+
* under `previousInteractionId`).
|
|
22
|
+
*/
|
|
23
|
+
function googleProviderMetadata({
|
|
24
|
+
signature,
|
|
25
|
+
interactionId,
|
|
26
|
+
}: {
|
|
27
|
+
signature?: string | null;
|
|
28
|
+
interactionId?: string;
|
|
29
|
+
}): { providerMetadata: { google: Record<string, string> } } | object {
|
|
30
|
+
const google: Record<string, string> = {};
|
|
31
|
+
if (signature != null) {
|
|
32
|
+
google.signature = signature;
|
|
33
|
+
}
|
|
34
|
+
if (interactionId != null) {
|
|
35
|
+
google.interactionId = interactionId;
|
|
36
|
+
}
|
|
37
|
+
return Object.keys(google).length > 0 ? { providerMetadata: { google } } : {};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const BUILTIN_TOOL_CALL_TYPES = new Set([
|
|
41
|
+
'google_search_call',
|
|
42
|
+
'code_execution_call',
|
|
43
|
+
'url_context_call',
|
|
44
|
+
'file_search_call',
|
|
45
|
+
'google_maps_call',
|
|
46
|
+
'mcp_server_tool_call',
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
const BUILTIN_TOOL_RESULT_TYPES = new Set([
|
|
50
|
+
'google_search_result',
|
|
51
|
+
'code_execution_result',
|
|
52
|
+
'url_context_result',
|
|
53
|
+
'file_search_result',
|
|
54
|
+
'google_maps_result',
|
|
55
|
+
'mcp_server_tool_result',
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
function builtinToolNameFromCallType(type: string): string {
|
|
59
|
+
return type.replace(/_call$/, '');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function builtinToolNameFromResultType(type: string): string {
|
|
63
|
+
return type.replace(/_result$/, '');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Walks the `steps[]` array of an Interactions response and emits AI SDK
|
|
68
|
+
* `LanguageModelV4Content[]`. Surfaces:
|
|
69
|
+
*
|
|
70
|
+
* - `model_output` steps: iterates `step.content[]` for `text` (with
|
|
71
|
+
* annotations → source parts) and `image` content blocks.
|
|
72
|
+
* - `thought` steps: emits a single `reasoning` part from `summary[*]`.
|
|
73
|
+
* - `function_call` steps: emits a `tool-call` part directly.
|
|
74
|
+
* - Built-in tool `*_call` / `*_result` steps (Google Search, Code Execution,
|
|
75
|
+
* URL Context, File Search, Google Maps, MCP Server): emits
|
|
76
|
+
* `tool-call`/`tool-result` parts with `providerExecuted: true`.
|
|
77
|
+
* - `user_input` steps are skipped (they echo the client's input).
|
|
78
|
+
*/
|
|
79
|
+
export function parseGoogleInteractionsOutputs({
|
|
80
|
+
steps,
|
|
81
|
+
generateId,
|
|
82
|
+
interactionId,
|
|
83
|
+
}: {
|
|
84
|
+
steps: Array<GoogleInteractionsStep> | null | undefined;
|
|
85
|
+
generateId: () => string;
|
|
86
|
+
/**
|
|
87
|
+
* Top-level `Interaction.id` on the response. Stamped onto each output
|
|
88
|
+
* part's `providerMetadata.google.interactionId` so the converter can drop
|
|
89
|
+
* matching assistant turns when `previousInteractionId` is used on the
|
|
90
|
+
* next turn (compaction).
|
|
91
|
+
*/
|
|
92
|
+
interactionId?: string;
|
|
93
|
+
}): ParseGoogleInteractionsOutputsResult {
|
|
94
|
+
const content: Array<LanguageModelV4Content> = [];
|
|
95
|
+
let hasFunctionCall = false;
|
|
96
|
+
|
|
97
|
+
if (steps == null) {
|
|
98
|
+
return { content, hasFunctionCall };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const step of steps) {
|
|
102
|
+
if (step == null || typeof step !== 'object') continue;
|
|
103
|
+
const type = (step as { type?: string }).type;
|
|
104
|
+
if (typeof type !== 'string') continue;
|
|
105
|
+
|
|
106
|
+
switch (type) {
|
|
107
|
+
case 'user_input': {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 'model_output': {
|
|
111
|
+
const blocks =
|
|
112
|
+
(step as { content?: Array<{ type?: string; [k: string]: unknown }> })
|
|
113
|
+
.content ?? [];
|
|
114
|
+
for (const block of blocks) {
|
|
115
|
+
if (block == null || typeof block !== 'object') continue;
|
|
116
|
+
const blockType = block.type;
|
|
117
|
+
if (blockType === 'text') {
|
|
118
|
+
const text = (block as { text?: string }).text ?? '';
|
|
119
|
+
const annotations = (
|
|
120
|
+
block as {
|
|
121
|
+
annotations?: Array<GoogleInteractionsAnnotation>;
|
|
122
|
+
}
|
|
123
|
+
).annotations;
|
|
124
|
+
content.push({
|
|
125
|
+
type: 'text',
|
|
126
|
+
text,
|
|
127
|
+
...googleProviderMetadata({ interactionId }),
|
|
128
|
+
});
|
|
129
|
+
const sources = annotationsToSources({ annotations, generateId });
|
|
130
|
+
for (const source of sources) {
|
|
131
|
+
content.push(source);
|
|
132
|
+
}
|
|
133
|
+
} else if (blockType === 'image') {
|
|
134
|
+
const image = block as {
|
|
135
|
+
data?: string;
|
|
136
|
+
mime_type?: string;
|
|
137
|
+
uri?: string;
|
|
138
|
+
};
|
|
139
|
+
if (image.data != null && image.data.length > 0) {
|
|
140
|
+
content.push({
|
|
141
|
+
type: 'file',
|
|
142
|
+
mediaType: image.mime_type ?? 'image/png',
|
|
143
|
+
data: { type: 'data', data: image.data },
|
|
144
|
+
...googleProviderMetadata({ interactionId }),
|
|
145
|
+
});
|
|
146
|
+
} else if (image.uri != null && image.uri.length > 0) {
|
|
147
|
+
content.push({
|
|
148
|
+
type: 'file',
|
|
149
|
+
mediaType: image.mime_type ?? 'image/png',
|
|
150
|
+
data: { type: 'url', url: new URL(image.uri) },
|
|
151
|
+
...googleProviderMetadata({ interactionId }),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case 'thought': {
|
|
159
|
+
const thought = step as {
|
|
160
|
+
signature?: string;
|
|
161
|
+
summary?: Array<{ type: string; text?: string }>;
|
|
162
|
+
};
|
|
163
|
+
const summary = Array.isArray(thought.summary) ? thought.summary : [];
|
|
164
|
+
const text = summary
|
|
165
|
+
.filter(
|
|
166
|
+
item => item?.type === 'text' && typeof item.text === 'string',
|
|
167
|
+
)
|
|
168
|
+
.map(item => item.text as string)
|
|
169
|
+
.join('\n');
|
|
170
|
+
content.push({
|
|
171
|
+
type: 'reasoning',
|
|
172
|
+
text,
|
|
173
|
+
...googleProviderMetadata({
|
|
174
|
+
signature: thought.signature,
|
|
175
|
+
interactionId,
|
|
176
|
+
}),
|
|
177
|
+
});
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
case 'function_call': {
|
|
181
|
+
hasFunctionCall = true;
|
|
182
|
+
const call = step as {
|
|
183
|
+
id: string;
|
|
184
|
+
name: string;
|
|
185
|
+
arguments?: Record<string, unknown> | null;
|
|
186
|
+
signature?: string | null;
|
|
187
|
+
};
|
|
188
|
+
content.push({
|
|
189
|
+
type: 'tool-call',
|
|
190
|
+
toolCallId: call.id,
|
|
191
|
+
toolName: call.name,
|
|
192
|
+
input: JSON.stringify(call.arguments ?? {}),
|
|
193
|
+
...googleProviderMetadata({
|
|
194
|
+
signature: call.signature,
|
|
195
|
+
interactionId,
|
|
196
|
+
}),
|
|
197
|
+
});
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
default: {
|
|
201
|
+
if (BUILTIN_TOOL_CALL_TYPES.has(type)) {
|
|
202
|
+
const call = step as {
|
|
203
|
+
id?: string;
|
|
204
|
+
arguments?: Record<string, unknown>;
|
|
205
|
+
name?: string;
|
|
206
|
+
server_name?: string;
|
|
207
|
+
};
|
|
208
|
+
const toolName =
|
|
209
|
+
type === 'mcp_server_tool_call'
|
|
210
|
+
? (call.name ?? 'mcp_server_tool')
|
|
211
|
+
: builtinToolNameFromCallType(type);
|
|
212
|
+
const input = JSON.stringify(call.arguments ?? {});
|
|
213
|
+
content.push({
|
|
214
|
+
type: 'tool-call',
|
|
215
|
+
toolCallId: call.id ?? generateId(),
|
|
216
|
+
toolName,
|
|
217
|
+
input,
|
|
218
|
+
providerExecuted: true,
|
|
219
|
+
});
|
|
220
|
+
} else if (BUILTIN_TOOL_RESULT_TYPES.has(type)) {
|
|
221
|
+
const result = step as {
|
|
222
|
+
call_id?: string;
|
|
223
|
+
result?: unknown;
|
|
224
|
+
is_error?: boolean;
|
|
225
|
+
name?: string;
|
|
226
|
+
};
|
|
227
|
+
const toolName =
|
|
228
|
+
type === 'mcp_server_tool_result'
|
|
229
|
+
? (result.name ?? 'mcp_server_tool')
|
|
230
|
+
: builtinToolNameFromResultType(type);
|
|
231
|
+
content.push({
|
|
232
|
+
type: 'tool-result',
|
|
233
|
+
toolCallId: result.call_id ?? generateId(),
|
|
234
|
+
toolName,
|
|
235
|
+
result: (result.result ?? null) as NonNullable<JSONValue>,
|
|
236
|
+
});
|
|
237
|
+
const sources = builtinToolResultToSources({
|
|
238
|
+
block:
|
|
239
|
+
step as unknown as GoogleInteractionsBuiltinToolResultContent,
|
|
240
|
+
generateId,
|
|
241
|
+
});
|
|
242
|
+
for (const source of sources) {
|
|
243
|
+
content.push(source);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return { content, hasFunctionCall };
|
|
252
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createJsonResponseHandler,
|
|
3
|
+
delay,
|
|
4
|
+
getFromApi,
|
|
5
|
+
isAbortError,
|
|
6
|
+
type FetchFunction,
|
|
7
|
+
} from '@ai-sdk/provider-utils';
|
|
8
|
+
import { googleFailedResponseHandler } from '../google-error';
|
|
9
|
+
import { cancelGoogleInteraction } from './cancel-google-interaction';
|
|
10
|
+
import {
|
|
11
|
+
googleInteractionsResponseSchema,
|
|
12
|
+
type GoogleInteractionsResponse,
|
|
13
|
+
} from './google-interactions-api';
|
|
14
|
+
import type { GoogleInteractionsStatus } from './google-interactions-prompt';
|
|
15
|
+
|
|
16
|
+
const TERMINAL_STATUSES: ReadonlySet<GoogleInteractionsStatus | string> =
|
|
17
|
+
new Set(['completed', 'failed', 'cancelled', 'incomplete']);
|
|
18
|
+
|
|
19
|
+
export function isTerminalStatus(
|
|
20
|
+
status: GoogleInteractionsStatus | string | null | undefined,
|
|
21
|
+
): boolean {
|
|
22
|
+
return status != null && TERMINAL_STATUSES.has(status);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
* Default polling cadence for background interactions. Starts at 1 s, doubles
|
|
27
|
+
* each tick up to a 10 s ceiling, and gives up after 30 minutes -- agent runs
|
|
28
|
+
* such as deep research can take tens of minutes server-side, so we err on
|
|
29
|
+
* the long side rather than truncate a real run. Override per-call via
|
|
30
|
+
* `providerOptions.google.pollingTimeoutMs`.
|
|
31
|
+
*/
|
|
32
|
+
const DEFAULT_INITIAL_DELAY_MS = 1000;
|
|
33
|
+
const DEFAULT_MAX_DELAY_MS = 10000;
|
|
34
|
+
const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
|
|
35
|
+
|
|
36
|
+
export type PollGoogleInteractionResult = {
|
|
37
|
+
response: GoogleInteractionsResponse;
|
|
38
|
+
rawResponse: unknown;
|
|
39
|
+
responseHeaders: Record<string, string> | undefined;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Polls `GET {baseURL}/interactions/{id}` until the response status is
|
|
44
|
+
* terminal (`completed` / `failed` / `cancelled` / `incomplete`). Throws if
|
|
45
|
+
* the polling loop exceeds `timeoutMs`, the response has no `id` to poll on,
|
|
46
|
+
* or the abort signal fires.
|
|
47
|
+
*/
|
|
48
|
+
export async function pollGoogleInteractionUntilTerminal({
|
|
49
|
+
baseURL,
|
|
50
|
+
interactionId,
|
|
51
|
+
headers,
|
|
52
|
+
fetch,
|
|
53
|
+
abortSignal,
|
|
54
|
+
initialDelayMs = DEFAULT_INITIAL_DELAY_MS,
|
|
55
|
+
maxDelayMs = DEFAULT_MAX_DELAY_MS,
|
|
56
|
+
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
57
|
+
}: {
|
|
58
|
+
baseURL: string;
|
|
59
|
+
interactionId: string | null | undefined;
|
|
60
|
+
headers: Record<string, string | undefined>;
|
|
61
|
+
fetch?: FetchFunction;
|
|
62
|
+
abortSignal?: AbortSignal;
|
|
63
|
+
initialDelayMs?: number;
|
|
64
|
+
maxDelayMs?: number;
|
|
65
|
+
timeoutMs?: number;
|
|
66
|
+
}): Promise<PollGoogleInteractionResult> {
|
|
67
|
+
if (interactionId == null || interactionId.length === 0) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
'google.interactions: cannot poll a background interaction without an id. ' +
|
|
70
|
+
'The POST response did not include an interaction id.',
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const startedAt = Date.now();
|
|
75
|
+
let nextDelayMs = initialDelayMs;
|
|
76
|
+
const url = `${baseURL}/interactions/${encodeURIComponent(interactionId)}`;
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
* When the caller aborts, fire a best-effort `POST /interactions/{id}/cancel`
|
|
80
|
+
* so the run stops billing on Google's side. Wrap every exit path that's
|
|
81
|
+
* triggered by an abort -- the explicit `abortSignal.aborted` check, the
|
|
82
|
+
* AbortError thrown by `delay()`, and any AbortError thrown by `getFromApi`.
|
|
83
|
+
*/
|
|
84
|
+
const cancelOnServer = () =>
|
|
85
|
+
cancelGoogleInteraction({ baseURL, interactionId, headers, fetch });
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
while (true) {
|
|
89
|
+
if (abortSignal?.aborted) {
|
|
90
|
+
await cancelOnServer();
|
|
91
|
+
throw new DOMException('Polling was aborted', 'AbortError');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`google.interactions: timed out polling interaction ${interactionId} after ${timeoutMs}ms.`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await delay(nextDelayMs, { abortSignal });
|
|
101
|
+
|
|
102
|
+
const {
|
|
103
|
+
value: response,
|
|
104
|
+
rawValue: rawResponse,
|
|
105
|
+
responseHeaders,
|
|
106
|
+
} = await getFromApi({
|
|
107
|
+
url,
|
|
108
|
+
headers,
|
|
109
|
+
failedResponseHandler: googleFailedResponseHandler,
|
|
110
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
111
|
+
googleInteractionsResponseSchema,
|
|
112
|
+
),
|
|
113
|
+
abortSignal,
|
|
114
|
+
fetch,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
if (isTerminalStatus(response.status)) {
|
|
118
|
+
return { response, rawResponse, responseHeaders };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
nextDelayMs = Math.min(nextDelayMs * 2, maxDelayMs);
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (isAbortError(error)) {
|
|
125
|
+
await cancelOnServer();
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV4CallOptions,
|
|
3
|
+
SharedV4Warning,
|
|
4
|
+
} from '@ai-sdk/provider';
|
|
5
|
+
import type {
|
|
6
|
+
GoogleInteractionsTool,
|
|
7
|
+
GoogleInteractionsToolChoice,
|
|
8
|
+
} from './google-interactions-prompt';
|
|
9
|
+
|
|
10
|
+
export type PrepareGoogleInteractionsToolsResult = {
|
|
11
|
+
tools: Array<GoogleInteractionsTool> | undefined;
|
|
12
|
+
toolChoice: GoogleInteractionsToolChoice | undefined;
|
|
13
|
+
toolWarnings: Array<SharedV4Warning>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Maps AI SDK tool definitions and `toolChoice` onto the Gemini Interactions
|
|
18
|
+
* `tools[]` and `tool_choice` request fields.
|
|
19
|
+
*
|
|
20
|
+
* AI SDK function tools (`{ type: 'function', name, description, inputSchema }`)
|
|
21
|
+
* map to Interactions `{ type: 'function', name, description, parameters }`,
|
|
22
|
+
* with `parameters` passed through as plain JSON Schema (per
|
|
23
|
+
* `googleapis/js-genai` `samples/interactions_tool_call_with_functions.ts` and
|
|
24
|
+
* `src/interactions/resources/interactions.ts` `Function.parameters: unknown`).
|
|
25
|
+
*
|
|
26
|
+
* Provider-defined tools (`{ type: 'provider', id: 'google.<name>', args }`)
|
|
27
|
+
* map to the discriminated `Tool` union. The full set of
|
|
28
|
+
* provider-defined tool ids supported here:
|
|
29
|
+
*
|
|
30
|
+
* - `google.google_search` -> `{ type: 'google_search', search_types? }`
|
|
31
|
+
* - `google.code_execution` -> `{ type: 'code_execution' }`
|
|
32
|
+
* - `google.url_context` -> `{ type: 'url_context' }`
|
|
33
|
+
* - `google.file_search` -> `{ type: 'file_search', file_search_store_names?, top_k?, metadata_filter? }`
|
|
34
|
+
* - `google.google_maps` -> `{ type: 'google_maps', latitude?, longitude?, enable_widget? }`
|
|
35
|
+
* - `google.computer_use` -> `{ type: 'computer_use', environment?, excludedPredefinedFunctions? }`
|
|
36
|
+
* - `google.mcp_server` -> `{ type: 'mcp_server', name?, url?, headers?, allowed_tools? }`
|
|
37
|
+
* - `google.retrieval` -> `{ type: 'retrieval', retrieval_types?, vertex_ai_search_config? }`
|
|
38
|
+
*
|
|
39
|
+
* `toolChoice` shapes:
|
|
40
|
+
* - `'auto'` -> `'auto'`
|
|
41
|
+
* - `'required'` -> `'any'`
|
|
42
|
+
* - `'none'` -> `'none'`
|
|
43
|
+
* - `{ type: 'tool', toolName }` -> `{ allowed_tools: { mode: 'validated', tools: [name] } }`
|
|
44
|
+
* (Interactions `AllowedTools.tools` is an `Array<string>` of function
|
|
45
|
+
* names, not tool descriptors -- see `src/interactions/resources/interactions.ts`
|
|
46
|
+
* line ~151).
|
|
47
|
+
*/
|
|
48
|
+
export function prepareGoogleInteractionsTools({
|
|
49
|
+
tools,
|
|
50
|
+
toolChoice,
|
|
51
|
+
}: {
|
|
52
|
+
tools: LanguageModelV4CallOptions['tools'];
|
|
53
|
+
toolChoice?: LanguageModelV4CallOptions['toolChoice'];
|
|
54
|
+
}): PrepareGoogleInteractionsToolsResult {
|
|
55
|
+
const toolWarnings: Array<SharedV4Warning> = [];
|
|
56
|
+
|
|
57
|
+
const normalized = tools?.length ? tools : undefined;
|
|
58
|
+
|
|
59
|
+
if (normalized == null) {
|
|
60
|
+
return { tools: undefined, toolChoice: undefined, toolWarnings };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const interactionsTools: Array<GoogleInteractionsTool> = [];
|
|
64
|
+
|
|
65
|
+
for (const tool of normalized) {
|
|
66
|
+
if (tool.type === 'function') {
|
|
67
|
+
interactionsTools.push({
|
|
68
|
+
type: 'function',
|
|
69
|
+
name: tool.name,
|
|
70
|
+
description: tool.description ?? '',
|
|
71
|
+
parameters: tool.inputSchema,
|
|
72
|
+
});
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (tool.type === 'provider') {
|
|
77
|
+
const args = (tool.args ?? {}) as Record<string, unknown>;
|
|
78
|
+
switch (tool.id) {
|
|
79
|
+
case 'google.google_search': {
|
|
80
|
+
const searchTypesArg = args.searchTypes as
|
|
81
|
+
| { webSearch?: unknown; imageSearch?: unknown }
|
|
82
|
+
| undefined;
|
|
83
|
+
let search_types:
|
|
84
|
+
| Array<'web_search' | 'image_search' | 'enterprise_web_search'>
|
|
85
|
+
| undefined;
|
|
86
|
+
if (searchTypesArg != null && typeof searchTypesArg === 'object') {
|
|
87
|
+
const list: Array<
|
|
88
|
+
'web_search' | 'image_search' | 'enterprise_web_search'
|
|
89
|
+
> = [];
|
|
90
|
+
if (searchTypesArg.webSearch != null) list.push('web_search');
|
|
91
|
+
if (searchTypesArg.imageSearch != null) list.push('image_search');
|
|
92
|
+
if (list.length > 0) {
|
|
93
|
+
search_types = list;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
interactionsTools.push({
|
|
97
|
+
type: 'google_search',
|
|
98
|
+
...(search_types != null ? { search_types } : {}),
|
|
99
|
+
});
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case 'google.code_execution': {
|
|
103
|
+
interactionsTools.push({ type: 'code_execution' });
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
case 'google.url_context': {
|
|
107
|
+
interactionsTools.push({ type: 'url_context' });
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 'google.file_search': {
|
|
111
|
+
interactionsTools.push({
|
|
112
|
+
type: 'file_search',
|
|
113
|
+
...(args.fileSearchStoreNames != null
|
|
114
|
+
? {
|
|
115
|
+
file_search_store_names:
|
|
116
|
+
args.fileSearchStoreNames as Array<string>,
|
|
117
|
+
}
|
|
118
|
+
: {}),
|
|
119
|
+
...(args.topK != null ? { top_k: args.topK as number } : {}),
|
|
120
|
+
...(args.metadataFilter != null
|
|
121
|
+
? { metadata_filter: args.metadataFilter as string }
|
|
122
|
+
: {}),
|
|
123
|
+
});
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case 'google.google_maps': {
|
|
127
|
+
interactionsTools.push({
|
|
128
|
+
type: 'google_maps',
|
|
129
|
+
...(args.latitude != null
|
|
130
|
+
? { latitude: args.latitude as number }
|
|
131
|
+
: {}),
|
|
132
|
+
...(args.longitude != null
|
|
133
|
+
? { longitude: args.longitude as number }
|
|
134
|
+
: {}),
|
|
135
|
+
...(args.enableWidget != null
|
|
136
|
+
? { enable_widget: args.enableWidget as boolean }
|
|
137
|
+
: {}),
|
|
138
|
+
});
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
case 'google.computer_use': {
|
|
142
|
+
interactionsTools.push({
|
|
143
|
+
type: 'computer_use',
|
|
144
|
+
environment:
|
|
145
|
+
(args.environment as 'browser' | undefined) ?? 'browser',
|
|
146
|
+
...(args.excludedPredefinedFunctions != null
|
|
147
|
+
? {
|
|
148
|
+
excludedPredefinedFunctions:
|
|
149
|
+
args.excludedPredefinedFunctions as Array<string>,
|
|
150
|
+
}
|
|
151
|
+
: {}),
|
|
152
|
+
});
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case 'google.mcp_server': {
|
|
156
|
+
interactionsTools.push({
|
|
157
|
+
type: 'mcp_server',
|
|
158
|
+
...(args.name != null ? { name: args.name as string } : {}),
|
|
159
|
+
...(args.url != null ? { url: args.url as string } : {}),
|
|
160
|
+
...(args.headers != null
|
|
161
|
+
? { headers: args.headers as Record<string, string> }
|
|
162
|
+
: {}),
|
|
163
|
+
...(args.allowedTools != null
|
|
164
|
+
? { allowed_tools: args.allowedTools as Array<unknown> }
|
|
165
|
+
: {}),
|
|
166
|
+
});
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
case 'google.retrieval': {
|
|
170
|
+
const vertexAiSearchConfig =
|
|
171
|
+
(args.vertexAiSearchConfig as
|
|
172
|
+
| { datastores?: Array<string>; engine?: string }
|
|
173
|
+
| undefined) ?? undefined;
|
|
174
|
+
interactionsTools.push({
|
|
175
|
+
type: 'retrieval',
|
|
176
|
+
...(args.retrievalTypes != null
|
|
177
|
+
? {
|
|
178
|
+
retrieval_types:
|
|
179
|
+
args.retrievalTypes as Array<'vertex_ai_search'>,
|
|
180
|
+
}
|
|
181
|
+
: { retrieval_types: ['vertex_ai_search'] }),
|
|
182
|
+
...(vertexAiSearchConfig != null
|
|
183
|
+
? { vertex_ai_search_config: vertexAiSearchConfig }
|
|
184
|
+
: {}),
|
|
185
|
+
});
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
default: {
|
|
189
|
+
toolWarnings.push({
|
|
190
|
+
type: 'unsupported',
|
|
191
|
+
feature: `provider-defined tool ${tool.id}`,
|
|
192
|
+
details: `provider-defined tool ${tool.id} is not supported by google.interactions; tool dropped.`,
|
|
193
|
+
});
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
toolWarnings.push({
|
|
201
|
+
type: 'unsupported',
|
|
202
|
+
feature: `tool of type ${(tool as { type: string }).type}`,
|
|
203
|
+
details:
|
|
204
|
+
'Only function tools and google.* provider-defined tools are supported by google.interactions; tool dropped.',
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/*
|
|
209
|
+
* `tool_choice` on the Interactions API governs function calling only -- the
|
|
210
|
+
* API rejects requests with `tool_choice` set when no `function` tools are
|
|
211
|
+
* present (`{"error":{"message":"Function calling config is set without
|
|
212
|
+
* function_declarations."}}`). Drop `tool_choice` when the resolved tool
|
|
213
|
+
* list is empty or contains no function tools.
|
|
214
|
+
*/
|
|
215
|
+
const hasFunctionTool = interactionsTools.some(t => t.type === 'function');
|
|
216
|
+
|
|
217
|
+
let mappedToolChoice: GoogleInteractionsToolChoice | undefined;
|
|
218
|
+
if (toolChoice != null && hasFunctionTool) {
|
|
219
|
+
switch (toolChoice.type) {
|
|
220
|
+
case 'auto':
|
|
221
|
+
mappedToolChoice = 'auto';
|
|
222
|
+
break;
|
|
223
|
+
case 'required':
|
|
224
|
+
mappedToolChoice = 'any';
|
|
225
|
+
break;
|
|
226
|
+
case 'none':
|
|
227
|
+
mappedToolChoice = 'none';
|
|
228
|
+
break;
|
|
229
|
+
case 'tool':
|
|
230
|
+
mappedToolChoice = {
|
|
231
|
+
allowed_tools: {
|
|
232
|
+
mode: 'validated',
|
|
233
|
+
tools: [toolChoice.toolName],
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
tools: interactionsTools.length > 0 ? interactionsTools : undefined,
|
|
242
|
+
toolChoice: mappedToolChoice,
|
|
243
|
+
toolWarnings,
|
|
244
|
+
};
|
|
245
|
+
}
|