@simulacra-ai/google 0.0.3 → 0.0.5

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.cjs ADDED
@@ -0,0 +1,501 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ GoogleProvider: () => GoogleProvider,
34
+ GoogleToolCodeContextTransformer: () => GoogleToolCodeContextTransformer
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/google-provider.ts
39
+ var import_node_crypto2 = __toESM(require("crypto"), 1);
40
+ var gemini = __toESM(require("@google/genai"), 1);
41
+ var import_core = require("@simulacra-ai/core");
42
+
43
+ // src/google-tool-code-context-transformer.ts
44
+ var import_node_crypto = __toESM(require("crypto"), 1);
45
+ var GoogleToolCodeContextTransformer = class {
46
+ /**
47
+ * Transforms completion messages by extracting tool calls from code blocks.
48
+ *
49
+ * Scans text content for code blocks tagged as "tool_code" and parses them into
50
+ * proper tool call content. The original code blocks are replaced with newlines
51
+ * in the text content.
52
+ *
53
+ * @param message - The completion message to transform.
54
+ * @returns A promise resolving to the transformed message.
55
+ */
56
+ transform_completion(message) {
57
+ if (message.role !== "assistant") {
58
+ return Promise.resolve(message);
59
+ }
60
+ return Promise.resolve({
61
+ ...message,
62
+ content: message.content.flatMap((c) => {
63
+ if (c.type !== "text") {
64
+ return [c];
65
+ }
66
+ return this.extract_tool_calls(c);
67
+ })
68
+ });
69
+ }
70
+ extract_tool_calls(content) {
71
+ const tool_calls = [];
72
+ const remaining_text = content.text.replaceAll(
73
+ /```(?:tool_code)\n(.*?)\n```/gs,
74
+ (_, tool_code) => {
75
+ const tool_call = this.parse_tool_call(content, tool_code);
76
+ if (tool_call) {
77
+ tool_calls.push(tool_call);
78
+ }
79
+ return "\n";
80
+ }
81
+ );
82
+ return [{ ...content, text: remaining_text }, ...tool_calls];
83
+ }
84
+ parse_tool_call(content, tool_code) {
85
+ const function_match = tool_code?.match(/^print\((\w+)\((.*)\)\)$/);
86
+ if (!function_match) {
87
+ return;
88
+ }
89
+ const [_, name, params_text] = function_match;
90
+ const params = Object.fromEntries(
91
+ Array.from(
92
+ params_text.matchAll(
93
+ /(\w+)\s*=\s*(("(?:\\"|[^"])*")|(-?[\d.]+)|([Tt]rue|[Ff]alse))\s*(,|$)/g
94
+ )
95
+ ).map(([_match, key, _full, str, num, bool]) => {
96
+ if (bool !== void 0) {
97
+ return [key, bool.toLocaleLowerCase() === "true"];
98
+ }
99
+ if (!isNaN(Number(num))) {
100
+ return [key, Number(num)];
101
+ }
102
+ try {
103
+ return [key, JSON.parse(str)];
104
+ } catch {
105
+ return [key, str];
106
+ }
107
+ })
108
+ );
109
+ return {
110
+ type: "tool",
111
+ tool_request_id: content.id ?? import_node_crypto.default.randomUUID(),
112
+ tool: name,
113
+ params
114
+ };
115
+ }
116
+ };
117
+
118
+ // src/google-provider.ts
119
+ var GoogleProvider = class _GoogleProvider {
120
+ #sdk;
121
+ #config;
122
+ context_transformers;
123
+ /**
124
+ * Creates a new Google Gemini provider instance.
125
+ *
126
+ * @param sdk - The initialized Google Generative AI SDK client.
127
+ * @param config - Configuration options for the provider.
128
+ * @param context_transformers - Provider-level context transformers. Defaults to GoogleToolCodeContextTransformer.
129
+ */
130
+ constructor(sdk, config, context_transformers = [new GoogleToolCodeContextTransformer()]) {
131
+ this.#sdk = sdk;
132
+ this.#config = config;
133
+ this.context_transformers = context_transformers;
134
+ }
135
+ /**
136
+ * Executes a model request and streams the response through the provided receiver.
137
+ *
138
+ * @param request - The request containing messages, tools, and system prompt.
139
+ * @param receiver - The receiver that handles streaming events.
140
+ * @param cancellation - Token to signal cancellation of the request.
141
+ * @returns A promise that resolves when the request completes.
142
+ */
143
+ async execute_request(request, receiver, cancellation) {
144
+ const { model, max_tokens, thinking, ...api_extras } = this.#config;
145
+ const params = {
146
+ model,
147
+ config: {
148
+ ...api_extras,
149
+ systemInstruction: request.system,
150
+ maxOutputTokens: max_tokens,
151
+ thinkingConfig: {
152
+ includeThoughts: thinking?.enable,
153
+ thinkingBudget: thinking?.budget_tokens
154
+ },
155
+ ...request.tools.length > 0 ? {
156
+ tools: [
157
+ {
158
+ functionDeclarations: request.tools.map((t) => to_gemini_tool(t))
159
+ }
160
+ ]
161
+ } : {}
162
+ },
163
+ contents: request.messages.map((m) => to_gemini_content(m))
164
+ };
165
+ receiver.before_request({ params });
166
+ receiver.request_raw(params);
167
+ const response = await this.#sdk.models.generateContentStream(params);
168
+ const { peeked_value: _peeked_value, generator } = await (0, import_core.peek_generator)(response);
169
+ this.#stream_response(generator, receiver, cancellation);
170
+ }
171
+ /**
172
+ * Creates a clone of this provider with the same configuration.
173
+ *
174
+ * @returns A new provider instance with identical configuration.
175
+ */
176
+ clone() {
177
+ return new _GoogleProvider(this.#sdk, this.#config, this.context_transformers);
178
+ }
179
+ async #stream_response(stream, receiver, cancellation) {
180
+ try {
181
+ let response;
182
+ const completed_parts = /* @__PURE__ */ new Set();
183
+ for await (const response_chunk of stream) {
184
+ if (cancellation.is_cancellation_requested) {
185
+ receiver.cancel();
186
+ return;
187
+ }
188
+ receiver.stream_raw(response_chunk);
189
+ const { candidates: candidates_chunk, ...rest } = response_chunk;
190
+ response = {
191
+ ...response,
192
+ ...rest,
193
+ candidates: response?.candidates ?? []
194
+ };
195
+ const candidates = response.candidates;
196
+ for (const candidate_chunk of candidates_chunk ?? []) {
197
+ if (!candidates[candidate_chunk.index ?? 0]) {
198
+ candidates[candidate_chunk.index ?? 0] = candidate_chunk;
199
+ const message2 = from_gemini_content(candidate_chunk.content);
200
+ const usage2 = from_gemini_usage(response.usageMetadata);
201
+ for (const content of message2.content) {
202
+ receiver.start_content({ content, message: message2, usage: usage2 });
203
+ }
204
+ receiver.start_message({ message: message2, usage: usage2 });
205
+ continue;
206
+ }
207
+ const { content: content_chunk, ...rest2 } = candidate_chunk;
208
+ const candidate = candidates[candidate_chunk.index ?? 0] = {
209
+ ...candidates[candidate_chunk.index ?? 0],
210
+ ...rest2,
211
+ content: {
212
+ ...candidates[candidate_chunk.index ?? 0]?.content,
213
+ parts: candidates[candidate_chunk.index ?? 0]?.content?.parts ?? []
214
+ }
215
+ };
216
+ for (const part_chunk of content_chunk?.parts ?? []) {
217
+ const [part] = candidate.content.parts.slice(-1);
218
+ if (!part) {
219
+ candidate.content.parts.push(part_chunk);
220
+ receiver.start_content({
221
+ message: from_gemini_content(candidate.content),
222
+ content: from_gemini_part(part_chunk),
223
+ usage: from_gemini_usage(response.usageMetadata)
224
+ });
225
+ receiver.update_message({
226
+ message: from_gemini_content(candidate.content),
227
+ usage: from_gemini_usage(response?.usageMetadata)
228
+ });
229
+ } else {
230
+ if (part_chunk.thought && part.thought) {
231
+ part.text = (part.text ?? "") + (part_chunk.text ?? "");
232
+ } else if (!part_chunk.thought && !part.thought && part_chunk.text && part.text) {
233
+ part.text = (part.text ?? "") + (part_chunk.text ?? "");
234
+ } else if (part_chunk.functionCall && part.functionCall) {
235
+ part.functionCall = (0, import_core.deep_merge)(part.functionCall, part_chunk.functionCall);
236
+ } else if (part_chunk.executableCode && part.executableCode) {
237
+ if (part_chunk.executableCode.code && part.executableCode.code) {
238
+ part.executableCode.code += part_chunk.executableCode.code;
239
+ delete part_chunk.executableCode.code;
240
+ }
241
+ part.executableCode = (0, import_core.deep_merge)(part.executableCode, part_chunk.executableCode);
242
+ } else if (part_chunk.videoMetadata && part.videoMetadata) {
243
+ part.videoMetadata = (0, import_core.deep_merge)(part.videoMetadata, part_chunk.videoMetadata);
244
+ } else if (part_chunk.fileData && part.fileData) {
245
+ part.fileData = (0, import_core.deep_merge)(part.fileData, part_chunk.fileData);
246
+ } else if (part_chunk.inlineData && part.inlineData) {
247
+ part.inlineData = (0, import_core.deep_merge)(part.inlineData, part_chunk.inlineData);
248
+ } else {
249
+ receiver.complete_content({
250
+ message: from_gemini_content(candidate.content),
251
+ content: from_gemini_part(part),
252
+ usage: from_gemini_usage(response.usageMetadata)
253
+ });
254
+ completed_parts.add(candidate.content.parts.length - 1);
255
+ candidate.content.parts.push(part_chunk);
256
+ receiver.start_content({
257
+ message: from_gemini_content(candidate.content),
258
+ content: from_gemini_part(part_chunk),
259
+ usage: from_gemini_usage(response.usageMetadata)
260
+ });
261
+ receiver.update_message({
262
+ message: from_gemini_content(candidate.content),
263
+ usage: from_gemini_usage(response?.usageMetadata)
264
+ });
265
+ continue;
266
+ }
267
+ receiver.update_content({
268
+ message: from_gemini_content(candidate.content),
269
+ content: from_gemini_part(part),
270
+ usage: from_gemini_usage(response.usageMetadata)
271
+ });
272
+ }
273
+ }
274
+ }
275
+ }
276
+ if (!response || !response.candidates?.[0]) {
277
+ throw new Error("no data", { cause: JSON.stringify(response?.promptFeedback ?? response) });
278
+ }
279
+ receiver.response_raw({ ...response });
280
+ const message = from_gemini_content(response.candidates[0].content);
281
+ const usage = from_gemini_usage(response.usageMetadata);
282
+ for (let i = 0; i < message.content.length; i++) {
283
+ if (!completed_parts.has(i)) {
284
+ receiver.complete_content({ content: message.content[i], message, usage });
285
+ }
286
+ }
287
+ receiver.complete_message({ message, usage, ...map_stop_reason(response) });
288
+ } catch (error) {
289
+ receiver.error(error);
290
+ }
291
+ }
292
+ };
293
+ function to_gemini_tool(tool) {
294
+ return {
295
+ name: tool.name,
296
+ description: tool.description,
297
+ parameters: {
298
+ type: gemini.Type.OBJECT,
299
+ properties: (0, import_core.undefined_if_empty)(
300
+ Object.fromEntries(
301
+ tool.parameters.map((p) => [p.name, to_gemini_tool_schema(p, p.description)])
302
+ )
303
+ ),
304
+ required: (0, import_core.undefined_if_empty)(tool.parameters.filter((p) => p.required).map((p) => p.name))
305
+ }
306
+ };
307
+ }
308
+ function from_gemini_content(content) {
309
+ if (!content) {
310
+ return;
311
+ }
312
+ return {
313
+ role: content.role === "model" ? "assistant" : "user",
314
+ content: content.parts?.map((p) => from_gemini_part(p)).filter(Boolean)
315
+ };
316
+ }
317
+ function from_gemini_part(part) {
318
+ if (!part) {
319
+ return;
320
+ }
321
+ if (part.thought) {
322
+ return {
323
+ type: "thinking",
324
+ thought: part.text ?? ""
325
+ };
326
+ }
327
+ if (part.text) {
328
+ return {
329
+ type: "text",
330
+ text: part.text ?? ""
331
+ };
332
+ }
333
+ if (part.functionCall) {
334
+ return {
335
+ type: "tool",
336
+ tool_request_id: part.functionCall.id ?? import_node_crypto2.default.randomUUID(),
337
+ tool: part.functionCall.name ?? "",
338
+ params: part.functionCall.args ?? {}
339
+ };
340
+ }
341
+ if (part.functionResponse) {
342
+ return {
343
+ type: "tool_result",
344
+ tool_request_id: part.functionResponse.id ?? "",
345
+ tool: part.functionResponse.name ?? "",
346
+ result: part.functionResponse.response ?? {}
347
+ };
348
+ }
349
+ return {
350
+ type: "raw",
351
+ model_kind: "google",
352
+ data: JSON.stringify(part)
353
+ };
354
+ }
355
+ function to_gemini_content(message) {
356
+ return {
357
+ role: message.role === "assistant" ? "model" : "user",
358
+ parts: message.content.map((c) => to_gemini_part(c))
359
+ };
360
+ }
361
+ function to_gemini_part(content) {
362
+ switch (content.type) {
363
+ case "text":
364
+ return {
365
+ text: content.text
366
+ };
367
+ case "tool":
368
+ return {
369
+ functionCall: {
370
+ id: content.tool_request_id,
371
+ name: content.tool,
372
+ args: content.params
373
+ }
374
+ };
375
+ case "raw":
376
+ if (content.model_kind === "google") {
377
+ try {
378
+ return {
379
+ ...JSON.parse(content.data)
380
+ };
381
+ } catch {
382
+ return {
383
+ text: content.data
384
+ };
385
+ }
386
+ }
387
+ return {
388
+ text: content.data
389
+ };
390
+ case "tool_result":
391
+ return {
392
+ functionResponse: {
393
+ id: content.tool_request_id,
394
+ name: content.tool,
395
+ response: content.result
396
+ }
397
+ };
398
+ case "thinking":
399
+ return {
400
+ thought: true,
401
+ text: content.thought
402
+ };
403
+ default:
404
+ throw new Error("unexpected content type");
405
+ }
406
+ }
407
+ function to_gemini_tool_schema(parameter_type, description) {
408
+ switch (parameter_type.type) {
409
+ case "string":
410
+ return {
411
+ type: gemini.Type.STRING,
412
+ description,
413
+ enum: parameter_type.enum,
414
+ default: !parameter_type.required ? parameter_type.default : void 0
415
+ };
416
+ case "number":
417
+ return {
418
+ type: gemini.Type.NUMBER,
419
+ description,
420
+ default: !parameter_type.required ? parameter_type.default : void 0
421
+ };
422
+ case "boolean":
423
+ return {
424
+ type: gemini.Type.BOOLEAN,
425
+ description,
426
+ default: !parameter_type.required ? parameter_type.default : void 0
427
+ };
428
+ case "object":
429
+ return {
430
+ type: gemini.Type.OBJECT,
431
+ description,
432
+ properties: Object.fromEntries(
433
+ Object.entries(parameter_type.properties).map(([name, p]) => [
434
+ name,
435
+ to_gemini_tool_schema(p, p.description)
436
+ ])
437
+ ),
438
+ required: (0, import_core.undefined_if_empty)(
439
+ Object.entries(parameter_type.properties).filter(([, p]) => p.required).map(([name]) => name)
440
+ )
441
+ };
442
+ case "array":
443
+ return {
444
+ type: gemini.Type.ARRAY,
445
+ description,
446
+ items: to_gemini_tool_schema(parameter_type.items, parameter_type.items.description)
447
+ };
448
+ }
449
+ }
450
+ function from_gemini_usage(usage) {
451
+ return {
452
+ cache_read_input_tokens: usage?.cachedContentTokenCount,
453
+ input_tokens: usage?.promptTokenCount,
454
+ output_tokens: usage?.candidatesTokenCount
455
+ };
456
+ }
457
+ function map_stop_reason(response) {
458
+ for (const { finishReason, finishMessage } of response?.candidates ?? []) {
459
+ switch (finishReason) {
460
+ case gemini.FinishReason.STOP:
461
+ if (response.candidates?.[0].content?.parts?.some((p) => !!p.functionCall)) {
462
+ return {
463
+ stop_reason: "tool_use",
464
+ stop_details: finishMessage
465
+ };
466
+ } else {
467
+ return {
468
+ stop_reason: "end_turn",
469
+ stop_details: finishMessage
470
+ };
471
+ }
472
+ case gemini.FinishReason.MAX_TOKENS:
473
+ return {
474
+ stop_reason: "max_tokens",
475
+ stop_details: finishMessage
476
+ };
477
+ case gemini.FinishReason.MALFORMED_FUNCTION_CALL:
478
+ return {
479
+ stop_reason: "error",
480
+ stop_details: finishMessage
481
+ };
482
+ case gemini.FinishReason.FINISH_REASON_UNSPECIFIED:
483
+ case gemini.FinishReason.SAFETY:
484
+ case gemini.FinishReason.RECITATION:
485
+ case gemini.FinishReason.LANGUAGE:
486
+ case gemini.FinishReason.BLOCKLIST:
487
+ case gemini.FinishReason.PROHIBITED_CONTENT:
488
+ case gemini.FinishReason.SPII:
489
+ case gemini.FinishReason.IMAGE_SAFETY:
490
+ }
491
+ }
492
+ return {
493
+ stop_reason: "other"
494
+ };
495
+ }
496
+ // Annotate the CommonJS export names for ESM import in node:
497
+ 0 && (module.exports = {
498
+ GoogleProvider,
499
+ GoogleToolCodeContextTransformer
500
+ });
501
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/google-provider.ts","../src/google-tool-code-context-transformer.ts"],"sourcesContent":["export { GoogleProvider, type GoogleProviderConfig } from \"./google-provider.ts\";\nexport { GoogleToolCodeContextTransformer } from \"./google-tool-code-context-transformer.ts\";\n","import crypto from \"node:crypto\";\n\nimport * as gemini from \"@google/genai\";\n\nimport type {\n AssistantContent,\n AssistantMessage,\n CancellationToken,\n CompletionResponseData,\n Content,\n EnumParameterType,\n Message,\n ModelProvider,\n ModelRequest,\n ParameterType,\n ProviderContextTransformer,\n StreamReceiver,\n ToolDefinition,\n} from \"@simulacra-ai/core\";\nimport { deep_merge, peek_generator, undefined_if_empty } from \"@simulacra-ai/core\";\nimport { GoogleToolCodeContextTransformer } from \"./google-tool-code-context-transformer.ts\";\n\n/**\n * Configuration options for the Google Gemini provider.\n */\nexport interface GoogleProviderConfig extends Record<string, unknown> {\n /** The model identifier to use (e.g., \"gemini-2.0-flash-exp\"). */\n model: string;\n /** The maximum number of tokens to generate in the response. */\n max_tokens?: number;\n /** Configuration for extended thinking mode. */\n thinking?: {\n /** Whether to enable extended thinking. */\n enable: boolean;\n /** The token budget allocated for thinking. */\n budget_tokens?: number;\n };\n}\n\n/**\n * Model provider implementation for Google's Gemini models.\n *\n * This provider wraps the Google Generative AI SDK to provide streaming completions\n * with support for tool use, extended thinking, and multimodal content. It handles\n * message formatting, content streaming, and usage tracking according to the\n * ModelProvider interface.\n */\nexport class GoogleProvider implements ModelProvider {\n readonly #sdk: gemini.GoogleGenAI;\n readonly #config: GoogleProviderConfig;\n\n readonly context_transformers: ProviderContextTransformer[];\n\n /**\n * Creates a new Google Gemini provider instance.\n *\n * @param sdk - The initialized Google Generative AI SDK client.\n * @param config - Configuration options for the provider.\n * @param context_transformers - Provider-level context transformers. Defaults to GoogleToolCodeContextTransformer.\n */\n constructor(\n sdk: gemini.GoogleGenAI,\n config: GoogleProviderConfig,\n context_transformers: ProviderContextTransformer[] = [new GoogleToolCodeContextTransformer()],\n ) {\n this.#sdk = sdk;\n this.#config = config;\n this.context_transformers = context_transformers;\n }\n\n /**\n * Executes a model request and streams the response through the provided receiver.\n *\n * @param request - The request containing messages, tools, and system prompt.\n * @param receiver - The receiver that handles streaming events.\n * @param cancellation - Token to signal cancellation of the request.\n * @returns A promise that resolves when the request completes.\n */\n async execute_request(\n request: ModelRequest,\n receiver: StreamReceiver,\n cancellation: CancellationToken,\n ): Promise<void> {\n const { model, max_tokens, thinking, ...api_extras } = this.#config;\n const params: gemini.GenerateContentParameters = {\n model,\n\n config: {\n ...api_extras,\n systemInstruction: request.system,\n maxOutputTokens: max_tokens,\n thinkingConfig: {\n includeThoughts: thinking?.enable,\n thinkingBudget: thinking?.budget_tokens,\n },\n ...(request.tools.length > 0\n ? {\n tools: [\n {\n functionDeclarations: request.tools.map((t) => to_gemini_tool(t)),\n },\n ],\n }\n : {}),\n },\n contents: request.messages.map((m) => to_gemini_content(m)),\n };\n receiver.before_request({ params });\n receiver.request_raw(params);\n\n const response = await this.#sdk.models.generateContentStream(params);\n const { peeked_value: _peeked_value, generator } = (await peek_generator(response)) as {\n peeked_value: gemini.GenerateContentResponse;\n generator: AsyncGenerator<gemini.GenerateContentResponse>;\n };\n\n // Intentionally not awaited. Streaming is event-driven through the receiver.\n // The policy wraps only connection establishment; chunk processing flows\n // asynchronously via StreamListener events back to the conversation.\n this.#stream_response(generator, receiver, cancellation);\n }\n\n /**\n * Creates a clone of this provider with the same configuration.\n *\n * @returns A new provider instance with identical configuration.\n */\n clone(): ModelProvider {\n return new GoogleProvider(this.#sdk, this.#config, this.context_transformers);\n }\n\n async #stream_response(\n stream: AsyncGenerator<gemini.GenerateContentResponse>,\n receiver: StreamReceiver,\n cancellation: CancellationToken,\n ) {\n try {\n let response: Partial<gemini.GenerateContentResponse> | undefined;\n const completed_parts = new Set<number>();\n for await (const response_chunk of stream) {\n if (cancellation.is_cancellation_requested) {\n receiver.cancel();\n return;\n }\n receiver.stream_raw(response_chunk);\n\n const { candidates: candidates_chunk, ...rest } = response_chunk;\n response = {\n ...response,\n ...rest,\n candidates: response?.candidates ?? [],\n };\n const candidates = response.candidates as gemini.Candidate[];\n\n for (const candidate_chunk of candidates_chunk ?? []) {\n if (!candidates[candidate_chunk.index ?? 0]) {\n candidates[candidate_chunk.index ?? 0] = candidate_chunk;\n const message = from_gemini_content(candidate_chunk.content) as AssistantMessage;\n const usage = from_gemini_usage(response.usageMetadata);\n for (const content of message.content) {\n receiver.start_content({ content, message, usage });\n }\n receiver.start_message({ message, usage });\n continue;\n }\n const { content: content_chunk, ...rest } = candidate_chunk;\n const candidate = (candidates[candidate_chunk.index ?? 0] = {\n ...candidates[candidate_chunk.index ?? 0],\n ...rest,\n content: {\n ...candidates[candidate_chunk.index ?? 0]?.content,\n parts: candidates[candidate_chunk.index ?? 0]?.content?.parts ?? [],\n },\n });\n\n for (const part_chunk of content_chunk?.parts ?? []) {\n const [part] = candidate.content.parts.slice(-1);\n if (!part) {\n candidate.content.parts.push(part_chunk);\n receiver.start_content({\n message: from_gemini_content(candidate.content) as AssistantMessage,\n content: from_gemini_part(part_chunk) as AssistantContent,\n usage: from_gemini_usage(response.usageMetadata),\n });\n receiver.update_message({\n message: from_gemini_content(candidate.content) as AssistantMessage,\n usage: from_gemini_usage(response?.usageMetadata),\n });\n } else {\n if (part_chunk.thought && part.thought) {\n part.text = (part.text ?? \"\") + (part_chunk.text ?? \"\");\n } else if (!part_chunk.thought && !part.thought && part_chunk.text && part.text) {\n part.text = (part.text ?? \"\") + (part_chunk.text ?? \"\");\n } else if (part_chunk.functionCall && part.functionCall) {\n part.functionCall = deep_merge(part.functionCall, part_chunk.functionCall);\n } else if (part_chunk.executableCode && part.executableCode) {\n if (part_chunk.executableCode.code && part.executableCode.code) {\n part.executableCode.code += part_chunk.executableCode.code;\n delete part_chunk.executableCode.code;\n }\n part.executableCode = deep_merge(part.executableCode, part_chunk.executableCode);\n } else if (part_chunk.videoMetadata && part.videoMetadata) {\n part.videoMetadata = deep_merge(part.videoMetadata, part_chunk.videoMetadata);\n } else if (part_chunk.fileData && part.fileData) {\n part.fileData = deep_merge(part.fileData, part_chunk.fileData);\n } else if (part_chunk.inlineData && part.inlineData) {\n part.inlineData = deep_merge(part.inlineData, part_chunk.inlineData);\n } else {\n receiver.complete_content({\n message: from_gemini_content(candidate.content) as AssistantMessage,\n content: from_gemini_part(part) as AssistantContent,\n usage: from_gemini_usage(response.usageMetadata),\n });\n completed_parts.add(candidate.content.parts.length - 1);\n candidate.content.parts.push(part_chunk);\n receiver.start_content({\n message: from_gemini_content(candidate.content) as AssistantMessage,\n content: from_gemini_part(part_chunk) as AssistantContent,\n usage: from_gemini_usage(response.usageMetadata),\n });\n receiver.update_message({\n message: from_gemini_content(candidate.content) as AssistantMessage,\n usage: from_gemini_usage(response?.usageMetadata),\n });\n continue;\n }\n receiver.update_content({\n message: from_gemini_content(candidate.content) as AssistantMessage,\n content: from_gemini_part(part) as AssistantContent,\n usage: from_gemini_usage(response.usageMetadata),\n });\n }\n }\n }\n }\n if (!response || !response.candidates?.[0]) {\n throw new Error(\"no data\", { cause: JSON.stringify(response?.promptFeedback ?? response) });\n }\n receiver.response_raw({ ...response });\n\n const message = from_gemini_content(response.candidates[0].content) as AssistantMessage;\n const usage = from_gemini_usage(response.usageMetadata);\n for (let i = 0; i < message.content.length; i++) {\n if (!completed_parts.has(i)) {\n receiver.complete_content({ content: message.content[i], message, usage });\n }\n }\n receiver.complete_message({ message, usage, ...map_stop_reason(response) });\n } catch (error) {\n receiver.error(error);\n }\n }\n}\n\nfunction to_gemini_tool(tool: ToolDefinition): gemini.FunctionDeclaration {\n return {\n name: tool.name,\n description: tool.description,\n parameters: {\n type: gemini.Type.OBJECT,\n properties: undefined_if_empty(\n Object.fromEntries(\n tool.parameters.map((p) => [p.name, to_gemini_tool_schema(p, p.description)]),\n ),\n ),\n required: undefined_if_empty(tool.parameters.filter((p) => p.required).map((p) => p.name)),\n },\n };\n}\n\nfunction from_gemini_content(content?: gemini.Content) {\n if (!content) {\n return;\n }\n return {\n role: content.role === \"model\" ? \"assistant\" : \"user\",\n content: content.parts?.map((p: gemini.Part) => from_gemini_part(p)).filter(Boolean),\n };\n}\n\nfunction from_gemini_part(part: gemini.Part) {\n if (!part) {\n return;\n }\n if (part.thought) {\n return {\n type: \"thinking\",\n thought: part.text ?? \"\",\n };\n }\n if (part.text) {\n return {\n type: \"text\",\n text: part.text ?? \"\",\n };\n }\n if (part.functionCall) {\n return {\n type: \"tool\",\n tool_request_id: part.functionCall.id ?? crypto.randomUUID(),\n tool: part.functionCall.name ?? \"\",\n params: part.functionCall.args ?? {},\n };\n }\n if (part.functionResponse) {\n return {\n type: \"tool_result\",\n tool_request_id: part.functionResponse.id ?? \"\",\n tool: part.functionResponse.name ?? \"\",\n result: part.functionResponse.response ?? {},\n };\n }\n return {\n type: \"raw\",\n model_kind: \"google\",\n data: JSON.stringify(part),\n };\n}\n\nfunction to_gemini_content(message: Message): gemini.Content {\n return {\n role: message.role === \"assistant\" ? \"model\" : \"user\",\n parts: message.content.map((c) => to_gemini_part(c)),\n };\n}\n\nfunction to_gemini_part(content: Readonly<Content>) {\n switch (content.type) {\n case \"text\":\n return {\n text: content.text,\n };\n case \"tool\":\n return {\n functionCall: {\n id: content.tool_request_id,\n name: content.tool,\n args: content.params,\n },\n };\n case \"raw\":\n if (content.model_kind === \"google\") {\n try {\n return {\n ...JSON.parse(content.data),\n };\n } catch {\n return {\n text: content.data,\n };\n }\n }\n return {\n text: content.data,\n };\n case \"tool_result\":\n return {\n functionResponse: {\n id: content.tool_request_id,\n name: content.tool,\n response: content.result,\n },\n };\n case \"thinking\":\n return {\n thought: true,\n text: content.thought,\n };\n default:\n throw new Error(\"unexpected content type\");\n }\n}\n\nfunction to_gemini_tool_schema(parameter_type: ParameterType, description?: string): gemini.Schema {\n switch (parameter_type.type) {\n case \"string\":\n return {\n type: gemini.Type.STRING,\n description,\n enum: (parameter_type as EnumParameterType).enum,\n default: !parameter_type.required ? parameter_type.default : undefined,\n };\n case \"number\":\n return {\n type: gemini.Type.NUMBER,\n description,\n default: !parameter_type.required ? parameter_type.default : undefined,\n };\n case \"boolean\":\n return {\n type: gemini.Type.BOOLEAN,\n description,\n default: !parameter_type.required ? parameter_type.default : undefined,\n };\n case \"object\":\n return {\n type: gemini.Type.OBJECT,\n description,\n properties: Object.fromEntries(\n Object.entries(parameter_type.properties).map(([name, p]) => [\n name,\n to_gemini_tool_schema(p, p.description),\n ]),\n ),\n required: undefined_if_empty(\n Object.entries(parameter_type.properties)\n .filter(([, p]) => p.required)\n .map(([name]) => name),\n ),\n };\n case \"array\":\n return {\n type: gemini.Type.ARRAY,\n description,\n items: to_gemini_tool_schema(parameter_type.items, parameter_type.items.description),\n };\n }\n}\n\nfunction from_gemini_usage(usage: gemini.GenerateContentResponseUsageMetadata | undefined) {\n return {\n cache_read_input_tokens: usage?.cachedContentTokenCount,\n input_tokens: usage?.promptTokenCount,\n output_tokens: usage?.candidatesTokenCount,\n };\n}\n\nfunction map_stop_reason(\n response: Partial<gemini.GenerateContentResponse>,\n): Pick<CompletionResponseData, \"stop_reason\" | \"stop_details\"> {\n for (const { finishReason, finishMessage } of response?.candidates ?? []) {\n switch (finishReason) {\n case gemini.FinishReason.STOP:\n if (response.candidates?.[0].content?.parts?.some((p: gemini.Part) => !!p.functionCall)) {\n return {\n stop_reason: \"tool_use\",\n stop_details: finishMessage,\n };\n } else {\n return {\n stop_reason: \"end_turn\",\n stop_details: finishMessage,\n };\n }\n case gemini.FinishReason.MAX_TOKENS:\n return {\n stop_reason: \"max_tokens\",\n stop_details: finishMessage,\n };\n case gemini.FinishReason.MALFORMED_FUNCTION_CALL:\n return {\n stop_reason: \"error\",\n stop_details: finishMessage,\n };\n case gemini.FinishReason.FINISH_REASON_UNSPECIFIED:\n case gemini.FinishReason.SAFETY:\n case gemini.FinishReason.RECITATION:\n case gemini.FinishReason.LANGUAGE:\n case gemini.FinishReason.BLOCKLIST:\n case gemini.FinishReason.PROHIBITED_CONTENT:\n case gemini.FinishReason.SPII:\n case gemini.FinishReason.IMAGE_SAFETY:\n }\n }\n return {\n stop_reason: \"other\",\n };\n}\n","import crypto from \"node:crypto\";\n\nimport type {\n AssistantContent,\n AssistantMessage,\n ProviderContextTransformer,\n TextContent,\n ToolContent,\n} from \"@simulacra-ai/core\";\n\n/**\n * Provider context transformer that extracts tool calls from Gemini's code execution blocks.\n *\n * Gemini models sometimes return tool calls as executable code in markdown code blocks\n * tagged with \"tool_code\". This transformer parses those blocks and converts them into\n * standard tool call content that the framework can execute.\n */\nexport class GoogleToolCodeContextTransformer implements ProviderContextTransformer {\n /**\n * Transforms completion messages by extracting tool calls from code blocks.\n *\n * Scans text content for code blocks tagged as \"tool_code\" and parses them into\n * proper tool call content. The original code blocks are replaced with newlines\n * in the text content.\n *\n * @param message - The completion message to transform.\n * @returns A promise resolving to the transformed message.\n */\n transform_completion(message: AssistantMessage) {\n if (message.role !== \"assistant\") {\n return Promise.resolve(message);\n }\n\n return Promise.resolve({\n ...message,\n content: message.content.flatMap((c) => {\n if (c.type !== \"text\") {\n return [c];\n }\n return this.extract_tool_calls(c);\n }),\n });\n }\n\n private extract_tool_calls(content: TextContent) {\n const tool_calls: ToolContent[] = [];\n const remaining_text = content.text.replaceAll(\n /```(?:tool_code)\\n(.*?)\\n```/gs,\n (_, tool_code) => {\n const tool_call = this.parse_tool_call(content, tool_code);\n if (tool_call) {\n tool_calls.push(tool_call);\n }\n return \"\\n\";\n },\n );\n\n return [{ ...content, text: remaining_text }, ...tool_calls] as AssistantContent[];\n }\n\n private parse_tool_call(content: AssistantContent, tool_code?: string) {\n const function_match = tool_code?.match(/^print\\((\\w+)\\((.*)\\)\\)$/);\n if (!function_match) {\n return;\n }\n const [_, name, params_text] = function_match;\n const params = Object.fromEntries(\n Array.from(\n params_text.matchAll(\n /(\\w+)\\s*=\\s*((\"(?:\\\\\"|[^\"])*\")|(-?[\\d.]+)|([Tt]rue|[Ff]alse))\\s*(,|$)/g,\n ),\n ).map(([_match, key, _full, str, num, bool]: string[]) => {\n if (bool !== undefined) {\n return [key, bool.toLocaleLowerCase() === \"true\"];\n }\n if (!isNaN(Number(num))) {\n return [key, Number(num)];\n }\n try {\n return [key, JSON.parse(str)];\n } catch {\n return [key, str];\n }\n }),\n );\n\n return {\n type: \"tool\",\n tool_request_id: content.id ?? crypto.randomUUID(),\n tool: name,\n params: params,\n } as ToolContent;\n }\n}\n"],"mappings":"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","names":["import_node_crypto","crypto","message","usage","rest","crypto"]}
@@ -1,9 +1,10 @@
1
- import * as gemini from "@google/genai";
2
- import type { CancellationToken, ModelProvider, ModelRequest, ProviderContextTransformer, StreamReceiver } from "@simulacra-ai/core";
1
+ import * as gemini from '@google/genai';
2
+ import { ModelProvider, ProviderContextTransformer, ModelRequest, StreamReceiver, CancellationToken, AssistantMessage } from '@simulacra-ai/core';
3
+
3
4
  /**
4
5
  * Configuration options for the Google Gemini provider.
5
6
  */
