@kenkaiiii/gg-ai 4.3.39 → 4.3.40

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 CHANGED
@@ -174,12 +174,70 @@ function zodToJsonSchema(schema) {
174
174
  }
175
175
 
176
176
  // src/providers/transform.ts
177
+ var NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
178
+ var NON_VISION_TOOL_IMAGE_PLACEHOLDER = "(tool image omitted: model does not support images)";
179
+ function stripImages(content, placeholder) {
180
+ const out = [];
181
+ let lastWasPlaceholder = false;
182
+ for (const block of content) {
183
+ if (block.type === "image") {
184
+ if (!lastWasPlaceholder) out.push({ type: "text", text: placeholder });
185
+ lastWasPlaceholder = true;
186
+ continue;
187
+ }
188
+ out.push(block);
189
+ lastWasPlaceholder = block.text === placeholder;
190
+ }
191
+ return out;
192
+ }
193
+ function downgradeUnsupportedImages(messages, supportsImages) {
194
+ if (supportsImages !== false) return messages;
195
+ return messages.map((msg) => {
196
+ if (msg.role === "user" && Array.isArray(msg.content)) {
197
+ return { ...msg, content: stripImages(msg.content, NON_VISION_USER_IMAGE_PLACEHOLDER) };
198
+ }
199
+ if (msg.role === "tool") {
200
+ return {
201
+ ...msg,
202
+ content: msg.content.map(
203
+ (tr) => Array.isArray(tr.content) ? {
204
+ ...tr,
205
+ content: stripImages(tr.content, NON_VISION_TOOL_IMAGE_PLACEHOLDER)
206
+ } : tr
207
+ )
208
+ };
209
+ }
210
+ return msg;
211
+ });
212
+ }
213
+ function toolResultText(content) {
214
+ if (typeof content === "string") return content;
215
+ return content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
216
+ }
217
+ function toolResultImages(content) {
218
+ if (typeof content === "string") return [];
219
+ return content.filter((b) => b.type === "image");
220
+ }
177
221
  function toAnthropicCacheControl(retention, baseUrl) {
178
222
  const resolved = retention ?? "short";
179
223
  if (resolved === "none") return void 0;
180
224
  const ttl = resolved === "long" && (!baseUrl || baseUrl.includes("api.anthropic.com")) ? "1h" : void 0;
181
225
  return { type: "ephemeral", ...ttl && { ttl } };
182
226
  }
227
+ function toAnthropicToolResultContent(content) {
228
+ if (typeof content === "string") return content;
229
+ return content.map((block) => {
230
+ if (block.type === "text") return { type: "text", text: block.text };
231
+ return {
232
+ type: "image",
233
+ source: {
234
+ type: "base64",
235
+ media_type: block.mediaType,
236
+ data: block.data
237
+ }
238
+ };
239
+ });
240
+ }
183
241
  function toAnthropicMessages(messages, cacheControl) {
184
242
  let systemText;
185
243
  const out = [];
@@ -243,7 +301,7 @@ function toAnthropicMessages(messages, cacheControl) {
243
301
  content: msg.content.map((result) => ({
244
302
  type: "tool_result",
245
303
  tool_use_id: result.toolCallId,
246
- content: result.content,
304
+ content: toAnthropicToolResultContent(result.content),
247
305
  is_error: result.isError
248
306
  }))
249
307
  });
@@ -414,11 +472,29 @@ function toOpenAIMessages(messages, options) {
414
472
  continue;
415
473
  }
416
474
  if (msg.role === "tool") {
475
+ const imageBlocks = [];
417
476
  for (const result of msg.content) {
477
+ const text = toolResultText(result.content);
478
+ const images = toolResultImages(result.content);
479
+ const hasText = text.length > 0;
418
480
  out.push({
419
481
  role: "tool",
420
482
  tool_call_id: remapToolCallId(result.toolCallId, idMap),
421
- content: result.content
483
+ content: hasText ? text : "(see attached image)"
484
+ });
485
+ if (images.length > 0 && options?.supportsImages !== false) {
486
+ for (const img of images) {
487
+ imageBlocks.push({
488
+ type: "image_url",
489
+ image_url: { url: `data:${img.mediaType};base64,${img.data}` }
490
+ });
491
+ }
492
+ }
493
+ }
494
+ if (imageBlocks.length > 0) {
495
+ out.push({
496
+ role: "user",
497
+ content: [{ type: "text", text: "Attached image(s) from tool result:" }, ...imageBlocks]
422
498
  });
423
499
  }
424
500
  }
