@core-ai/google-genai 0.4.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 +233 -30
- 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,6 +375,7 @@ 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,
|
|
@@ -305,20 +383,45 @@ async function* transformStream(stream) {
|
|
|
305
383
|
cacheReadTokens: 0,
|
|
306
384
|
cacheWriteTokens: 0
|
|
307
385
|
},
|
|
308
|
-
outputTokenDetails: {
|
|
309
|
-
reasoningTokens: 0
|
|
310
|
-
}
|
|
386
|
+
outputTokenDetails: {}
|
|
311
387
|
};
|
|
312
388
|
for await (const chunk of stream) {
|
|
313
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
|
+
}
|
|
314
405
|
if (chunk.text) {
|
|
406
|
+
if (reasoningOpen) {
|
|
407
|
+
reasoningOpen = false;
|
|
408
|
+
yield {
|
|
409
|
+
type: "reasoning-end"
|
|
410
|
+
};
|
|
411
|
+
}
|
|
315
412
|
yield {
|
|
316
|
-
type: "
|
|
413
|
+
type: "text-delta",
|
|
317
414
|
text: chunk.text
|
|
318
415
|
};
|
|
319
416
|
}
|
|
320
417
|
const functionCalls = chunk.functionCalls ?? [];
|
|
321
418
|
if (functionCalls.length > 0) {
|
|
419
|
+
if (reasoningOpen) {
|
|
420
|
+
reasoningOpen = false;
|
|
421
|
+
yield {
|
|
422
|
+
type: "reasoning-end"
|
|
423
|
+
};
|
|
424
|
+
}
|
|
322
425
|
sawToolCalls = true;
|
|
323
426
|
for (const [index, functionCall] of functionCalls.entries()) {
|
|
324
427
|
const mappedCall = mapFunctionCall(functionCall, index);
|
|
@@ -361,6 +464,11 @@ async function* transformStream(stream) {
|
|
|
361
464
|
finishReason = candidateFinishReason;
|
|
362
465
|
}
|
|
363
466
|
}
|
|
467
|
+
if (reasoningOpen) {
|
|
468
|
+
yield {
|
|
469
|
+
type: "reasoning-end"
|
|
470
|
+
};
|
|
471
|
+
}
|
|
364
472
|
for (const toolCall of bufferedToolCalls.values()) {
|
|
365
473
|
yield {
|
|
366
474
|
type: "tool-call-end",
|
|
@@ -376,11 +484,106 @@ async function* transformStream(stream) {
|
|
|
376
484
|
usage
|
|
377
485
|
};
|
|
378
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
|
+
}
|
|
379
582
|
function mapUsage(response, fallback) {
|
|
380
583
|
const inputTokens = response.usageMetadata?.promptTokenCount ?? fallback?.inputTokens ?? 0;
|
|
381
584
|
const textTokens = response.usageMetadata?.candidatesTokenCount ?? 0;
|
|
382
|
-
const reasoningTokens = response.usageMetadata?.thoughtsTokenCount ?? fallback?.outputTokenDetails
|
|
383
|
-
const outputTokens = textTokens + reasoningTokens;
|
|
585
|
+
const reasoningTokens = response.usageMetadata?.thoughtsTokenCount ?? fallback?.outputTokenDetails?.reasoningTokens;
|
|
586
|
+
const outputTokens = textTokens + (reasoningTokens ?? 0);
|
|
384
587
|
const cacheReadTokens = response.usageMetadata?.cachedContentTokenCount ?? fallback?.inputTokenDetails.cacheReadTokens ?? 0;
|
|
385
588
|
return {
|
|
386
589
|
inputTokens,
|
|
@@ -390,7 +593,7 @@ function mapUsage(response, fallback) {
|
|
|
390
593
|
cacheWriteTokens: 0
|
|
391
594
|
},
|
|
392
595
|
outputTokenDetails: {
|
|
393
|
-
reasoningTokens
|
|
596
|
+
...reasoningTokens !== void 0 ? { reasoningTokens } : {}
|
|
394
597
|
}
|
|
395
598
|
};
|
|
396
599
|
}
|
|
@@ -498,7 +701,7 @@ async function* transformStructuredOutputStream(stream, schema, provider, toolNa
|
|
|
498
701
|
let contentBuffer = "";
|
|
499
702
|
const toolArgumentDeltas = /* @__PURE__ */ new Map();
|
|
500
703
|
for await (const event of stream) {
|
|
501
|
-
if (event.type === "
|
|
704
|
+
if (event.type === "text-delta") {
|
|
502
705
|
contentBuffer += event.text;
|
|
503
706
|
yield {
|
|
504
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
|
},
|