6
- export interface GoogleProviderConfig extends Record<string, unknown> {
7
+ interface GoogleProviderConfig extends Record<string, unknown> {
7
8
  /** The model identifier to use (e.g., "gemini-2.0-flash-exp"). */
8
9
  model: string;
9
10
  /** The maximum number of tokens to generate in the response. */
@@ -24,7 +25,7 @@ export interface GoogleProviderConfig extends Record<string, unknown> {
24
25
  * message formatting, content streaming, and usage tracking according to the
25
26
  * ModelProvider interface.
26
27
  */
27
- export declare class GoogleProvider implements ModelProvider {
28
+ declare class GoogleProvider implements ModelProvider {
28
29
  #private;
29
30
  readonly context_transformers: ProviderContextTransformer[];
30
31
  /**
@@ -51,4 +52,28 @@ export declare class GoogleProvider implements ModelProvider {
51
52
  */
52
53
  clone(): ModelProvider;
53
54
  }
54
- //# sourceMappingURL=google-provider.d.ts.map
55
+
56
+ /**
57
+ * Provider context transformer that extracts tool calls from Gemini's code execution blocks.
58
+ *
59
+ * Gemini models sometimes return tool calls as executable code in markdown code blocks
60
+ * tagged with "tool_code". This transformer parses those blocks and converts them into
61
+ * standard tool call content that the framework can execute.
62
+ */
63
+ declare class GoogleToolCodeContextTransformer implements ProviderContextTransformer {
64
+ /**
65
+ * Transforms completion messages by extracting tool calls from code blocks.
66
+ *
67
+ * Scans text content for code blocks tagged as "tool_code" and parses them into
68
+ * proper tool call content. The original code blocks are replaced with newlines
69
+ * in the text content.
70
+ *
71
+ * @param message - The completion message to transform.
72
+ * @returns A promise resolving to the transformed message.
73
+ */
74
+ transform_completion(message: AssistantMessage): Promise<AssistantMessage>;
75
+ private extract_tool_calls;
76
+ private parse_tool_call;
77
+ }
78
+
79
+ export { GoogleProvider, type GoogleProviderConfig, GoogleToolCodeContextTransformer };