@core-ai/google-genai 0.2.0 → 0.3.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 +2 -0
- package/dist/index.js +280 -87
- package/package.json +4 -4
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,15 +2,31 @@
|
|
|
2
2
|
import { GoogleGenAI } from "@google/genai";
|
|
3
3
|
|
|
4
4
|
// src/chat-model.ts
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
StructuredOutputNoObjectGeneratedError,
|
|
7
|
+
StructuredOutputParseError,
|
|
8
|
+
StructuredOutputValidationError,
|
|
9
|
+
createObjectStreamResult,
|
|
10
|
+
createStreamResult
|
|
11
|
+
} from "@core-ai/core-ai";
|
|
6
12
|
|
|
7
13
|
// src/chat-adapter.ts
|
|
8
14
|
import {
|
|
9
|
-
ApiError,
|
|
10
15
|
FunctionCallingConfigMode
|
|
11
16
|
} from "@google/genai";
|
|
12
17
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
13
|
-
|
|
18
|
+
|
|
19
|
+
// src/object-utils.ts
|
|
20
|
+
function asObject(value) {
|
|
21
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/chat-adapter.ts
|
|
28
|
+
var DEFAULT_STRUCTURED_OUTPUT_TOOL_NAME = "core_ai_generate_object";
|
|
29
|
+
var DEFAULT_STRUCTURED_OUTPUT_TOOL_DESCRIPTION = "Return a JSON object that matches the requested schema.";
|
|
14
30
|
function convertMessages(messages) {
|
|
15
31
|
const systemParts = [];
|
|
16
32
|
const contents = [];
|
|
@@ -101,17 +117,17 @@ function convertUserContentPart(part) {
|
|
|
101
117
|
};
|
|
102
118
|
}
|
|
103
119
|
function convertTools(tools) {
|
|
104
|
-
const functionDeclarations = Object.values(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
);
|
|
120
|
+
const functionDeclarations = Object.values(
|
|
121
|
+
tools
|
|
122
|
+
).map((tool) => {
|
|
123
|
+
const schema = zodToJsonSchema(tool.parameters);
|
|
124
|
+
const { $schema: _schema, ...parametersJsonSchema } = schema;
|
|
125
|
+
return {
|
|
126
|
+
name: tool.name,
|
|
127
|
+
description: tool.description,
|
|
128
|
+
parametersJsonSchema
|
|
129
|
+
};
|
|
130
|
+
});
|
|
115
131
|
if (functionDeclarations.length === 0) {
|
|
116
132
|
return [];
|
|
117
133
|
}
|
|
@@ -150,6 +166,33 @@ function convertToolChoice(choice) {
|
|
|
150
166
|
}
|
|
151
167
|
};
|
|
152
168
|
}
|
|
169
|
+
function getStructuredOutputToolName(options) {
|
|
170
|
+
const trimmedName = options.schemaName?.trim();
|
|
171
|
+
if (trimmedName && trimmedName.length > 0) {
|
|
172
|
+
return trimmedName;
|
|
173
|
+
}
|
|
174
|
+
return DEFAULT_STRUCTURED_OUTPUT_TOOL_NAME;
|
|
175
|
+
}
|
|
176
|
+
function createStructuredOutputOptions(options) {
|
|
177
|
+
const toolName = getStructuredOutputToolName(options);
|
|
178
|
+
return {
|
|
179
|
+
messages: options.messages,
|
|
180
|
+
tools: {
|
|
181
|
+
structured_output: {
|
|
182
|
+
name: toolName,
|
|
183
|
+
description: options.schemaDescription ?? DEFAULT_STRUCTURED_OUTPUT_TOOL_DESCRIPTION,
|
|
184
|
+
parameters: options.schema
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
toolChoice: {
|
|
188
|
+
type: "tool",
|
|
189
|
+
toolName
|
|
190
|
+
},
|
|
191
|
+
config: options.config,
|
|
192
|
+
providerOptions: options.providerOptions,
|
|
193
|
+
signal: options.signal
|
|
194
|
+
};
|
|
195
|
+
}
|
|
153
196
|
function isToolResultContent(content) {
|
|
154
197
|
if (content.role !== "user" || !content.parts || content.parts.length === 0) {
|
|
155
198
|
return false;
|
|
@@ -282,7 +325,9 @@ async function* transformStream(stream) {
|
|
|
282
325
|
toolCallId: mappedCall.id,
|
|
283
326
|
toolName: mappedCall.name
|
|
284
327
|
};
|
|
285
|
-
const serializedArguments = JSON.stringify(
|
|
328
|
+
const serializedArguments = JSON.stringify(
|
|
329
|
+
mappedCall.arguments
|
|
330
|
+
);
|
|
286
331
|
if (serializedArguments !== "{}") {
|
|
287
332
|
yield {
|
|
288
333
|
type: "tool-call-delta",
|
|
@@ -339,13 +384,11 @@ function mapUsage(response, fallback) {
|
|
|
339
384
|
totalTokens
|
|
340
385
|
};
|
|
341
386
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
function wrapError(error) {
|
|
387
|
+
|
|
388
|
+
// src/google-error.ts
|
|
389
|
+
import { ApiError } from "@google/genai";
|
|
390
|
+
import { ProviderError } from "@core-ai/core-ai";
|
|
391
|
+
function wrapGoogleError(error) {
|
|
349
392
|
if (error instanceof ApiError) {
|
|
350
393
|
return new ProviderError(error.message, "google", error.status, error);
|
|
351
394
|
}
|
|
@@ -359,33 +402,215 @@ function wrapError(error) {
|
|
|
359
402
|
|
|
360
403
|
// src/chat-model.ts
|
|
361
404
|
function createGoogleGenAIChatModel(client, modelId) {
|
|
405
|
+
const provider = "google";
|
|
406
|
+
async function callGenerateContentApi(request) {
|
|
407
|
+
try {
|
|
408
|
+
return await client.models.generateContent(request);
|
|
409
|
+
} catch (error) {
|
|
410
|
+
throw wrapGoogleError(error);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
async function callGenerateContentStreamApi(request) {
|
|
414
|
+
try {
|
|
415
|
+
return await client.models.generateContentStream(request);
|
|
416
|
+
} catch (error) {
|
|
417
|
+
throw wrapGoogleError(error);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async function generateChat(options) {
|
|
421
|
+
const request = createGenerateRequest(modelId, options);
|
|
422
|
+
const response = await callGenerateContentApi(request);
|
|
423
|
+
return mapGenerateResponse(response);
|
|
424
|
+
}
|
|
425
|
+
async function streamChat(options) {
|
|
426
|
+
const request = createGenerateRequest(modelId, options);
|
|
427
|
+
const stream = await callGenerateContentStreamApi(request);
|
|
428
|
+
return createStreamResult(transformStream(stream));
|
|
429
|
+
}
|
|
362
430
|
return {
|
|
363
|
-
provider
|
|
431
|
+
provider,
|
|
364
432
|
modelId,
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
433
|
+
generate: generateChat,
|
|
434
|
+
stream: streamChat,
|
|
435
|
+
async generateObject(options) {
|
|
436
|
+
const structuredOptions = createStructuredOutputOptions(options);
|
|
437
|
+
const result = await generateChat(structuredOptions);
|
|
438
|
+
const toolName = getStructuredOutputToolName(options);
|
|
439
|
+
const object = extractStructuredObject(
|
|
440
|
+
result,
|
|
441
|
+
options.schema,
|
|
442
|
+
provider,
|
|
443
|
+
toolName
|
|
444
|
+
);
|
|
445
|
+
return {
|
|
446
|
+
object,
|
|
447
|
+
finishReason: result.finishReason,
|
|
448
|
+
usage: result.usage
|
|
449
|
+
};
|
|
373
450
|
},
|
|
374
|
-
async
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
451
|
+
async streamObject(options) {
|
|
452
|
+
const structuredOptions = createStructuredOutputOptions(options);
|
|
453
|
+
const stream = await streamChat(structuredOptions);
|
|
454
|
+
const toolName = getStructuredOutputToolName(options);
|
|
455
|
+
return createObjectStreamResult(
|
|
456
|
+
transformStructuredOutputStream(
|
|
457
|
+
stream,
|
|
458
|
+
options.schema,
|
|
459
|
+
provider,
|
|
460
|
+
toolName
|
|
461
|
+
)
|
|
462
|
+
);
|
|
382
463
|
}
|
|
383
464
|
};
|
|
384
465
|
}
|
|
466
|
+
function extractStructuredObject(result, schema, provider, toolName) {
|
|
467
|
+
const structuredToolCall = result.toolCalls.find(
|
|
468
|
+
(toolCall) => toolCall.name === toolName
|
|
469
|
+
);
|
|
470
|
+
if (structuredToolCall) {
|
|
471
|
+
return validateStructuredToolArguments(
|
|
472
|
+
schema,
|
|
473
|
+
structuredToolCall.arguments,
|
|
474
|
+
provider
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
const rawOutput = result.content?.trim();
|
|
478
|
+
if (rawOutput && rawOutput.length > 0) {
|
|
479
|
+
return parseAndValidateStructuredPayload(schema, rawOutput, provider);
|
|
480
|
+
}
|
|
481
|
+
throw new StructuredOutputNoObjectGeneratedError(
|
|
482
|
+
"model did not emit a structured object payload",
|
|
483
|
+
provider
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
async function* transformStructuredOutputStream(stream, schema, provider, toolName) {
|
|
487
|
+
let validatedObject;
|
|
488
|
+
let contentBuffer = "";
|
|
489
|
+
const toolArgumentDeltas = /* @__PURE__ */ new Map();
|
|
490
|
+
for await (const event of stream) {
|
|
491
|
+
if (event.type === "content-delta") {
|
|
492
|
+
contentBuffer += event.text;
|
|
493
|
+
yield {
|
|
494
|
+
type: "object-delta",
|
|
495
|
+
text: event.text
|
|
496
|
+
};
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
if (event.type === "tool-call-delta") {
|
|
500
|
+
const previous = toolArgumentDeltas.get(event.toolCallId) ?? "";
|
|
501
|
+
toolArgumentDeltas.set(
|
|
502
|
+
event.toolCallId,
|
|
503
|
+
`${previous}${event.argumentsDelta}`
|
|
504
|
+
);
|
|
505
|
+
yield {
|
|
506
|
+
type: "object-delta",
|
|
507
|
+
text: event.argumentsDelta
|
|
508
|
+
};
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
if (event.type === "tool-call-end" && event.toolCall.name === toolName) {
|
|
512
|
+
validatedObject = validateStructuredToolArguments(
|
|
513
|
+
schema,
|
|
514
|
+
event.toolCall.arguments,
|
|
515
|
+
provider
|
|
516
|
+
);
|
|
517
|
+
yield {
|
|
518
|
+
type: "object",
|
|
519
|
+
object: validatedObject
|
|
520
|
+
};
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
if (event.type === "finish") {
|
|
524
|
+
if (validatedObject === void 0) {
|
|
525
|
+
const fallbackPayload = getFallbackStructuredPayload(
|
|
526
|
+
contentBuffer,
|
|
527
|
+
toolArgumentDeltas
|
|
528
|
+
);
|
|
529
|
+
if (!fallbackPayload) {
|
|
530
|
+
throw new StructuredOutputNoObjectGeneratedError(
|
|
531
|
+
"structured output stream ended without an object payload",
|
|
532
|
+
provider
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
validatedObject = parseAndValidateStructuredPayload(
|
|
536
|
+
schema,
|
|
537
|
+
fallbackPayload,
|
|
538
|
+
provider
|
|
539
|
+
);
|
|
540
|
+
yield {
|
|
541
|
+
type: "object",
|
|
542
|
+
object: validatedObject
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
yield {
|
|
546
|
+
type: "finish",
|
|
547
|
+
finishReason: event.finishReason,
|
|
548
|
+
usage: event.usage
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
function getFallbackStructuredPayload(contentBuffer, toolArgumentDeltas) {
|
|
554
|
+
for (const delta of toolArgumentDeltas.values()) {
|
|
555
|
+
const trimmed = delta.trim();
|
|
556
|
+
if (trimmed.length > 0) {
|
|
557
|
+
return trimmed;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
const trimmedContent = contentBuffer.trim();
|
|
561
|
+
if (trimmedContent.length > 0) {
|
|
562
|
+
return trimmedContent;
|
|
563
|
+
}
|
|
564
|
+
return void 0;
|
|
565
|
+
}
|
|
566
|
+
function validateStructuredToolArguments(schema, toolArguments, provider) {
|
|
567
|
+
return validateStructuredObject(
|
|
568
|
+
schema,
|
|
569
|
+
toolArguments,
|
|
570
|
+
provider,
|
|
571
|
+
JSON.stringify(toolArguments)
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
function parseAndValidateStructuredPayload(schema, rawPayload, provider) {
|
|
575
|
+
const parsedPayload = parseJson(rawPayload, provider);
|
|
576
|
+
return validateStructuredObject(schema, parsedPayload, provider, rawPayload);
|
|
577
|
+
}
|
|
578
|
+
function parseJson(rawOutput, provider) {
|
|
579
|
+
try {
|
|
580
|
+
return JSON.parse(rawOutput);
|
|
581
|
+
} catch (error) {
|
|
582
|
+
throw new StructuredOutputParseError(
|
|
583
|
+
"failed to parse structured output as JSON",
|
|
584
|
+
provider,
|
|
585
|
+
{
|
|
586
|
+
rawOutput,
|
|
587
|
+
cause: error
|
|
588
|
+
}
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function validateStructuredObject(schema, value, provider, rawOutput) {
|
|
593
|
+
const parsed = schema.safeParse(value);
|
|
594
|
+
if (parsed.success) {
|
|
595
|
+
return parsed.data;
|
|
596
|
+
}
|
|
597
|
+
throw new StructuredOutputValidationError(
|
|
598
|
+
"structured output does not match schema",
|
|
599
|
+
provider,
|
|
600
|
+
formatZodIssues(parsed.error.issues),
|
|
601
|
+
{
|
|
602
|
+
rawOutput
|
|
603
|
+
}
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
function formatZodIssues(issues) {
|
|
607
|
+
return issues.map((issue) => {
|
|
608
|
+
const path = issue.path.length > 0 ? issue.path.map((segment) => String(segment)).join(".") : "<root>";
|
|
609
|
+
return `${path}: ${issue.message}`;
|
|
610
|
+
});
|
|
611
|
+
}
|
|
385
612
|
|
|
386
613
|
// src/embedding-model.ts
|
|
387
|
-
import { ApiError as ApiError2 } from "@google/genai";
|
|
388
|
-
import { ProviderError as ProviderError2 } from "@core-ai/core-ai";
|
|
389
614
|
function createGoogleGenAIEmbeddingModel(client, modelId) {
|
|
390
615
|
return {
|
|
391
616
|
provider: "google",
|
|
@@ -407,48 +632,33 @@ function createGoogleGenAIEmbeddingModel(client, modelId) {
|
|
|
407
632
|
...providerOptions,
|
|
408
633
|
config: {
|
|
409
634
|
...baseRequest.config,
|
|
410
|
-
...
|
|
635
|
+
...asObject(providerOptions["config"])
|
|
411
636
|
}
|
|
412
637
|
} : baseRequest;
|
|
413
638
|
const response = await client.models.embedContent(request);
|
|
639
|
+
const tokenCounts = (response.embeddings ?? []).map((item) => item.statistics?.tokenCount).filter(
|
|
640
|
+
(tokenCount) => typeof tokenCount === "number"
|
|
641
|
+
);
|
|
642
|
+
const usage = tokenCounts.length > 0 ? {
|
|
643
|
+
inputTokens: tokenCounts.reduce(
|
|
644
|
+
(total, tokenCount) => total + tokenCount,
|
|
645
|
+
0
|
|
646
|
+
)
|
|
647
|
+
} : void 0;
|
|
414
648
|
return {
|
|
415
649
|
embeddings: (response.embeddings ?? []).map(
|
|
416
650
|
(item) => item.values ?? []
|
|
417
651
|
),
|
|
418
|
-
usage
|
|
419
|
-
inputTokens: (response.embeddings ?? []).reduce(
|
|
420
|
-
(total, item) => total + (item.statistics?.tokenCount ?? 0),
|
|
421
|
-
0
|
|
422
|
-
)
|
|
423
|
-
}
|
|
652
|
+
usage
|
|
424
653
|
};
|
|
425
654
|
} catch (error) {
|
|
426
|
-
throw
|
|
655
|
+
throw wrapGoogleError(error);
|
|
427
656
|
}
|
|
428
657
|
}
|
|
429
658
|
};
|
|
430
659
|
}
|
|
431
|
-
function wrapError2(error) {
|
|
432
|
-
if (error instanceof ApiError2) {
|
|
433
|
-
return new ProviderError2(error.message, "google", error.status, error);
|
|
434
|
-
}
|
|
435
|
-
return new ProviderError2(
|
|
436
|
-
error instanceof Error ? error.message : String(error),
|
|
437
|
-
"google",
|
|
438
|
-
void 0,
|
|
439
|
-
error
|
|
440
|
-
);
|
|
441
|
-
}
|
|
442
|
-
function asObject2(value) {
|
|
443
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
444
|
-
return value;
|
|
445
|
-
}
|
|
446
|
-
return {};
|
|
447
|
-
}
|
|
448
660
|
|
|
449
661
|
// src/image-model.ts
|
|
450
|
-
import { ApiError as ApiError3 } from "@google/genai";
|
|
451
|
-
import { ProviderError as ProviderError3 } from "@core-ai/core-ai";
|
|
452
662
|
function createGoogleGenAIImageModel(client, modelId) {
|
|
453
663
|
return {
|
|
454
664
|
provider: "google",
|
|
@@ -469,7 +679,7 @@ function createGoogleGenAIImageModel(client, modelId) {
|
|
|
469
679
|
...providerOptions,
|
|
470
680
|
config: {
|
|
471
681
|
...baseRequest.config,
|
|
472
|
-
...
|
|
682
|
+
...asObject(providerOptions["config"])
|
|
473
683
|
}
|
|
474
684
|
} : baseRequest;
|
|
475
685
|
const response = await client.models.generateImages(request);
|
|
@@ -481,22 +691,11 @@ function createGoogleGenAIImageModel(client, modelId) {
|
|
|
481
691
|
}))
|
|
482
692
|
};
|
|
483
693
|
} catch (error) {
|
|
484
|
-
throw
|
|
694
|
+
throw wrapGoogleError(error);
|
|
485
695
|
}
|
|
486
696
|
}
|
|
487
697
|
};
|
|
488
698
|
}
|
|
489
|
-
function wrapError3(error) {
|
|
490
|
-
if (error instanceof ApiError3) {
|
|
491
|
-
return new ProviderError3(error.message, "google", error.status, error);
|
|
492
|
-
}
|
|
493
|
-
return new ProviderError3(
|
|
494
|
-
error instanceof Error ? error.message : String(error),
|
|
495
|
-
"google",
|
|
496
|
-
void 0,
|
|
497
|
-
error
|
|
498
|
-
);
|
|
499
|
-
}
|
|
500
699
|
function mapSizeToImageConfig(size) {
|
|
501
700
|
if (!size) {
|
|
502
701
|
return {};
|
|
@@ -531,12 +730,6 @@ function greatestCommonDivisor(a, b) {
|
|
|
531
730
|
}
|
|
532
731
|
return x === 0 ? 1 : x;
|
|
533
732
|
}
|
|
534
|
-
function asObject3(value) {
|
|
535
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
536
|
-
return value;
|
|
537
|
-
}
|
|
538
|
-
return {};
|
|
539
|
-
}
|
|
540
733
|
|
|
541
734
|
// src/provider.ts
|
|
542
735
|
function createGoogleGenAI(options = {}) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@core-ai/google-genai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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,12 +44,12 @@
|
|
|
44
44
|
"test:watch": "vitest"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@core-ai/core-ai": "^0.
|
|
47
|
+
"@core-ai/core-ai": "^0.3.0",
|
|
48
48
|
"@google/genai": "^1.42.0",
|
|
49
|
-
"zod-to-json-schema": "^3.
|
|
49
|
+
"zod-to-json-schema": "^3.25.1"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"zod": "^3.25.
|
|
52
|
+
"zod": "^3.25.0 || ^4.0.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@core-ai/eslint-config": "^0.0.0",
|