@demicodes/provider-google 0.9.0 → 0.10.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/dist/index.mjs +95 -15
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -107,6 +107,13 @@ function positiveInteger(value, field) {
|
|
|
107
107
|
//#region src/provider.ts
|
|
108
108
|
const DEFAULT_GOOGLE_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
|
|
109
109
|
const DEFAULT_MAX_OUTPUT_TOKENS = 32e3;
|
|
110
|
+
/**
|
|
111
|
+
* Marks a thought signature as this provider's own. Transcripts outlive a
|
|
112
|
+
* provider choice — a conversation can start on one and continue on another —
|
|
113
|
+
* and each provider's signature format is private to it, so an untagged (or
|
|
114
|
+
* foreign-tagged) signature must never be replayed as if it were ours.
|
|
115
|
+
*/
|
|
116
|
+
const SIGNATURE_TAG = "google:";
|
|
110
117
|
const DEFAULT_EFFORT_BUDGET_TOKENS = {
|
|
111
118
|
low: 4096,
|
|
112
119
|
medium: 16384,
|
|
@@ -260,6 +267,7 @@ function googleThinkingConfig(thinking, options) {
|
|
|
260
267
|
function inferenceItemsToGoogleContents(items) {
|
|
261
268
|
const contents = [];
|
|
262
269
|
const toolNames = /* @__PURE__ */ new Map();
|
|
270
|
+
const degraded = /* @__PURE__ */ new Set();
|
|
263
271
|
let pendingSignature = null;
|
|
264
272
|
const append = (role, parts) => {
|
|
265
273
|
if (parts.length === 0) return;
|
|
@@ -284,25 +292,33 @@ function inferenceItemsToGoogleContents(items) {
|
|
|
284
292
|
pendingSignature = null;
|
|
285
293
|
break;
|
|
286
294
|
case "assistant_thinking":
|
|
287
|
-
pendingSignature = item.signature;
|
|
295
|
+
pendingSignature = ownSignature(item.signature);
|
|
288
296
|
break;
|
|
289
297
|
case "assistant_redacted_thinking": break;
|
|
290
|
-
case "tool_use":
|
|
298
|
+
case "tool_use":
|
|
291
299
|
toolNames.set(item.toolUseId, item.toolName);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
300
|
+
if (!pendingSignature) {
|
|
301
|
+
degraded.add(item.toolUseId);
|
|
302
|
+
append("model", [{ text: `[called ${item.toolName} with ${stringifyToolInput(item.input)}]` }]);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
append("model", [{
|
|
306
|
+
functionCall: {
|
|
307
|
+
name: item.toolName,
|
|
308
|
+
args: item.input ?? {},
|
|
309
|
+
id: item.toolUseId
|
|
310
|
+
},
|
|
311
|
+
thoughtSignature: pendingSignature
|
|
312
|
+
}]);
|
|
299
313
|
pendingSignature = null;
|
|
300
314
|
break;
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
315
|
+
case "tool_result": {
|
|
316
|
+
const name = toolNames.get(item.toolUseId) ?? "tool";
|
|
317
|
+
if (degraded.has(item.toolUseId)) append("user", [{ text: `[${name} returned] ${toolResultText(item.output)}` }]);
|
|
318
|
+
else append("user", toolResultToGoogle(item.toolUseId, name, item.output));
|
|
304
319
|
pendingSignature = null;
|
|
305
320
|
break;
|
|
321
|
+
}
|
|
306
322
|
}
|
|
307
323
|
return contents;
|
|
308
324
|
}
|
|
@@ -339,11 +355,75 @@ function toolResultToGoogle(toolUseId, toolName, output) {
|
|
|
339
355
|
response: { output: text }
|
|
340
356
|
} }, ...media];
|
|
341
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* Keywords Gemini's function-declaration schema accepts. Its `parameters` is an
|
|
360
|
+
* OpenAPI 3.0 subset, not JSON Schema, and it rejects the whole request on the
|
|
361
|
+
* first keyword it does not know rather than ignoring it:
|
|
362
|
+
*
|
|
363
|
+
* Invalid JSON payload received. Unknown name "additionalProperties"
|
|
364
|
+
* at 'tools[0].function_declarations[3].parameters': Cannot find field.
|
|
365
|
+
*
|
|
366
|
+
* `additionalProperties: false` is exactly what a careful tool author writes —
|
|
367
|
+
* demi's own shell tools all do — so passing schemas through verbatim breaks
|
|
368
|
+
* every agent that uses them.
|
|
369
|
+
*/
|
|
370
|
+
const GOOGLE_SCHEMA_KEYS = /* @__PURE__ */ new Set([
|
|
371
|
+
"type",
|
|
372
|
+
"format",
|
|
373
|
+
"title",
|
|
374
|
+
"description",
|
|
375
|
+
"nullable",
|
|
376
|
+
"enum",
|
|
377
|
+
"items",
|
|
378
|
+
"properties",
|
|
379
|
+
"required",
|
|
380
|
+
"minimum",
|
|
381
|
+
"maximum",
|
|
382
|
+
"minItems",
|
|
383
|
+
"maxItems",
|
|
384
|
+
"anyOf",
|
|
385
|
+
"default"
|
|
386
|
+
]);
|
|
387
|
+
/**
|
|
388
|
+
* Drops keywords Gemini does not accept, recursing through the containers that
|
|
389
|
+
* hold nested schemas. Dropping is the right failure mode here: the discarded
|
|
390
|
+
* keywords ($schema, additionalProperties, allOf…) constrain what the model may
|
|
391
|
+
* send, and a slightly loose tool schema still validates on demi's side, where
|
|
392
|
+
* the command parses its own input anyway. Rejecting or erroring would break
|
|
393
|
+
* callers over a constraint the transport merely cannot express.
|
|
394
|
+
*/
|
|
395
|
+
function toGoogleSchema(schema) {
|
|
396
|
+
if (!isRecord(schema)) return {};
|
|
397
|
+
const out = {};
|
|
398
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
399
|
+
if (!GOOGLE_SCHEMA_KEYS.has(key)) continue;
|
|
400
|
+
if (key === "properties" && isRecord(value)) out.properties = Object.fromEntries(Object.entries(value).map(([name, child]) => [name, toGoogleSchema(child)]));
|
|
401
|
+
else if (key === "items") out.items = toGoogleSchema(value);
|
|
402
|
+
else if (key === "anyOf" && Array.isArray(value)) out.anyOf = value.map(toGoogleSchema);
|
|
403
|
+
else out[key] = value;
|
|
404
|
+
}
|
|
405
|
+
return out;
|
|
406
|
+
}
|
|
407
|
+
/** Unwraps a signature this provider issued; anything else is not ours to replay. */
|
|
408
|
+
function ownSignature(signature) {
|
|
409
|
+
if (!signature || !signature.startsWith(SIGNATURE_TAG)) return null;
|
|
410
|
+
return signature.slice(7) || null;
|
|
411
|
+
}
|
|
412
|
+
function stringifyToolInput(input) {
|
|
413
|
+
try {
|
|
414
|
+
return JSON.stringify(input ?? {}) ?? "{}";
|
|
415
|
+
} catch {
|
|
416
|
+
return "{}";
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function toolResultText(output) {
|
|
420
|
+
return output.map((block) => block.type === "text" ? block.text : `[${block.source.mediaType}]`).join("\n");
|
|
421
|
+
}
|
|
342
422
|
function toolToGoogleFunctionDeclaration(tool) {
|
|
343
423
|
return {
|
|
344
424
|
name: tool.name,
|
|
345
425
|
description: tool.description,
|
|
346
|
-
parameters: tool.inputSchema
|
|
426
|
+
parameters: toGoogleSchema(tool.inputSchema)
|
|
347
427
|
};
|
|
348
428
|
}
|
|
349
429
|
async function* mapGoogleContentStream(events, signal) {
|
|
@@ -382,7 +462,7 @@ async function* mapGoogleContentStream(events, signal) {
|
|
|
382
462
|
thinkingOpen = true;
|
|
383
463
|
yield {
|
|
384
464
|
type: "thinking_signature",
|
|
385
|
-
signature
|
|
465
|
+
signature: `${SIGNATURE_TAG}${signature}`
|
|
386
466
|
};
|
|
387
467
|
}
|
|
388
468
|
yield {
|
|
@@ -409,7 +489,7 @@ async function* mapGoogleContentStream(events, signal) {
|
|
|
409
489
|
const signature = stringOrNull(part.thoughtSignature);
|
|
410
490
|
if (signature && thinkingOpen) yield {
|
|
411
491
|
type: "thinking_signature",
|
|
412
|
-
signature
|
|
492
|
+
signature: `${SIGNATURE_TAG}${signature}`
|
|
413
493
|
};
|
|
414
494
|
if (text) {
|
|
415
495
|
thinkingOpen = false;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@demicodes/provider-google",
|
|
3
3
|
"description": "Google Gemini API provider adapter for Demi.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.10.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@demicodes/core": "^0.
|
|
15
|
-
"@demicodes/provider": "^0.
|
|
16
|
-
"@demicodes/utils": "^0.
|
|
14
|
+
"@demicodes/core": "^0.10.0",
|
|
15
|
+
"@demicodes/provider": "^0.10.0",
|
|
16
|
+
"@demicodes/utils": "^0.10.0"
|
|
17
17
|
},
|
|
18
18
|
"license": "Apache-2.0",
|
|
19
19
|
"main": "./dist/index.mjs",
|