@core-ai/google-genai 0.3.0 → 0.5.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.js +244 -31
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -16,6 +16,65 @@ import {
|
|
|
16
16
|
} from "@google/genai";
|
|
17
17
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
18
18
|
|
|
19
|
+
// src/model-capabilities.ts
|
|
20
|
+
var DEFAULT_CAPABILITIES = {
|
|
21
|
+
reasoning: {
|
|
22
|
+
thinkingParam: "thinkingBudget",
|
|
23
|
+
canDisableThinking: true
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var MODEL_CAPABILITIES = {
|
|
27
|
+
"gemini-3-pro": {
|
|
28
|
+
reasoning: {
|
|
29
|
+
thinkingParam: "thinkingLevel",
|
|
30
|
+
canDisableThinking: false
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"gemini-2.5-pro": {
|
|
34
|
+
reasoning: {
|
|
35
|
+
thinkingParam: "thinkingBudget",
|
|
36
|
+
canDisableThinking: false
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"gemini-2.5-flash": {
|
|
40
|
+
reasoning: {
|
|
41
|
+
thinkingParam: "thinkingBudget",
|
|
42
|
+
canDisableThinking: true
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"gemini-2.5-flash-lite": {
|
|
46
|
+
reasoning: {
|
|
47
|
+
thinkingParam: "thinkingBudget",
|
|
48
|
+
canDisableThinking: true
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
function getGoogleModelCapabilities(modelId) {
|
|
53
|
+
const normalizedModelId = normalizeModelId(modelId);
|
|
54
|
+
return MODEL_CAPABILITIES[normalizedModelId] ?? DEFAULT_CAPABILITIES;
|
|
55
|
+
}
|
|
56
|
+
function normalizeModelId(modelId) {
|
|
57
|
+
return modelId.replace(/-\d{8}$/, "");
|
|
58
|
+
}
|
|
59
|
+
function toGoogleThinkingLevel(effort) {
|
|
60
|
+
if (effort === "high" || effort === "max") {
|
|
61
|
+
return "HIGH";
|
|
62
|
+
}
|
|
63
|
+
return "LOW";
|
|
64
|
+
}
|
|
65
|
+
function toGoogleThinkingBudget(effort) {
|
|
66
|
+
if (effort === "minimal") {
|
|
67
|
+
return 1024;
|
|
68
|
+
}
|
|
69
|
+
if (effort === "low") {
|
|
70
|
+
return 4096;
|
|
71
|
+
}
|
|
72
|
+
if (effort === "medium") {
|
|
73
|
+
return 16384;
|
|
74
|
+
}
|
|
75
|
+
return 32768;
|
|
76
|
+
}
|
|
77
|
+
|
|
19
78
|
// src/object-utils.ts
|
|
20
79
|
function asObject(value) {
|
|
21
80
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
@@ -46,18 +105,31 @@ function convertMessages(messages) {
|
|
|
46
105
|
}
|
|
47
106
|
if (message.role === "assistant") {
|
|
48
107
|
const assistantParts = [];
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
108
|
+
for (const part of message.parts) {
|
|
109
|
+
if (part.type === "text") {
|
|
110
|
+
assistantParts.push({ text: part.text });
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (part.type === "tool-call") {
|
|
114
|
+
toolCallNameById.set(part.toolCall.id, part.toolCall.name);
|
|
115
|
+
assistantParts.push({
|
|
116
|
+
functionCall: {
|
|
117
|
+
id: part.toolCall.id,
|
|
118
|
+
name: part.toolCall.name,
|
|
119
|
+
args: part.toolCall.arguments
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const thoughtPart = {
|
|
125
|
+
text: part.text,
|
|
126
|
+
thought: true
|
|
127
|
+
};
|
|
128
|
+
const thoughtSignature = part.providerMetadata?.["thoughtSignature"];
|
|
129
|
+
if (typeof thoughtSignature === "string") {
|
|
130
|
+
thoughtPart["thoughtSignature"] = thoughtSignature;
|
|
131
|
+
}
|
|
132
|
+
assistantParts.push(thoughtPart);
|
|
61
133
|
}
|
|
62
134
|
contents.push({
|
|
63
135
|
role: "model",
|
|
@@ -177,6 +249,7 @@ function createStructuredOutputOptions(options) {
|
|
|
177
249
|
const toolName = getStructuredOutputToolName(options);
|
|
178
250
|
return {
|
|
179
251
|
messages: options.messages,
|
|
252
|
+
reasoning: options.reasoning,
|
|
180
253
|
tools: {
|
|
181
254
|
structured_output: {
|
|
182
255
|
name: toolName,
|
|
@@ -232,7 +305,8 @@ function createGenerateRequest(modelId, options) {
|
|
|
232
305
|
...options.config?.topP !== void 0 ? { topP: options.config.topP } : {},
|
|
233
306
|
...options.config?.stopSequences ? { stopSequences: options.config.stopSequences } : {},
|
|
234
307
|
...options.config?.frequencyPenalty !== void 0 ? { frequencyPenalty: options.config.frequencyPenalty } : {},
|
|
235
|
-
...options.config?.presencePenalty !== void 0 ? { presencePenalty: options.config.presencePenalty } : {}
|
|
308
|
+
...options.config?.presencePenalty !== void 0 ? { presencePenalty: options.config.presencePenalty } : {},
|
|
309
|
+
...mapReasoningToConfig(modelId, options)
|
|
236
310
|
}
|
|
237
311
|
};
|
|
238
312
|
const providerOptions = options.providerOptions;
|
|
@@ -250,31 +324,34 @@ function createGenerateRequest(modelId, options) {
|
|
|
250
324
|
};
|
|
251
325
|
}
|
|
252
326
|
function mapGenerateResponse(response) {
|
|
253
|
-
const
|
|
327
|
+
const parts = extractAssistantParts(response);
|
|
328
|
+
const toolCalls = parts.flatMap(
|
|
329
|
+
(part) => part.type === "tool-call" ? [part.toolCall] : []
|
|
330
|
+
);
|
|
331
|
+
const content = parts.flatMap((part) => part.type === "text" ? [part.text] : []).join("");
|
|
332
|
+
const reasoning = parts.flatMap((part) => part.type === "reasoning" ? [part.text] : []).join("");
|
|
254
333
|
const finishReason = mapFinishReason(
|
|
255
334
|
response.candidates?.[0]?.finishReason ?? void 0
|
|
256
335
|
);
|
|
257
336
|
if (!response.candidates?.[0]) {
|
|
258
337
|
return {
|
|
259
|
-
|
|
338
|
+
parts,
|
|
339
|
+
content: content.length > 0 ? content : null,
|
|
340
|
+
reasoning: reasoning.length > 0 ? reasoning : null,
|
|
260
341
|
toolCalls,
|
|
261
342
|
finishReason: toolCalls.length > 0 ? "tool-calls" : finishReason,
|
|
262
343
|
usage: mapUsage(response)
|
|
263
344
|
};
|
|
264
345
|
}
|
|
265
346
|
return {
|
|
266
|
-
|
|
347
|
+
parts,
|
|
348
|
+
content: content.length > 0 ? content : null,
|
|
349
|
+
reasoning: reasoning.length > 0 ? reasoning : null,
|
|
267
350
|
toolCalls,
|
|
268
351
|
finishReason: toolCalls.length > 0 ? "tool-calls" : finishReason,
|
|
269
352
|
usage: mapUsage(response)
|
|
270
353
|
};
|
|
271
354
|
}
|
|
272
|
-
function parseFunctionCalls(calls) {
|
|
273
|
-
if (!calls || calls.length === 0) {
|
|
274
|
-
return [];
|
|
275
|
-
}
|
|
276
|
-
return calls.map((call, index) => mapFunctionCall(call, index));
|
|
277
|
-
}
|
|
278
355
|
function mapFunctionCall(toolCall, index) {
|
|
279
356
|
return {
|
|
280
357
|
id: toolCall.id ?? `tool-${index}`,
|
|
@@ -298,22 +375,53 @@ async function* transformStream(stream) {
|
|
|
298
375
|
const bufferedToolCalls = /* @__PURE__ */ new Map();
|
|
299
376
|
let finishReason = "unknown";
|
|
300
377
|
let sawToolCalls = false;
|
|
378
|
+
let reasoningOpen = false;
|
|
301
379
|
let usage = {
|
|
302
380
|
inputTokens: 0,
|
|
303
381
|
outputTokens: 0,
|
|
304
|
-
|
|
305
|
-
|
|
382
|
+
inputTokenDetails: {
|
|
383
|
+
cacheReadTokens: 0,
|
|
384
|
+
cacheWriteTokens: 0
|
|
385
|
+
},
|
|
386
|
+
outputTokenDetails: {}
|
|
306
387
|
};
|
|
307
388
|
for await (const chunk of stream) {
|
|
308
389
|
usage = mapUsage(chunk, usage);
|
|
390
|
+
const reasoningDeltas = extractReasoningDeltas(chunk);
|
|
391
|
+
if (reasoningDeltas.length > 0) {
|
|
392
|
+
if (!reasoningOpen) {
|
|
393
|
+
reasoningOpen = true;
|
|
394
|
+
yield {
|
|
395
|
+
type: "reasoning-start"
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
for (const delta of reasoningDeltas) {
|
|
399
|
+
yield {
|
|
400
|
+
type: "reasoning-delta",
|
|
401
|
+
text: delta
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}
|
|
309
405
|
if (chunk.text) {
|
|
406
|
+
if (reasoningOpen) {
|
|
407
|
+
reasoningOpen = false;
|
|
408
|
+
yield {
|
|
409
|
+
type: "reasoning-end"
|
|
410
|
+
};
|
|
411
|
+
}
|
|
310
412
|
yield {
|
|
311
|
-
type: "
|
|
413
|
+
type: "text-delta",
|
|
312
414
|
text: chunk.text
|
|
313
415
|
};
|
|
314
416
|
}
|
|
315
417
|
const functionCalls = chunk.functionCalls ?? [];
|
|
316
418
|
if (functionCalls.length > 0) {
|
|
419
|
+
if (reasoningOpen) {
|
|
420
|
+
reasoningOpen = false;
|
|
421
|
+
yield {
|
|
422
|
+
type: "reasoning-end"
|
|
423
|
+
};
|
|
424
|
+
}
|
|
317
425
|
sawToolCalls = true;
|
|
318
426
|
for (const [index, functionCall] of functionCalls.entries()) {
|
|
319
427
|
const mappedCall = mapFunctionCall(functionCall, index);
|
|
@@ -356,6 +464,11 @@ async function* transformStream(stream) {
|
|
|
356
464
|
finishReason = candidateFinishReason;
|
|
357
465
|
}
|
|
358
466
|
}
|
|
467
|
+
if (reasoningOpen) {
|
|
468
|
+
yield {
|
|
469
|
+
type: "reasoning-end"
|
|
470
|
+
};
|
|
471
|
+
}
|
|
359
472
|
for (const toolCall of bufferedToolCalls.values()) {
|
|
360
473
|
yield {
|
|
361
474
|
type: "tool-call-end",
|
|
@@ -371,17 +484,117 @@ async function* transformStream(stream) {
|
|
|
371
484
|
usage
|
|
372
485
|
};
|
|
373
486
|
}
|
|
487
|
+
function mapReasoningToConfig(modelId, options) {
|
|
488
|
+
if (!options.reasoning) {
|
|
489
|
+
return {};
|
|
490
|
+
}
|
|
491
|
+
const capabilities = getGoogleModelCapabilities(modelId);
|
|
492
|
+
const providerConfig = asObject(options.providerOptions?.["config"]);
|
|
493
|
+
const providerThinkingConfig = asObject(providerConfig["thinkingConfig"]);
|
|
494
|
+
if (Object.keys(providerThinkingConfig).length > 0) {
|
|
495
|
+
return {};
|
|
496
|
+
}
|
|
497
|
+
if (capabilities.reasoning.thinkingParam === "thinkingLevel") {
|
|
498
|
+
return {
|
|
499
|
+
thinkingConfig: {
|
|
500
|
+
thinkingLevel: toGoogleThinkingLevel(options.reasoning.effort),
|
|
501
|
+
includeThoughts: true
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
return {
|
|
506
|
+
thinkingConfig: {
|
|
507
|
+
thinkingBudget: toGoogleThinkingBudget(options.reasoning.effort),
|
|
508
|
+
includeThoughts: true
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
function extractAssistantParts(response) {
|
|
513
|
+
const parts = [];
|
|
514
|
+
const seenToolCalls = /* @__PURE__ */ new Set();
|
|
515
|
+
const candidateParts = response.candidates?.[0]?.content?.parts ?? [];
|
|
516
|
+
for (const part of candidateParts) {
|
|
517
|
+
if (part.thought) {
|
|
518
|
+
const thoughtText = typeof part.text === "string" ? part.text : "";
|
|
519
|
+
if (thoughtText.length === 0) {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
const thoughtSignature = typeof part.thoughtSignature === "string" ? part.thoughtSignature : void 0;
|
|
523
|
+
parts.push({
|
|
524
|
+
type: "reasoning",
|
|
525
|
+
text: thoughtText,
|
|
526
|
+
...thoughtSignature ? {
|
|
527
|
+
providerMetadata: {
|
|
528
|
+
thoughtSignature
|
|
529
|
+
}
|
|
530
|
+
} : {}
|
|
531
|
+
});
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
if (part.functionCall) {
|
|
535
|
+
const toolCall = mapFunctionCall(part.functionCall, 0);
|
|
536
|
+
const key = `${toolCall.id}:${toolCall.name}`;
|
|
537
|
+
if (!seenToolCalls.has(key)) {
|
|
538
|
+
seenToolCalls.add(key);
|
|
539
|
+
parts.push({
|
|
540
|
+
type: "tool-call",
|
|
541
|
+
toolCall
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (typeof part.text === "string" && part.text.length > 0) {
|
|
547
|
+
parts.push({
|
|
548
|
+
type: "text",
|
|
549
|
+
text: part.text
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
for (const [index, functionCall] of (response.functionCalls ?? []).entries()) {
|
|
554
|
+
const toolCall = mapFunctionCall(functionCall, index);
|
|
555
|
+
const key = `${toolCall.id}:${toolCall.name}`;
|
|
556
|
+
if (seenToolCalls.has(key)) {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
seenToolCalls.add(key);
|
|
560
|
+
parts.push({
|
|
561
|
+
type: "tool-call",
|
|
562
|
+
toolCall
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
if (parts.length === 0 && response.text) {
|
|
566
|
+
parts.push({
|
|
567
|
+
type: "text",
|
|
568
|
+
text: response.text
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
return parts;
|
|
572
|
+
}
|
|
573
|
+
function extractReasoningDeltas(response) {
|
|
574
|
+
const candidateParts = response.candidates?.[0]?.content?.parts ?? [];
|
|
575
|
+
return candidateParts.flatMap((part) => {
|
|
576
|
+
if (!part.thought || typeof part.text !== "string" || part.text.length === 0) {
|
|
577
|
+
return [];
|
|
578
|
+
}
|
|
579
|
+
return [part.text];
|
|
580
|
+
});
|
|
581
|
+
}
|
|
374
582
|
function mapUsage(response, fallback) {
|
|
375
583
|
const inputTokens = response.usageMetadata?.promptTokenCount ?? fallback?.inputTokens ?? 0;
|
|
376
584
|
const textTokens = response.usageMetadata?.candidatesTokenCount ?? 0;
|
|
377
|
-
const reasoningTokens = response.usageMetadata?.thoughtsTokenCount ?? fallback?.reasoningTokens
|
|
378
|
-
const outputTokens = textTokens + reasoningTokens;
|
|
379
|
-
const
|
|
585
|
+
const reasoningTokens = response.usageMetadata?.thoughtsTokenCount ?? fallback?.outputTokenDetails?.reasoningTokens;
|
|
586
|
+
const outputTokens = textTokens + (reasoningTokens ?? 0);
|
|
587
|
+
const cacheReadTokens = response.usageMetadata?.cachedContentTokenCount ?? fallback?.inputTokenDetails.cacheReadTokens ?? 0;
|
|
380
588
|
return {
|
|
381
589
|
inputTokens,
|
|
382
590
|
outputTokens,
|
|
383
|
-
|
|
384
|
-
|
|
591
|
+
inputTokenDetails: {
|
|
592
|
+
cacheReadTokens,
|
|
593
|
+
cacheWriteTokens: 0
|
|
594
|
+
},
|
|
595
|
+
outputTokenDetails: {
|
|
596
|
+
...reasoningTokens !== void 0 ? { reasoningTokens } : {}
|
|
597
|
+
}
|
|
385
598
|
};
|
|
386
599
|
}
|
|
387
600
|
|
|
@@ -488,7 +701,7 @@ async function* transformStructuredOutputStream(stream, schema, provider, toolNa
|
|
|
488
701
|
let contentBuffer = "";
|
|
489
702
|
const toolArgumentDeltas = /* @__PURE__ */ new Map();
|
|
490
703
|
for await (const event of stream) {
|
|
491
|
-
if (event.type === "
|
|
704
|
+
if (event.type === "text-delta") {
|
|
492
705
|
contentBuffer += event.text;
|
|
493
706
|
yield {
|
|
494
707
|
type: "object-delta",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@core-ai/google-genai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Google GenAI provider package for @core-ai/core-ai",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Omnifact (https://omnifact.ai)",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"test:watch": "vitest"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@core-ai/core-ai": "^0.
|
|
47
|
+
"@core-ai/core-ai": "^0.5.0",
|
|
48
48
|
"@google/genai": "^1.42.0",
|
|
49
49
|
"zod-to-json-schema": "^3.25.1"
|
|
50
50
|
},
|