@ljoukov/llm 4.0.9 → 4.0.11

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
+ createPartFromBase64,
10
+ createPartFromFunctionResponse,
11
+ createPartFromUri
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,144 @@ 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 createPartFromBase64(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 = createPartFromBase64(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 = createPartFromBase64(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 = createPartFromUri(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 buildGeminiFunctionResponseParts(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
+ {
4711
+ functionResponse: {
4712
+ name: options.toolName,
4713
+ response: responsePayload2
4714
+ }
4715
+ }
4716
+ ];
4717
+ }
4718
+ const responseOutput = outputItems.map((item) => toGeminiToolOutputPlaceholder(item));
4719
+ const responseParts = outputItems.flatMap((item) => {
4720
+ const mediaPart = buildGeminiToolOutputMediaPart(item);
4721
+ return mediaPart ? [mediaPart] : [];
4722
+ });
4723
+ const responsePayload = { output: responseOutput };
4724
+ const functionResponsePart = options.callId ? createPartFromFunctionResponse(options.callId, options.toolName, responsePayload) : {
4725
+ functionResponse: {
4726
+ name: options.toolName,
4727
+ response: responsePayload
4728
+ }
4729
+ };
4730
+ return [functionResponsePart, ...responseParts];
4731
+ }
4591
4732
  function parseOpenAiToolArguments(raw) {
4592
4733
  const trimmed = raw.trim();
4593
4734
  if (trimmed.length === 0) {
@@ -5048,7 +5189,7 @@ function resolveAttachmentExtension(mimeType) {
5048
5189
  function buildLoggedAttachmentFilename(prefix, index, mimeType) {
5049
5190
  return `${prefix}-${index.toString()}.${resolveAttachmentExtension(mimeType)}`;
5050
5191
  }
5051
- function decodeDataUrlAttachment(value, options) {
5192
+ function parseDataUrlPayload(value) {
5052
5193
  const trimmed = value.trim();
5053
5194
  if (!trimmed.toLowerCase().startsWith("data:")) {
5054
5195
  return null;
@@ -5064,13 +5205,24 @@ function decodeDataUrlAttachment(value, options) {
5064
5205
  try {
5065
5206
  const bytes = isBase64 ? Buffer4.from(payload, "base64") : Buffer4.from(decodeURIComponent(payload), "utf8");
5066
5207
  return {
5067
- filename: buildLoggedAttachmentFilename(options.prefix, options.index, mimeType),
5208
+ mimeType,
5209
+ dataBase64: bytes.toString("base64"),
5068
5210
  bytes
5069
5211
  };
5070
5212
  } catch {
5071
5213
  return null;
5072
5214
  }
5073
5215
  }
5216
+ function decodeDataUrlAttachment(value, options) {
5217
+ const parsed = parseDataUrlPayload(value);
5218
+ if (!parsed) {
5219
+ return null;
5220
+ }
5221
+ return {
5222
+ filename: buildLoggedAttachmentFilename(options.prefix, options.index, parsed.mimeType),
5223
+ bytes: parsed.bytes
5224
+ };
5225
+ }
5074
5226
  function collectPayloadAttachments(value, options) {
5075
5227
  if (typeof value === "string") {
5076
5228
  const attachment = decodeDataUrlAttachment(value, {
@@ -7402,14 +7554,13 @@ async function runToolLoop(request) {
7402
7554
  error: result.error,
7403
7555
  durationMs: result.durationMs
7404
7556
  });
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
- });
7557
+ responseParts.push(
7558
+ ...buildGeminiFunctionResponseParts({
7559
+ toolName: entry.toolName,
7560
+ callId: entry.call.id,
7561
+ outputPayload
7562
+ })
7563
+ );
7413
7564
  }
7414
7565
  const stepCompletedAtMs = Date.now();
7415
7566
  const timing = buildStepTiming({