@@ -500,7 +576,8 @@ async function* runStream(options) {
500
576
  const isOAuth = options.apiKey?.startsWith("sk-ant-oat");
501
577
  const useStreaming = options.streaming !== false;
502
578
  const cacheControl = toAnthropicCacheControl(options.cacheRetention, options.baseUrl);
503
- const { system: rawSystem, messages } = toAnthropicMessages(options.messages, cacheControl);
579
+ const downgradedMessages = downgradeUnsupportedImages(options.messages, options.supportsImages);
580
+ const { system: rawSystem, messages } = toAnthropicMessages(downgradedMessages, cacheControl);
504
581
  const system = isOAuth ? [
505
582
  {
506
583
  type: "text",
@@ -892,9 +969,11 @@ async function* runStream2(options) {
892
969
  const useStreaming = options.streaming !== false;
893
970
  const client = createClient2(options);
894
971
  const usesThinkingParam = options.provider === "glm" || options.provider === "moonshot" || options.provider === "xiaomi";
895
- const messages = toOpenAIMessages(options.messages, {
972
+ const downgradedMessages = downgradeUnsupportedImages(options.messages, options.supportsImages);
973
+ const messages = toOpenAIMessages(downgradedMessages, {
896
974
  provider: options.provider,
897
- thinking: !!options.thinking
975
+ thinking: !!options.thinking,
976
+ supportsImages: options.supportsImages
898
977
  });
899
978
  const defaultTemp = options.provider === "glm" ? 0.6 : void 0;
900
979
  const effectiveTemp = options.temperature ?? defaultTemp;
@@ -1186,7 +1265,8 @@ function streamOpenAICodex(options) {
1186
1265
  async function* runStream3(options) {
1187
1266
  const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
1188
1267
  const url = `${baseUrl}/codex/responses`;
1189
- const { system, input } = toCodexInput(options.messages);
1268
+ const downgraded = downgradeUnsupportedImages(options.messages, options.supportsImages);
1269
+ const { system, input } = toCodexInput(downgraded, { supportsImages: options.supportsImages });
1190
1270
  const body = {
1191
1271
  model: options.model,
1192
1272
  store: false,
@@ -1406,7 +1486,11 @@ function remapCodexId(id, idMap) {
1406
1486
  idMap.set(id, mapped);
1407
1487
  return mapped;
1408
1488
  }
1409
- function toCodexInput(messages) {
1489
+ function codexToolResultText(content) {
1490
+ if (typeof content === "string") return content;
1491
+ return content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
1492
+ }
1493
+ function toCodexInput(messages, options) {
1410
1494
  let system;
1411
1495
  const input = [];
1412
1496
  const idMap = /* @__PURE__ */ new Map();
@@ -1459,12 +1543,33 @@ function toCodexInput(messages) {
1459
1543
  continue;
1460
1544
  }
1461
1545
  if (msg.role === "tool") {
1546
+ const toolImages = [];
1462
1547
  for (const result of msg.content) {
1463
1548
  const [callId] = result.toolCallId.includes("|") ? result.toolCallId.split("|", 2) : [result.toolCallId];
1549
+ const text = codexToolResultText(result.content);
1464
1550
  input.push({
1465
1551
  type: "function_call_output",
1466
1552
  call_id: remapCodexId(callId, idMap),
1467
- output: result.content
1553
+ output: text.length > 0 ? text : "(see attached image)"
1554
+ });
1555
+ if (options?.supportsImages !== false && Array.isArray(result.content)) {
1556
+ for (const block of result.content) {
1557
+ if (block.type === "image") toolImages.push(block);
1558
+ }
1559
+ }
1560
+ }
1561
+ if (toolImages.length > 0) {
1562
+ input.push({
1563
+ type: "message",
1564
+ role: "user",
1565
+ content: [
1566
+ { type: "input_text", text: "Attached image(s) from tool result:" },
1567
+ ...toolImages.map((img) => ({
1568
+ type: "input_image",
1569
+ detail: "auto",
1570
+ image_url: `data:${img.mediaType};base64,${img.data}`
1571
+ }))
1572
+ ]
1468
1573
  });
1469
1574
  }
1470
1575
  }