dominds 1.28.0 → 1.29.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 +1 -0
- package/README.zh.md +1 -0
- package/dist/dialog-global-registry.d.ts +2 -0
- package/dist/dialog-global-registry.js +41 -0
- package/dist/dialog.d.ts +1 -0
- package/dist/dialog.js +5 -0
- package/dist/docs/codex-provider-auth-policy.md +59 -0
- package/dist/docs/codex-provider-auth-policy.zh.md +59 -0
- package/dist/docs/llm-provider-isolation.md +1 -1
- package/dist/docs/llm-provider-isolation.zh.md +1 -1
- package/dist/llm/api-quirks.d.ts +1 -0
- package/dist/llm/api-quirks.js +2 -1
- package/dist/llm/client.d.ts +2 -1
- package/dist/llm/defaults.yaml +163 -7
- package/dist/llm/gen/codex.d.ts +3 -2
- package/dist/llm/gen/codex.js +82 -10
- package/dist/llm/gen/google.d.ts +20 -0
- package/dist/llm/gen/google.js +735 -0
- package/dist/llm/gen/mock.js +8 -0
- package/dist/llm/gen/openai-compatible.js +5 -0
- package/dist/llm/gen/openai.js +9 -1
- package/dist/llm/gen/registry.js +4 -2
- package/dist/llm/gen/tool-output-limit.js +1 -0
- package/dist/llm/gen/tool-result-image-ingest.d.ts +1 -0
- package/dist/llm/gen/tool-result-image-ingest.js +2 -1
- package/dist/llm/kernel-driver/drive.js +21 -3
- package/dist/main-dialog-goal-reminder.d.ts +20 -0
- package/dist/main-dialog-goal-reminder.js +344 -0
- package/dist/minds/system-prompt-parts.js +14 -14
- package/dist/runtime/driver-messages.js +16 -16
- package/dist/server/setup-routes.js +3 -1
- package/dist/team.d.ts +12 -3
- package/dist/team.js +36 -4
- package/dist/tools/builtins.js +2 -0
- package/dist/tools/ctrl.d.ts +1 -0
- package/dist/tools/ctrl.js +65 -1
- package/dist/tools/prompts/control/en/index.md +4 -2
- package/dist/tools/prompts/control/en/principles.md +5 -4
- package/dist/tools/prompts/control/en/scenarios.md +9 -4
- package/dist/tools/prompts/control/en/tools.md +53 -9
- package/dist/tools/prompts/control/zh/index.md +4 -2
- package/dist/tools/prompts/control/zh/principles.md +5 -4
- package/dist/tools/prompts/control/zh/scenarios.md +9 -4
- package/dist/tools/prompts/control/zh/tools.md +53 -9
- package/dist/tools/team_mgmt-manual.js +10 -8
- package/dist/tools/team_mgmt.js +3 -0
- package/dist/tools/txt.js +115 -70
- package/package.json +5 -4
- package/webapp/dist/assets/{main-YWP5PWOM.js → main-YWYVZGBW.js} +119 -60
- package/webapp/dist/assets/{main-YWP5PWOM.js.map → main-YWYVZGBW.js.map} +3 -3
- package/webapp/dist/index.html +1 -1
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Module: llm/gen/google
|
|
4
|
+
*
|
|
5
|
+
* Google Gemini API integration implementing streaming and batch generation.
|
|
6
|
+
* Rationale:
|
|
7
|
+
* - Direct native integration via the official `@google/genai` SDK.
|
|
8
|
+
* - Supports reasoning/thinking segments via Gemini 2.0+ `thought` parts.
|
|
9
|
+
* - Supports functional tools/declarations and correlated response structures.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.GoogleGen = void 0;
|
|
13
|
+
const genai_1 = require("@google/genai");
|
|
14
|
+
const log_1 = require("../../log");
|
|
15
|
+
const i18n_text_1 = require("../../runtime/i18n-text");
|
|
16
|
+
const work_language_1 = require("../../runtime/work-language");
|
|
17
|
+
const artifacts_1 = require("./artifacts");
|
|
18
|
+
const failure_classifier_1 = require("./failure-classifier");
|
|
19
|
+
const tool_result_image_ingest_1 = require("./tool-result-image-ingest");
|
|
20
|
+
const log = (0, log_1.createLogger)('llm/google');
|
|
21
|
+
class GoogleGen {
|
|
22
|
+
apiType;
|
|
23
|
+
constructor(apiType = 'google') {
|
|
24
|
+
this.apiType = apiType;
|
|
25
|
+
}
|
|
26
|
+
classifyFailure(error) {
|
|
27
|
+
const message = (0, failure_classifier_1.readErrorMessage)(error) ?? 'Unknown Google GenAI error';
|
|
28
|
+
const msg = message.toLowerCase();
|
|
29
|
+
// 1. Check for abort/cancellation error
|
|
30
|
+
if (msg.includes('abort') ||
|
|
31
|
+
msg.includes('cancel') ||
|
|
32
|
+
(error instanceof Error && (error.name === 'AbortError' || error.message === 'AbortError'))) {
|
|
33
|
+
return {
|
|
34
|
+
kind: 'rejected',
|
|
35
|
+
message: 'Aborted.',
|
|
36
|
+
code: 'ABORTED',
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const status = (0, failure_classifier_1.readErrorStatus)(error);
|
|
40
|
+
const code = (0, failure_classifier_1.readErrorCode)(error);
|
|
41
|
+
// 2. Check for rate limit / quota limits (429)
|
|
42
|
+
if (status === 429 || msg.includes('quota') || msg.includes('limit') || msg.includes('429')) {
|
|
43
|
+
return {
|
|
44
|
+
kind: 'retriable',
|
|
45
|
+
message,
|
|
46
|
+
status: 429,
|
|
47
|
+
code: code || 'RESOURCE_EXHAUSTED',
|
|
48
|
+
retryStrategy: 'smart_rate',
|
|
49
|
+
retryAfterMs: (0, failure_classifier_1.readProviderSuggestedRetryAfterMs)(error),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// 3. Check for auth/keys/permission errors (401/403)
|
|
53
|
+
if (status === 401 ||
|
|
54
|
+
status === 403 ||
|
|
55
|
+
msg.includes('auth') ||
|
|
56
|
+
msg.includes('key') ||
|
|
57
|
+
msg.includes('401') ||
|
|
58
|
+
msg.includes('403')) {
|
|
59
|
+
return {
|
|
60
|
+
kind: 'rejected',
|
|
61
|
+
message,
|
|
62
|
+
status: status ?? 401,
|
|
63
|
+
code: code || 'UNAUTHENTICATED',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// 4. Check for invalid request, bad parameters (400, 404, etc.)
|
|
67
|
+
if (status === 400 || status === 404 || status === 413 || status === 422) {
|
|
68
|
+
return {
|
|
69
|
+
kind: 'rejected',
|
|
70
|
+
message,
|
|
71
|
+
status,
|
|
72
|
+
code: code || 'INVALID_ARGUMENT',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// 5. Check for 5xx errors or other server errors (retriable)
|
|
76
|
+
if (status !== undefined || code !== undefined) {
|
|
77
|
+
return {
|
|
78
|
+
kind: 'retriable',
|
|
79
|
+
message,
|
|
80
|
+
status,
|
|
81
|
+
code,
|
|
82
|
+
retryStrategy: 'conservative',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// Fall back to the generic LLM failure classifier for transport/network issues.
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
async genToReceiver(providerConfig, agent, systemPrompt, funcTools, requestContext, context, receiver, genseq, abortSignal) {
|
|
89
|
+
const apiKey = process.env[providerConfig.apiKeyEnvVar];
|
|
90
|
+
if (!apiKey) {
|
|
91
|
+
throw new Error(`Google API key not found in env var ${providerConfig.apiKeyEnvVar}`);
|
|
92
|
+
}
|
|
93
|
+
const ai = new genai_1.GoogleGenAI({
|
|
94
|
+
apiKey,
|
|
95
|
+
...(providerConfig.baseUrl ? { httpOptions: { baseUrl: providerConfig.baseUrl } } : {}),
|
|
96
|
+
});
|
|
97
|
+
const modelKey = requestContext.modelKey || agent.model || 'gemini-2.5-flash';
|
|
98
|
+
const geminiContents = await buildGeminiRequestInput(context, requestContext, providerConfig, receiver.toolResultImageIngest ? receiver.toolResultImageIngest.bind(receiver) : undefined, receiver.userImageIngest ? receiver.userImageIngest.bind(receiver) : undefined);
|
|
99
|
+
const modelParams = agent.model_params?.google || {};
|
|
100
|
+
const geminiTools = [];
|
|
101
|
+
if (funcTools.length > 0) {
|
|
102
|
+
geminiTools.push({ functionDeclarations: funcTools.map(funcToolToGeminiFunction) });
|
|
103
|
+
}
|
|
104
|
+
if (modelParams.google_search_tool) {
|
|
105
|
+
geminiTools.push({ googleSearch: {} });
|
|
106
|
+
}
|
|
107
|
+
const finalTools = geminiTools.length > 0 ? geminiTools : undefined;
|
|
108
|
+
const requirement = requestContext.toolUseRequirement ?? 'auto';
|
|
109
|
+
if (funcTools.length === 0 && requirement === 'required') {
|
|
110
|
+
throw new Error(`Google request invariant violation: toolUseRequirement=required but no tools are available (dialog=${requestContext.dialogSelfId})`);
|
|
111
|
+
}
|
|
112
|
+
const config = {
|
|
113
|
+
systemInstruction: { parts: [{ text: systemPrompt }] },
|
|
114
|
+
...(requirement !== 'none' && finalTools ? { tools: finalTools } : {}),
|
|
115
|
+
...(modelParams.temperature !== undefined && { temperature: modelParams.temperature }),
|
|
116
|
+
...(modelParams.top_p !== undefined && { topP: modelParams.top_p }),
|
|
117
|
+
...(abortSignal && { abortSignal }),
|
|
118
|
+
};
|
|
119
|
+
if (requirement === 'required' && finalTools) {
|
|
120
|
+
config.toolConfig = { functionCallingConfig: { mode: genai_1.FunctionCallingConfigMode.ANY } };
|
|
121
|
+
}
|
|
122
|
+
else if (requirement === 'none' && finalTools) {
|
|
123
|
+
config.toolConfig = { functionCallingConfig: { mode: genai_1.FunctionCallingConfigMode.NONE } };
|
|
124
|
+
}
|
|
125
|
+
else if (finalTools) {
|
|
126
|
+
config.toolConfig = { functionCallingConfig: { mode: genai_1.FunctionCallingConfigMode.AUTO } };
|
|
127
|
+
}
|
|
128
|
+
if (agent.model_params?.json_response) {
|
|
129
|
+
config.responseMimeType = 'application/json';
|
|
130
|
+
}
|
|
131
|
+
const supportsThinking = providerConfig.models[modelKey]?.supports_thinking;
|
|
132
|
+
const defaultThinking = providerConfig.models[modelKey]?.default_thinking;
|
|
133
|
+
const thinkingEnabled = modelParams.thinking !== undefined ? modelParams.thinking : defaultThinking;
|
|
134
|
+
if (supportsThinking) {
|
|
135
|
+
if (thinkingEnabled) {
|
|
136
|
+
config.thinkingConfig = {};
|
|
137
|
+
if (modelParams.thinking_budget !== undefined) {
|
|
138
|
+
config.thinkingConfig.thinkingBudget = modelParams.thinking_budget;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
config.thinkingConfig = {
|
|
143
|
+
thinkingBudget: 0,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
let state = 'idle';
|
|
148
|
+
let funcCallCounter = 0;
|
|
149
|
+
let finalUsage;
|
|
150
|
+
try {
|
|
151
|
+
const responseStream = await ai.models.generateContentStream({
|
|
152
|
+
model: modelKey,
|
|
153
|
+
contents: geminiContents,
|
|
154
|
+
config,
|
|
155
|
+
});
|
|
156
|
+
for await (const chunk of responseStream) {
|
|
157
|
+
if (abortSignal?.aborted) {
|
|
158
|
+
throw new Error('AbortError');
|
|
159
|
+
}
|
|
160
|
+
const candidates = chunk.candidates || [];
|
|
161
|
+
for (const candidate of candidates) {
|
|
162
|
+
const content = candidate.content;
|
|
163
|
+
if (!content)
|
|
164
|
+
continue;
|
|
165
|
+
const parts = content.parts || [];
|
|
166
|
+
for (const part of parts) {
|
|
167
|
+
if (part.thought === true) {
|
|
168
|
+
if (state === 'idle') {
|
|
169
|
+
state = 'thinking';
|
|
170
|
+
await receiver.thinkingStart();
|
|
171
|
+
}
|
|
172
|
+
else if (state === 'saying') {
|
|
173
|
+
state = 'thinking';
|
|
174
|
+
await receiver.sayingFinish();
|
|
175
|
+
await receiver.thinkingStart();
|
|
176
|
+
}
|
|
177
|
+
if (part.text) {
|
|
178
|
+
await receiver.thinkingChunk(part.text);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else if (part.text) {
|
|
182
|
+
if (state === 'idle') {
|
|
183
|
+
state = 'saying';
|
|
184
|
+
await receiver.sayingStart();
|
|
185
|
+
}
|
|
186
|
+
else if (state === 'thinking') {
|
|
187
|
+
state = 'saying';
|
|
188
|
+
await receiver.thinkingFinish();
|
|
189
|
+
await receiver.sayingStart();
|
|
190
|
+
}
|
|
191
|
+
await receiver.sayingChunk(part.text);
|
|
192
|
+
}
|
|
193
|
+
else if (part.functionCall) {
|
|
194
|
+
const name = part.functionCall.name;
|
|
195
|
+
if (!name) {
|
|
196
|
+
throw new Error(`Gemini functionCall missing name (callId=${part.functionCall.id || 'unknown'})`);
|
|
197
|
+
}
|
|
198
|
+
if (state === 'thinking') {
|
|
199
|
+
state = 'idle';
|
|
200
|
+
await receiver.thinkingFinish();
|
|
201
|
+
}
|
|
202
|
+
else if (state === 'saying') {
|
|
203
|
+
state = 'idle';
|
|
204
|
+
await receiver.sayingFinish();
|
|
205
|
+
}
|
|
206
|
+
const callId = part.functionCall.id || `call_gemini_${genseq}_${funcCallCounter++}`;
|
|
207
|
+
const argsStr = JSON.stringify(part.functionCall.args || {});
|
|
208
|
+
await receiver.funcCall(callId, name, argsStr);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (chunk.usageMetadata) {
|
|
213
|
+
finalUsage = {
|
|
214
|
+
kind: 'available',
|
|
215
|
+
promptTokens: chunk.usageMetadata.promptTokenCount ?? 0,
|
|
216
|
+
completionTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,
|
|
217
|
+
totalTokens: chunk.usageMetadata.totalTokenCount ?? undefined,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (state === 'thinking') {
|
|
222
|
+
await receiver.thinkingFinish();
|
|
223
|
+
}
|
|
224
|
+
else if (state === 'saying') {
|
|
225
|
+
await receiver.sayingFinish();
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
usage: finalUsage || { kind: 'unavailable' },
|
|
229
|
+
llmGenModel: modelKey,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
if (state === 'thinking') {
|
|
234
|
+
await receiver.thinkingFinish();
|
|
235
|
+
}
|
|
236
|
+
else if (state === 'saying') {
|
|
237
|
+
await receiver.sayingFinish();
|
|
238
|
+
}
|
|
239
|
+
throw err;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async genMoreMessages(providerConfig, agent, systemPrompt, funcTools, requestContext, context, genseq, abortSignal) {
|
|
243
|
+
const apiKey = process.env[providerConfig.apiKeyEnvVar];
|
|
244
|
+
if (!apiKey) {
|
|
245
|
+
throw new Error(`Google API key not found in env var ${providerConfig.apiKeyEnvVar}`);
|
|
246
|
+
}
|
|
247
|
+
const ai = new genai_1.GoogleGenAI({
|
|
248
|
+
apiKey,
|
|
249
|
+
...(providerConfig.baseUrl ? { httpOptions: { baseUrl: providerConfig.baseUrl } } : {}),
|
|
250
|
+
});
|
|
251
|
+
const modelKey = requestContext.modelKey || agent.model || 'gemini-2.5-flash';
|
|
252
|
+
const geminiContents = await buildGeminiRequestInput(context, requestContext, providerConfig);
|
|
253
|
+
const modelParams = agent.model_params?.google || {};
|
|
254
|
+
const geminiTools = [];
|
|
255
|
+
if (funcTools.length > 0) {
|
|
256
|
+
geminiTools.push({ functionDeclarations: funcTools.map(funcToolToGeminiFunction) });
|
|
257
|
+
}
|
|
258
|
+
if (modelParams.google_search_tool) {
|
|
259
|
+
geminiTools.push({ googleSearch: {} });
|
|
260
|
+
}
|
|
261
|
+
const finalTools = geminiTools.length > 0 ? geminiTools : undefined;
|
|
262
|
+
const requirement = requestContext.toolUseRequirement ?? 'auto';
|
|
263
|
+
if (funcTools.length === 0 && requirement === 'required') {
|
|
264
|
+
throw new Error(`Google request invariant violation: toolUseRequirement=required but no tools are available (dialog=${requestContext.dialogSelfId})`);
|
|
265
|
+
}
|
|
266
|
+
const config = {
|
|
267
|
+
systemInstruction: { parts: [{ text: systemPrompt }] },
|
|
268
|
+
...(requirement !== 'none' && finalTools ? { tools: finalTools } : {}),
|
|
269
|
+
...(modelParams.temperature !== undefined && { temperature: modelParams.temperature }),
|
|
270
|
+
...(modelParams.top_p !== undefined && { topP: modelParams.top_p }),
|
|
271
|
+
...(abortSignal && { abortSignal }),
|
|
272
|
+
};
|
|
273
|
+
if (requirement === 'required' && finalTools) {
|
|
274
|
+
config.toolConfig = { functionCallingConfig: { mode: genai_1.FunctionCallingConfigMode.ANY } };
|
|
275
|
+
}
|
|
276
|
+
else if (requirement === 'none' && finalTools) {
|
|
277
|
+
config.toolConfig = { functionCallingConfig: { mode: genai_1.FunctionCallingConfigMode.NONE } };
|
|
278
|
+
}
|
|
279
|
+
else if (finalTools) {
|
|
280
|
+
config.toolConfig = { functionCallingConfig: { mode: genai_1.FunctionCallingConfigMode.AUTO } };
|
|
281
|
+
}
|
|
282
|
+
if (agent.model_params?.json_response) {
|
|
283
|
+
config.responseMimeType = 'application/json';
|
|
284
|
+
}
|
|
285
|
+
const supportsThinking = providerConfig.models[modelKey]?.supports_thinking;
|
|
286
|
+
const defaultThinking = providerConfig.models[modelKey]?.default_thinking;
|
|
287
|
+
const thinkingEnabled = modelParams.thinking !== undefined ? modelParams.thinking : defaultThinking;
|
|
288
|
+
if (supportsThinking) {
|
|
289
|
+
if (thinkingEnabled) {
|
|
290
|
+
config.thinkingConfig = {};
|
|
291
|
+
if (modelParams.thinking_budget !== undefined) {
|
|
292
|
+
config.thinkingConfig.thinkingBudget = modelParams.thinking_budget;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
config.thinkingConfig = {
|
|
297
|
+
thinkingBudget: 0,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const response = await ai.models.generateContent({
|
|
302
|
+
model: modelKey,
|
|
303
|
+
contents: geminiContents,
|
|
304
|
+
config,
|
|
305
|
+
});
|
|
306
|
+
const outputs = [];
|
|
307
|
+
const messages = [];
|
|
308
|
+
let thinkingContent = '';
|
|
309
|
+
let sayingContent = '';
|
|
310
|
+
const candidate = response.candidates?.[0];
|
|
311
|
+
if (candidate && candidate.content) {
|
|
312
|
+
const parts = candidate.content.parts || [];
|
|
313
|
+
let funcCallCounter = 0;
|
|
314
|
+
for (const part of parts) {
|
|
315
|
+
if (part.thought === true) {
|
|
316
|
+
if (part.text) {
|
|
317
|
+
thinkingContent += part.text;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
else if (part.text) {
|
|
321
|
+
sayingContent += part.text;
|
|
322
|
+
}
|
|
323
|
+
else if (part.functionCall) {
|
|
324
|
+
const name = part.functionCall.name;
|
|
325
|
+
if (!name) {
|
|
326
|
+
throw new Error(`Gemini functionCall missing name (callId=${part.functionCall.id || 'unknown'})`);
|
|
327
|
+
}
|
|
328
|
+
const callId = part.functionCall.id || `call_gemini_${genseq}_${funcCallCounter++}`;
|
|
329
|
+
const argsStr = JSON.stringify(part.functionCall.args || {});
|
|
330
|
+
outputs.push({
|
|
331
|
+
kind: 'message',
|
|
332
|
+
message: {
|
|
333
|
+
type: 'func_call_msg',
|
|
334
|
+
role: 'assistant',
|
|
335
|
+
genseq,
|
|
336
|
+
id: callId,
|
|
337
|
+
name,
|
|
338
|
+
arguments: argsStr,
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
messages.push({
|
|
342
|
+
type: 'func_call_msg',
|
|
343
|
+
role: 'assistant',
|
|
344
|
+
genseq,
|
|
345
|
+
id: callId,
|
|
346
|
+
name: name,
|
|
347
|
+
arguments: argsStr,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (thinkingContent) {
|
|
353
|
+
messages.unshift({
|
|
354
|
+
type: 'thinking_msg',
|
|
355
|
+
role: 'assistant',
|
|
356
|
+
genseq,
|
|
357
|
+
content: thinkingContent,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
if (sayingContent) {
|
|
361
|
+
outputs.push({
|
|
362
|
+
kind: 'message',
|
|
363
|
+
message: {
|
|
364
|
+
type: 'saying_msg',
|
|
365
|
+
role: 'assistant',
|
|
366
|
+
genseq,
|
|
367
|
+
content: sayingContent,
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
messages.push({
|
|
371
|
+
type: 'saying_msg',
|
|
372
|
+
role: 'assistant',
|
|
373
|
+
genseq,
|
|
374
|
+
content: sayingContent,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
const usage = response.usageMetadata
|
|
378
|
+
? {
|
|
379
|
+
kind: 'available',
|
|
380
|
+
promptTokens: response.usageMetadata.promptTokenCount ?? 0,
|
|
381
|
+
completionTokens: response.usageMetadata.candidatesTokenCount ?? 0,
|
|
382
|
+
totalTokens: response.usageMetadata.totalTokenCount ?? undefined,
|
|
383
|
+
}
|
|
384
|
+
: { kind: 'unavailable' };
|
|
385
|
+
return {
|
|
386
|
+
messages,
|
|
387
|
+
outputs,
|
|
388
|
+
usage,
|
|
389
|
+
llmGenModel: modelKey,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
exports.GoogleGen = GoogleGen;
|
|
394
|
+
async function userLikeMessageToGeminiContentWithImages(msg, requestContext, providerConfig, allowedImageKeys, onUserImageIngest) {
|
|
395
|
+
const items = msg.contentItems;
|
|
396
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
397
|
+
return chatMessageToGeminiContent(msg);
|
|
398
|
+
}
|
|
399
|
+
const parts = [{ text: msg.content }];
|
|
400
|
+
const supportsImageInput = (0, tool_result_image_ingest_1.resolveModelImageInputSupport)(requestContext.modelKey === undefined
|
|
401
|
+
? undefined
|
|
402
|
+
: providerConfig?.models[requestContext.modelKey], true);
|
|
403
|
+
for (const [itemIndex, item] of items.entries()) {
|
|
404
|
+
if (item.type === 'input_text') {
|
|
405
|
+
parts.push({ text: item.text });
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
if (item.type === 'input_image') {
|
|
409
|
+
if (!supportsImageInput) {
|
|
410
|
+
if (onUserImageIngest) {
|
|
411
|
+
await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
|
|
412
|
+
requestContext,
|
|
413
|
+
...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
|
|
414
|
+
artifact: item.artifact,
|
|
415
|
+
disposition: 'filtered_model_unsupported',
|
|
416
|
+
providerPathLabel: 'Google GenAI path',
|
|
417
|
+
}));
|
|
418
|
+
}
|
|
419
|
+
parts.push({ text: `[image not sent: current model does not support image input]` });
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (!(0, artifacts_1.isVisionImageMimeType)(item.mimeType)) {
|
|
423
|
+
if (onUserImageIngest) {
|
|
424
|
+
await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
|
|
425
|
+
requestContext,
|
|
426
|
+
...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
|
|
427
|
+
artifact: item.artifact,
|
|
428
|
+
disposition: 'filtered_mime_unsupported',
|
|
429
|
+
mimeType: item.mimeType,
|
|
430
|
+
providerPathLabel: 'Google GenAI path',
|
|
431
|
+
}));
|
|
432
|
+
}
|
|
433
|
+
parts.push({ text: `[image not sent: unsupported mimeType=${item.mimeType}]` });
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
if (!allowedImageKeys.has((0, tool_result_image_ingest_1.buildImageBudgetKeyForContentItem)({ msg, itemIndex, artifact: item.artifact }))) {
|
|
437
|
+
if (onUserImageIngest) {
|
|
438
|
+
await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
|
|
439
|
+
requestContext,
|
|
440
|
+
...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
|
|
441
|
+
artifact: item.artifact,
|
|
442
|
+
disposition: 'filtered_size_limit',
|
|
443
|
+
detail: (0, tool_result_image_ingest_1.buildImageBudgetLimitDetail)({
|
|
444
|
+
byteLength: item.byteLength,
|
|
445
|
+
budgetBytes: tool_result_image_ingest_1.GEMINI_TOOL_RESULT_IMAGE_BUDGET_BYTES,
|
|
446
|
+
}),
|
|
447
|
+
providerPathLabel: 'Google GenAI path',
|
|
448
|
+
}));
|
|
449
|
+
}
|
|
450
|
+
parts.push({
|
|
451
|
+
text: `[image not sent: request image budget exceeded bytes=${String(item.byteLength)} budget=${String(tool_result_image_ingest_1.GEMINI_TOOL_RESULT_IMAGE_BUDGET_BYTES)}]`,
|
|
452
|
+
});
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
const bytesResult = await (0, tool_result_image_ingest_1.readToolResultImageBytesSafe)(item.artifact);
|
|
456
|
+
if (bytesResult.kind === 'missing') {
|
|
457
|
+
if (onUserImageIngest) {
|
|
458
|
+
await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
|
|
459
|
+
requestContext,
|
|
460
|
+
...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
|
|
461
|
+
artifact: item.artifact,
|
|
462
|
+
disposition: 'filtered_missing',
|
|
463
|
+
providerPathLabel: 'Google GenAI path',
|
|
464
|
+
}));
|
|
465
|
+
}
|
|
466
|
+
parts.push({ text: `[image missing: ${item.artifact.relPath}]` });
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
if (bytesResult.kind === 'read_failed') {
|
|
470
|
+
if (onUserImageIngest) {
|
|
471
|
+
await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
|
|
472
|
+
requestContext,
|
|
473
|
+
...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
|
|
474
|
+
artifact: item.artifact,
|
|
475
|
+
disposition: 'filtered_read_failed',
|
|
476
|
+
detail: bytesResult.detail,
|
|
477
|
+
providerPathLabel: 'Google GenAI path',
|
|
478
|
+
}));
|
|
479
|
+
}
|
|
480
|
+
parts.push({ text: `[image unreadable: ${item.artifact.relPath}]` });
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (onUserImageIngest) {
|
|
484
|
+
await onUserImageIngest((0, tool_result_image_ingest_1.buildUserImageIngest)({
|
|
485
|
+
requestContext,
|
|
486
|
+
...(msg.type === 'prompting_msg' ? { msgId: msg.msgId } : {}),
|
|
487
|
+
artifact: item.artifact,
|
|
488
|
+
disposition: 'fed_native',
|
|
489
|
+
providerPathLabel: 'Google GenAI path',
|
|
490
|
+
}));
|
|
491
|
+
}
|
|
492
|
+
parts.push({
|
|
493
|
+
inlineData: {
|
|
494
|
+
mimeType: item.mimeType,
|
|
495
|
+
data: bytesResult.bytes.toString('base64'),
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return {
|
|
502
|
+
role: 'user',
|
|
503
|
+
parts,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
async function funcResultToGeminiContentWithLimit(msg, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest) {
|
|
507
|
+
const items = msg.contentItems;
|
|
508
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
509
|
+
return chatMessageToGeminiContent(msg);
|
|
510
|
+
}
|
|
511
|
+
const parts = [];
|
|
512
|
+
let responseObj;
|
|
513
|
+
try {
|
|
514
|
+
responseObj = typeof msg.content === 'string' ? JSON.parse(msg.content) : msg.content;
|
|
515
|
+
if (typeof responseObj !== 'object' || responseObj === null || Array.isArray(responseObj)) {
|
|
516
|
+
responseObj = { result: msg.content };
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
catch (err) {
|
|
520
|
+
responseObj = { result: msg.content };
|
|
521
|
+
}
|
|
522
|
+
parts.push({
|
|
523
|
+
functionResponse: {
|
|
524
|
+
name: msg.name,
|
|
525
|
+
response: responseObj,
|
|
526
|
+
id: msg.id,
|
|
527
|
+
},
|
|
528
|
+
});
|
|
529
|
+
for (const [itemIndex, item] of items.entries()) {
|
|
530
|
+
if (item.type === 'input_text') {
|
|
531
|
+
parts.push({ text: item.text });
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
if (item.type === 'input_image') {
|
|
535
|
+
if (!supportsImageInput) {
|
|
536
|
+
if (onToolResultImageIngest) {
|
|
537
|
+
await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
|
|
538
|
+
requestContext,
|
|
539
|
+
toolCallId: msg.id,
|
|
540
|
+
toolName: msg.name,
|
|
541
|
+
artifact: item.artifact,
|
|
542
|
+
disposition: 'filtered_model_unsupported',
|
|
543
|
+
providerPathLabel: 'Google GenAI path',
|
|
544
|
+
}));
|
|
545
|
+
}
|
|
546
|
+
parts.push({ text: `[image not sent: current model does not support image input]` });
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
if (!(0, artifacts_1.isVisionImageMimeType)(item.mimeType)) {
|
|
550
|
+
if (onToolResultImageIngest) {
|
|
551
|
+
await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
|
|
552
|
+
requestContext,
|
|
553
|
+
toolCallId: msg.id,
|
|
554
|
+
toolName: msg.name,
|
|
555
|
+
artifact: item.artifact,
|
|
556
|
+
disposition: 'filtered_mime_unsupported',
|
|
557
|
+
mimeType: item.mimeType,
|
|
558
|
+
providerPathLabel: 'Google GenAI path',
|
|
559
|
+
}));
|
|
560
|
+
}
|
|
561
|
+
parts.push({ text: `[image omitted: unsupported mimeType=${item.mimeType}]` });
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
if (!allowedImageKeys.has((0, tool_result_image_ingest_1.buildImageBudgetKeyForContentItem)({ msg, itemIndex, artifact: item.artifact }))) {
|
|
565
|
+
if (onToolResultImageIngest) {
|
|
566
|
+
await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
|
|
567
|
+
requestContext,
|
|
568
|
+
toolCallId: msg.id,
|
|
569
|
+
toolName: msg.name,
|
|
570
|
+
artifact: item.artifact,
|
|
571
|
+
disposition: 'filtered_size_limit',
|
|
572
|
+
detail: (0, tool_result_image_ingest_1.buildImageBudgetLimitDetail)({
|
|
573
|
+
byteLength: item.byteLength,
|
|
574
|
+
budgetBytes: tool_result_image_ingest_1.GEMINI_TOOL_RESULT_IMAGE_BUDGET_BYTES,
|
|
575
|
+
}),
|
|
576
|
+
providerPathLabel: 'Google GenAI path',
|
|
577
|
+
}));
|
|
578
|
+
}
|
|
579
|
+
parts.push({
|
|
580
|
+
text: `[image omitted: request image budget exceeded bytes=${String(item.byteLength)} budget=${String(tool_result_image_ingest_1.GEMINI_TOOL_RESULT_IMAGE_BUDGET_BYTES)}]`,
|
|
581
|
+
});
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
const bytesResult = await (0, tool_result_image_ingest_1.readToolResultImageBytesSafe)(item.artifact);
|
|
585
|
+
if (bytesResult.kind === 'missing') {
|
|
586
|
+
if (onToolResultImageIngest) {
|
|
587
|
+
await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
|
|
588
|
+
requestContext,
|
|
589
|
+
toolCallId: msg.id,
|
|
590
|
+
toolName: msg.name,
|
|
591
|
+
artifact: item.artifact,
|
|
592
|
+
disposition: 'filtered_missing',
|
|
593
|
+
providerPathLabel: 'Google GenAI path',
|
|
594
|
+
}));
|
|
595
|
+
}
|
|
596
|
+
parts.push({ text: `[image missing: ${item.artifact.relPath}]` });
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
if (bytesResult.kind === 'read_failed') {
|
|
600
|
+
if (onToolResultImageIngest) {
|
|
601
|
+
await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
|
|
602
|
+
requestContext,
|
|
603
|
+
toolCallId: msg.id,
|
|
604
|
+
toolName: msg.name,
|
|
605
|
+
artifact: item.artifact,
|
|
606
|
+
disposition: 'filtered_read_failed',
|
|
607
|
+
detail: bytesResult.detail,
|
|
608
|
+
providerPathLabel: 'Google GenAI path',
|
|
609
|
+
}));
|
|
610
|
+
}
|
|
611
|
+
parts.push({ text: `[image unreadable: ${item.artifact.relPath}]` });
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
const bytes = bytesResult.bytes;
|
|
615
|
+
if (onToolResultImageIngest) {
|
|
616
|
+
await onToolResultImageIngest((0, tool_result_image_ingest_1.buildToolResultImageIngest)({
|
|
617
|
+
requestContext,
|
|
618
|
+
toolCallId: msg.id,
|
|
619
|
+
toolName: msg.name,
|
|
620
|
+
artifact: item.artifact,
|
|
621
|
+
disposition: 'fed_native',
|
|
622
|
+
providerPathLabel: 'Google GenAI path',
|
|
623
|
+
}));
|
|
624
|
+
}
|
|
625
|
+
parts.push({
|
|
626
|
+
inlineData: {
|
|
627
|
+
mimeType: item.mimeType,
|
|
628
|
+
data: bytes.toString('base64'),
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return {
|
|
635
|
+
role: 'user',
|
|
636
|
+
parts,
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
async function buildGeminiRequestInput(context, requestContext, providerConfig, onToolResultImageIngest, onUserImageIngest) {
|
|
640
|
+
const input = [];
|
|
641
|
+
const allowedImageKeys = (0, tool_result_image_ingest_1.selectLatestImagesWithinBudget)(context, tool_result_image_ingest_1.GEMINI_TOOL_RESULT_IMAGE_BUDGET_BYTES);
|
|
642
|
+
const supportsImageInput = (0, tool_result_image_ingest_1.resolveModelImageInputSupport)(requestContext.modelKey === undefined
|
|
643
|
+
? undefined
|
|
644
|
+
: providerConfig?.models[requestContext.modelKey], true);
|
|
645
|
+
for (const msg of context) {
|
|
646
|
+
if ((msg.type === 'prompting_msg' ||
|
|
647
|
+
msg.type === 'tellask_result_msg' ||
|
|
648
|
+
msg.type === 'tellask_carryover_msg') &&
|
|
649
|
+
Array.isArray(msg.contentItems) &&
|
|
650
|
+
msg.contentItems.length > 0) {
|
|
651
|
+
input.push(await userLikeMessageToGeminiContentWithImages(msg, requestContext, providerConfig, allowedImageKeys, onUserImageIngest));
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
if (msg.type === 'func_result_msg') {
|
|
655
|
+
input.push(await funcResultToGeminiContentWithLimit(msg, requestContext, allowedImageKeys, supportsImageInput, onToolResultImageIngest));
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
input.push(chatMessageToGeminiContent(msg));
|
|
659
|
+
}
|
|
660
|
+
return input;
|
|
661
|
+
}
|
|
662
|
+
function chatMessageToGeminiContent(msg) {
|
|
663
|
+
switch (msg.type) {
|
|
664
|
+
case 'environment_msg':
|
|
665
|
+
case 'prompting_msg':
|
|
666
|
+
case 'tellask_result_msg':
|
|
667
|
+
case 'tellask_carryover_msg':
|
|
668
|
+
return {
|
|
669
|
+
role: 'user',
|
|
670
|
+
parts: [{ text: msg.content }],
|
|
671
|
+
};
|
|
672
|
+
case 'transient_guide_msg':
|
|
673
|
+
case 'saying_msg':
|
|
674
|
+
return {
|
|
675
|
+
role: 'model',
|
|
676
|
+
parts: [{ text: msg.content }],
|
|
677
|
+
};
|
|
678
|
+
case 'thinking_msg':
|
|
679
|
+
return {
|
|
680
|
+
role: 'model',
|
|
681
|
+
parts: [{ thought: true, text: msg.content }],
|
|
682
|
+
};
|
|
683
|
+
case 'func_call_msg': {
|
|
684
|
+
const args = typeof msg.arguments === 'string' ? JSON.parse(msg.arguments) : msg.arguments;
|
|
685
|
+
return {
|
|
686
|
+
role: 'model',
|
|
687
|
+
parts: [
|
|
688
|
+
{
|
|
689
|
+
functionCall: {
|
|
690
|
+
name: msg.name,
|
|
691
|
+
args,
|
|
692
|
+
id: msg.id,
|
|
693
|
+
},
|
|
694
|
+
},
|
|
695
|
+
],
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
case 'func_result_msg': {
|
|
699
|
+
let responseObj;
|
|
700
|
+
try {
|
|
701
|
+
responseObj = typeof msg.content === 'string' ? JSON.parse(msg.content) : msg.content;
|
|
702
|
+
if (typeof responseObj !== 'object' || responseObj === null || Array.isArray(responseObj)) {
|
|
703
|
+
responseObj = { result: msg.content };
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
catch (err) {
|
|
707
|
+
responseObj = { result: msg.content };
|
|
708
|
+
}
|
|
709
|
+
return {
|
|
710
|
+
role: 'user',
|
|
711
|
+
parts: [
|
|
712
|
+
{
|
|
713
|
+
functionResponse: {
|
|
714
|
+
name: msg.name,
|
|
715
|
+
response: responseObj,
|
|
716
|
+
id: msg.id,
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
],
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
default: {
|
|
723
|
+
const _exhaustive = msg;
|
|
724
|
+
return _exhaustive;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
function funcToolToGeminiFunction(funcTool) {
|
|
729
|
+
const description = (0, i18n_text_1.getTextForLanguage)({ i18n: funcTool.descriptionI18n, fallback: funcTool.description }, (0, work_language_1.getWorkLanguage)());
|
|
730
|
+
return {
|
|
731
|
+
name: funcTool.name,
|
|
732
|
+
description,
|
|
733
|
+
parametersJsonSchema: funcTool.parameters,
|
|
734
|
+
};
|
|
735
|
+
}
|