@ljoukov/llm 4.0.9 → 4.0.10

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 CHANGED
@@ -5,7 +5,10 @@ import { randomBytes } from "crypto";
5
5
  import {
6
6
  FinishReason,
7
7
  FunctionCallingConfigMode,
8
- ThinkingLevel
8
+ ThinkingLevel,
9
+ createFunctionResponsePartFromBase64,
10
+ createFunctionResponsePartFromUri,
11
+ createPartFromFunctionResponse
9
12
  } from "@google/genai";
10
13
  import { zodToJsonSchema } from "@alcyone-labs/zod-to-json-schema";
11
14
  import { z as z3 } from "zod";
@@ -4588,6 +4591,150 @@ function toOpenAiToolOutput(value) {
4588
4591
  }
4589
4592
  return mergeToolOutput(value);
4590
4593
  }
4594
+ function toGeminiToolOutputItems(value) {
4595
+ if (isLlmToolOutputContentItem(value)) {
4596
+ return [value];
4597
+ }
4598
+ if (Array.isArray(value) && value.every((item) => isLlmToolOutputContentItem(item))) {
4599
+ return value;
4600
+ }
4601
+ return null;
4602
+ }
4603
+ function inferToolOutputMimeTypeFromFilename(filename) {
4604
+ const normalized = filename?.trim().toLowerCase() ?? "";
4605
+ if (normalized.length === 0) {
4606
+ return void 0;
4607
+ }
4608
+ if (normalized.endsWith(".png")) {
4609
+ return "image/png";
4610
+ }
4611
+ if (normalized.endsWith(".jpg") || normalized.endsWith(".jpeg")) {
4612
+ return "image/jpeg";
4613
+ }
4614
+ if (normalized.endsWith(".webp")) {
4615
+ return "image/webp";
4616
+ }
4617
+ if (normalized.endsWith(".gif")) {
4618
+ return "image/gif";
4619
+ }
4620
+ if (normalized.endsWith(".heic")) {
4621
+ return "image/heic";
4622
+ }
4623
+ if (normalized.endsWith(".heif")) {
4624
+ return "image/heif";
4625
+ }
4626
+ if (normalized.endsWith(".pdf")) {
4627
+ return "application/pdf";
4628
+ }
4629
+ if (normalized.endsWith(".json")) {
4630
+ return "application/json";
4631
+ }
4632
+ if (normalized.endsWith(".md")) {
4633
+ return "text/markdown";
4634
+ }
4635
+ if (normalized.endsWith(".txt")) {
4636
+ return "text/plain";
4637
+ }
4638
+ return void 0;
4639
+ }
4640
+ function buildGeminiToolOutputMediaPart(item) {
4641
+ if (item.type === "input_image") {
4642
+ const parsed = parseDataUrlPayload(item.image_url);
4643
+ if (!parsed) {
4644
+ return null;
4645
+ }
4646
+ return createFunctionResponsePartFromBase64(parsed.dataBase64, parsed.mimeType);
4647
+ }
4648
+ if (item.type === "input_file") {
4649
+ const dataUrl = typeof item.file_url === "string" ? parseDataUrlPayload(item.file_url) : null;
4650
+ if (dataUrl) {
4651
+ const part = createFunctionResponsePartFromBase64(dataUrl.dataBase64, dataUrl.mimeType);
4652
+ const displayName = item.filename?.trim();
4653
+ if (displayName && part.inlineData) {
4654
+ part.inlineData.displayName = displayName;
4655
+ }
4656
+ return part;
4657
+ }
4658
+ const inferredMimeType = inferToolOutputMimeTypeFromFilename(item.filename);
4659
+ if (typeof item.file_data === "string" && item.file_data.trim().length > 0 && inferredMimeType) {
4660
+ const part = createFunctionResponsePartFromBase64(item.file_data, inferredMimeType);
4661
+ const displayName = item.filename?.trim();
4662
+ if (displayName && part.inlineData) {
4663
+ part.inlineData.displayName = displayName;
4664
+ }
4665
+ return part;
4666
+ }
4667
+ if (typeof item.file_url === "string" && item.file_url.trim().length > 0 && inferredMimeType) {
4668
+ const part = createFunctionResponsePartFromUri(item.file_url, inferredMimeType);
4669
+ const displayName = item.filename?.trim();
4670
+ if (displayName && part.fileData) {
4671
+ part.fileData.displayName = displayName;
4672
+ }
4673
+ return part;
4674
+ }
4675
+ }
4676
+ return null;
4677
+ }
4678
+ function toGeminiToolOutputPlaceholder(item) {
4679
+ if (item.type === "input_text") {
4680
+ return {
4681
+ type: item.type,
4682
+ text: item.text
4683
+ };
4684
+ }
4685
+ if (item.type === "input_image") {
4686
+ const parsed = parseDataUrlPayload(item.image_url);
4687
+ return {
4688
+ type: item.type,
4689
+ mimeType: parsed?.mimeType ?? void 0,
4690
+ media: "attached-inline-data"
4691
+ };
4692
+ }
4693
+ const dataUrl = typeof item.file_url === "string" ? parseDataUrlPayload(item.file_url) : null;
4694
+ return {
4695
+ type: item.type,
4696
+ filename: item.filename ?? void 0,
4697
+ fileId: item.file_id ?? void 0,
4698
+ mimeType: dataUrl?.mimeType ?? inferToolOutputMimeTypeFromFilename(item.filename) ?? void 0,
4699
+ media: dataUrl || typeof item.file_data === "string" && item.file_data.trim().length > 0 ? "attached-inline-data" : typeof item.file_url === "string" && item.file_url.trim().length > 0 ? "attached-file-data" : void 0
4700
+ };
4701
+ }
4702
+ function buildGeminiFunctionResponsePart(options) {
4703
+ const outputItems = toGeminiToolOutputItems(options.outputPayload);
4704
+ if (!outputItems) {
4705
+ const responsePayload2 = isPlainRecord(options.outputPayload) ? sanitiseLogValue(options.outputPayload) : { output: sanitiseLogValue(options.outputPayload) };
4706
+ if (options.callId) {
4707
+ return createPartFromFunctionResponse(options.callId, options.toolName, responsePayload2);
4708
+ }
4709
+ return {
4710
+ functionResponse: {
4711
+ name: options.toolName,
4712
+ response: responsePayload2
4713
+ }
4714
+ };
4715
+ }
4716
+ const responseOutput = outputItems.map((item) => toGeminiToolOutputPlaceholder(item));
4717
+ const responseMediaParts = outputItems.flatMap((item) => {
4718
+ const mediaPart = buildGeminiToolOutputMediaPart(item);
4719
+ return mediaPart ? [mediaPart] : [];
4720
+ });
4721
+ const responsePayload = { output: responseOutput };
4722
+ if (options.callId) {
4723
+ return createPartFromFunctionResponse(
4724
+ options.callId,
4725
+ options.toolName,
4726
+ responsePayload,
4727
+ responseMediaParts
4728
+ );
4729
+ }
4730
+ return {
4731
+ functionResponse: {
4732
+ name: options.toolName,
4733
+ response: { output: responseOutput },
4734
+ ...responseMediaParts.length > 0 ? { parts: responseMediaParts } : {}
4735
+ }
4736
+ };
4737
+ }
4591
4738
  function parseOpenAiToolArguments(raw) {
4592
4739
  const trimmed = raw.trim();
4593
4740
  if (trimmed.length === 0) {
@@ -5048,7 +5195,7 @@ function resolveAttachmentExtension(mimeType) {
5048
5195
  function buildLoggedAttachmentFilename(prefix, index, mimeType) {
5049
5196
  return `${prefix}-${index.toString()}.${resolveAttachmentExtension(mimeType)}`;
5050
5197
  }
5051
- function decodeDataUrlAttachment(value, options) {
5198
+ function parseDataUrlPayload(value) {
5052
5199
  const trimmed = value.trim();
5053
5200
  if (!trimmed.toLowerCase().startsWith("data:")) {
5054
5201
  return null;
@@ -5064,13 +5211,24 @@ function decodeDataUrlAttachment(value, options) {
5064
5211
  try {
5065
5212
  const bytes = isBase64 ? Buffer4.from(payload, "base64") : Buffer4.from(decodeURIComponent(payload), "utf8");
5066
5213
  return {
5067
- filename: buildLoggedAttachmentFilename(options.prefix, options.index, mimeType),
5214
+ mimeType,
5215
+ dataBase64: bytes.toString("base64"),
5068
5216
  bytes
5069
5217
  };
5070
5218
  } catch {
5071
5219
  return null;
5072
5220
  }
5073
5221
  }
5222
+ function decodeDataUrlAttachment(value, options) {
5223
+ const parsed = parseDataUrlPayload(value);
5224
+ if (!parsed) {
5225
+ return null;
5226
+ }
5227
+ return {
5228
+ filename: buildLoggedAttachmentFilename(options.prefix, options.index, parsed.mimeType),
5229
+ bytes: parsed.bytes
5230
+ };
5231
+ }
5074
5232
  function collectPayloadAttachments(value, options) {
5075
5233
  if (typeof value === "string") {
5076
5234
  const attachment = decodeDataUrlAttachment(value, {
@@ -7402,14 +7560,13 @@ async function runToolLoop(request) {
7402
7560
  error: result.error,
7403
7561
  durationMs: result.durationMs
7404
7562
  });
7405
- const responsePayload = isPlainRecord(outputPayload) ? outputPayload : { output: outputPayload };
7406
- responseParts.push({
7407
- functionResponse: {
7408
- name: entry.toolName,
7409
- response: responsePayload,
7410
- ...entry.call.id ? { id: entry.call.id } : {}
7411
- }
7412
- });
7563
+ responseParts.push(
7564
+ buildGeminiFunctionResponsePart({
7565
+ toolName: entry.toolName,
7566
+ callId: entry.call.id,
7567
+ outputPayload
7568
+ })
7569
+ );
7413
7570
  }
7414
7571
  const stepCompletedAtMs = Date.now();
7415
7572
  const timing = buildStepTiming({