190proof 1.0.115 → 1.0.116

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 CHANGED
@@ -136,6 +136,16 @@ const payload: GenericPayload = {
136
136
  const response = await callWithRetries("image-example", payload);
137
137
  ```
138
138
 
139
+ How images reach the model depends on the provider. OpenAI, Anthropic, and
140
+ Google get native image blocks. Groq is text-only: images degrade to an inline
141
+ `Image (url)` text reference. OpenRouter sends OpenAI-style `image_url` content
142
+ parts (the remote URL when present, else a `data:` URI) — but if the model has
143
+ no vision-capable endpoints, OpenRouter rejects the request with a
144
+ routing-layer 404, so the retry loop resends the payload with images degraded
145
+ to the same inline text references Groq gets, and remembers the model
146
+ (in-process, until restart) so later calls degrade up front. Messages without
147
+ image attachments serialize identically either way.
148
+
139
149
  ### With System Messages
140
150
 
141
151
  ```typescript
package/dist/index.d.mts CHANGED
@@ -352,6 +352,15 @@ interface GenericPayload {
352
352
  signal?: AbortSignal;
353
353
  }
354
354
 
355
+ /**
356
+ * In-process memory of OpenRouter models that rejected image input (the
357
+ * routing-layer 404 "No endpoints found that support image input"). Payloads
358
+ * for these models degrade image attachments to inline `Image (url)` text
359
+ * references up front — the exact pre-vision serialization — instead of
360
+ * burning a doomed attempt per call. Populated by the retry loop on first
361
+ * rejection; cleared only by process restart (exported so tests can reset it).
362
+ */
363
+ declare const openRouterImageRejectedModels: Set<string>;
355
364
  declare const OPENROUTER_STREAM_TIMEOUT_MS = 600000;
356
365
  declare const OPENROUTER_NONSTREAM_TIMEOUT_MS = 180000;
357
366
  /**
@@ -366,4 +375,4 @@ declare function parseModelString(model: string): {
366
375
  };
367
376
  declare function callWithRetries(id: string | string[], aiPayload: GenericPayload, aiConfig?: OpenAIConfig | AnthropicAIConfig, retries?: number, chunkTimeoutMs?: number): Promise<ParsedResponseMessage>;
368
377
 
369
- export { type AnyModel, ClaudeModel, type FunctionCall, type FunctionDefinition, GPTModel, GeminiModel, type GenericMessage, type GenericPayload, GroqModel, MIN_STREAM_ATTEMPT_MS, OPENROUTER_NONSTREAM_TIMEOUT_MS, OPENROUTER_STREAM_TIMEOUT_MS, type OpenAIConfig, OpenRouterModel, type OpenRouterProviderPreferences, type ParsedResponseMessage, type Provider, type ToolResult, callWithRetries, parseModelString };
378
+ export { type AnyModel, ClaudeModel, type FunctionCall, type FunctionDefinition, GPTModel, GeminiModel, type GenericMessage, type GenericPayload, GroqModel, MIN_STREAM_ATTEMPT_MS, OPENROUTER_NONSTREAM_TIMEOUT_MS, OPENROUTER_STREAM_TIMEOUT_MS, type OpenAIConfig, OpenRouterModel, type OpenRouterProviderPreferences, type ParsedResponseMessage, type Provider, type ToolResult, callWithRetries, openRouterImageRejectedModels, parseModelString };
package/dist/index.d.ts CHANGED
@@ -352,6 +352,15 @@ interface GenericPayload {
352
352
  signal?: AbortSignal;
353
353
  }
354
354
 
355
+ /**
356
+ * In-process memory of OpenRouter models that rejected image input (the
357
+ * routing-layer 404 "No endpoints found that support image input"). Payloads
358
+ * for these models degrade image attachments to inline `Image (url)` text
359
+ * references up front — the exact pre-vision serialization — instead of
360
+ * burning a doomed attempt per call. Populated by the retry loop on first
361
+ * rejection; cleared only by process restart (exported so tests can reset it).
362
+ */
363
+ declare const openRouterImageRejectedModels: Set<string>;
355
364
  declare const OPENROUTER_STREAM_TIMEOUT_MS = 600000;
356
365
  declare const OPENROUTER_NONSTREAM_TIMEOUT_MS = 180000;
357
366
  /**
@@ -366,4 +375,4 @@ declare function parseModelString(model: string): {
366
375
  };
367
376
  declare function callWithRetries(id: string | string[], aiPayload: GenericPayload, aiConfig?: OpenAIConfig | AnthropicAIConfig, retries?: number, chunkTimeoutMs?: number): Promise<ParsedResponseMessage>;
368
377
 
369
- export { type AnyModel, ClaudeModel, type FunctionCall, type FunctionDefinition, GPTModel, GeminiModel, type GenericMessage, type GenericPayload, GroqModel, MIN_STREAM_ATTEMPT_MS, OPENROUTER_NONSTREAM_TIMEOUT_MS, OPENROUTER_STREAM_TIMEOUT_MS, type OpenAIConfig, OpenRouterModel, type OpenRouterProviderPreferences, type ParsedResponseMessage, type Provider, type ToolResult, callWithRetries, parseModelString };
378
+ export { type AnyModel, ClaudeModel, type FunctionCall, type FunctionDefinition, GPTModel, GeminiModel, type GenericMessage, type GenericPayload, GroqModel, MIN_STREAM_ATTEMPT_MS, OPENROUTER_NONSTREAM_TIMEOUT_MS, OPENROUTER_STREAM_TIMEOUT_MS, type OpenAIConfig, OpenRouterModel, type OpenRouterProviderPreferences, type ParsedResponseMessage, type Provider, type ToolResult, callWithRetries, openRouterImageRejectedModels, parseModelString };
package/dist/index.js CHANGED
@@ -28,8 +28,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
30
  // index.ts
31
- var proof_exports = {};
32
- __export(proof_exports, {
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
33
  ClaudeModel: () => ClaudeModel,
34
34
  GPTModel: () => GPTModel,
35
35
  GeminiModel: () => GeminiModel,
@@ -39,9 +39,10 @@ __export(proof_exports, {
39
39
  OPENROUTER_STREAM_TIMEOUT_MS: () => OPENROUTER_STREAM_TIMEOUT_MS,
40
40
  OpenRouterModel: () => OpenRouterModel,
41
41
  callWithRetries: () => callWithRetries,
42
+ openRouterImageRejectedModels: () => openRouterImageRejectedModels,
42
43
  parseModelString: () => parseModelString
43
44
  });
44
- module.exports = __toCommonJS(proof_exports);
45
+ module.exports = __toCommonJS(index_exports);
45
46
 
46
47
  // interfaces.ts
47
48
  var ClaudeModel = /* @__PURE__ */ ((ClaudeModel2) => {
@@ -172,8 +173,7 @@ async function withRetries(identifier, apiName, fn, options = {}) {
172
173
  return await fn();
173
174
  } catch (error3) {
174
175
  lastError = error3;
175
- if ((_a = options.signal) == null ? void 0 : _a.aborted)
176
- throw error3;
176
+ if ((_a = options.signal) == null ? void 0 : _a.aborted) throw error3;
177
177
  if (onError) {
178
178
  onError(error3, attempt);
179
179
  } else {
@@ -197,8 +197,7 @@ function parseStreamedResponse(identifier, paragraph, toolCallAccumulators, allo
197
197
  const functionCalls = [];
198
198
  for (let i = 0; i < toolCallAccumulators.length; i++) {
199
199
  const acc = toolCallAccumulators[i];
200
- if (!acc.name || !acc.arguments)
201
- continue;
200
+ if (!acc.name || !acc.arguments) continue;
202
201
  if (allowedFunctionNames && !allowedFunctionNames.has(acc.name)) {
203
202
  throw new Error(
204
203
  `Stream error: received function call with unknown name: ${acc.name}`
@@ -337,8 +336,7 @@ function buildOpenAIRequestConfig(identifier, model, config) {
337
336
  };
338
337
  }
339
338
  function filterOpenAICompatReasoningDetails(details) {
340
- if (!Array.isArray(details))
341
- return details || void 0;
339
+ if (!Array.isArray(details)) return details || void 0;
342
340
  const blocks = details.filter(
343
341
  (block) => typeof (block == null ? void 0 : block.type) === "string" && block.type.startsWith("reasoning.")
344
342
  );
@@ -410,19 +408,17 @@ async function prepareOpenAIPayload(identifier, payload) {
410
408
  };
411
409
  });
412
410
  }
413
- if (message.reasoning)
414
- outMessage.reasoning = message.reasoning;
411
+ if (message.reasoning) outMessage.reasoning = message.reasoning;
415
412
  const reasoningDetails = filterOpenAICompatReasoningDetails(
416
413
  message.reasoningDetails
417
414
  );
418
- if (reasoningDetails)
419
- outMessage.reasoning_details = reasoningDetails;
415
+ if (reasoningDetails) outMessage.reasoning_details = reasoningDetails;
420
416
  preparedPayload.messages.push(outMessage);
421
417
  }
422
418
  return preparedPayload;
423
419
  }
424
420
  async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs, requestTimeoutMs = 12e4, signal) {
425
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
421
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
426
422
  const functionNames = openAiPayload.tools ? new Set(openAiPayload.tools.map((fn) => fn.function.name)) : null;
427
423
  const { endpoint, headers } = buildOpenAIRequestConfig(
428
424
  id,
@@ -440,7 +436,15 @@ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs,
440
436
  const response = await fetch(endpoint, {
441
437
  method: "POST",
442
438
  headers,
443
- body: JSON.stringify({ ...openAiPayload, stream: true }),
439
+ // include_usage: OpenAI only reports token usage on streams when asked,
440
+ // via a final choices-less chunk — without it every streamed call is
441
+ // invisible to usage accounting. Skipped for Azure, where older
442
+ // api-versions reject stream_options.
443
+ body: JSON.stringify({
444
+ ...openAiPayload,
445
+ stream: true,
446
+ ...(openAiConfig == null ? void 0 : openAiConfig.service) !== "azure" ? { stream_options: { include_usage: true } } : {}
447
+ }),
444
448
  // Merge (don't overwrite) the internal timeout controller with the caller's
445
449
  // cancellation signal so both an internal timeout and an external abort stop
446
450
  // the stream.
@@ -451,6 +455,7 @@ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs,
451
455
  }
452
456
  let paragraph = "";
453
457
  let reasoning = "";
458
+ let streamUsage = null;
454
459
  const toolCallAccumulators = [];
455
460
  const reader = response.body.getReader();
456
461
  let partialChunk = "";
@@ -478,17 +483,18 @@ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs,
478
483
  }
479
484
  const jsonStrings = chunk.split(/^data: /gm);
480
485
  for (const jsonString of jsonStrings) {
481
- if (!jsonString)
482
- continue;
486
+ if (!jsonString) continue;
483
487
  if (jsonString.includes("[DONE]")) {
484
488
  clearTimeout(overallTimeout);
485
- return parseStreamedResponse(
489
+ const parsed = parseStreamedResponse(
486
490
  id,
487
491
  paragraph,
488
492
  toolCallAccumulators,
489
493
  functionNames,
490
494
  reasoning
491
495
  );
496
+ parsed.usage = streamUsage;
497
+ return parsed;
492
498
  }
493
499
  let json;
494
500
  try {
@@ -498,6 +504,15 @@ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs,
498
504
  continue;
499
505
  }
500
506
  if (!((_a = json.choices) == null ? void 0 : _a.length)) {
507
+ if (json.usage) {
508
+ streamUsage = {
509
+ prompt_tokens: json.usage.prompt_tokens,
510
+ completion_tokens: json.usage.completion_tokens,
511
+ total_tokens: json.usage.total_tokens,
512
+ cached_tokens: (_c = (_b = json.usage.prompt_tokens_details) == null ? void 0 : _b.cached_tokens) != null ? _c : 0
513
+ };
514
+ continue;
515
+ }
501
516
  if (json.error) {
502
517
  logger_default.error(id, "Stream error from OpenAI:", json.error);
503
518
  const error2 = new Error("Stream error: OpenAI error");
@@ -510,27 +525,24 @@ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs,
510
525
  }
511
526
  continue;
512
527
  }
513
- const toolCalls = (_c = (_b = json.choices[0]) == null ? void 0 : _b.delta) == null ? void 0 : _c.tool_calls;
528
+ const toolCalls = (_e = (_d = json.choices[0]) == null ? void 0 : _d.delta) == null ? void 0 : _e.tool_calls;
514
529
  if (toolCalls) {
515
530
  for (const toolCall of toolCalls) {
516
- const idx = (_d = toolCall.index) != null ? _d : 0;
531
+ const idx = (_f = toolCall.index) != null ? _f : 0;
517
532
  while (toolCallAccumulators.length <= idx) {
518
533
  toolCallAccumulators.push({ name: "", arguments: "" });
519
534
  }
520
- if (toolCall.id)
521
- toolCallAccumulators[idx].id = toolCall.id;
522
- if ((_e = toolCall.function) == null ? void 0 : _e.name)
535
+ if (toolCall.id) toolCallAccumulators[idx].id = toolCall.id;
536
+ if ((_g = toolCall.function) == null ? void 0 : _g.name)
523
537
  toolCallAccumulators[idx].name += toolCall.function.name;
524
- if ((_f = toolCall.function) == null ? void 0 : _f.arguments)
538
+ if ((_h = toolCall.function) == null ? void 0 : _h.arguments)
525
539
  toolCallAccumulators[idx].arguments += toolCall.function.arguments;
526
540
  }
527
541
  }
528
- const text = (_h = (_g = json.choices[0]) == null ? void 0 : _g.delta) == null ? void 0 : _h.content;
529
- if (text)
530
- paragraph += text;
531
- const reasoningDelta = (_j = (_i = json.choices[0]) == null ? void 0 : _i.delta) == null ? void 0 : _j.reasoning;
532
- if (reasoningDelta)
533
- reasoning += reasoningDelta;
542
+ const text = (_j = (_i = json.choices[0]) == null ? void 0 : _i.delta) == null ? void 0 : _j.content;
543
+ if (text) paragraph += text;
544
+ const reasoningDelta = (_l = (_k = json.choices[0]) == null ? void 0 : _k.delta) == null ? void 0 : _l.reasoning;
545
+ if (reasoningDelta) reasoning += reasoningDelta;
534
546
  }
535
547
  }
536
548
  }
@@ -675,8 +687,7 @@ function jigAnthropicMessages(messages) {
675
687
  ];
676
688
  }
677
689
  jiggedMessages = jiggedMessages.reduce((acc, message) => {
678
- if (acc.length === 0)
679
- return [message];
690
+ if (acc.length === 0) return [message];
680
691
  const lastMessage = acc[acc.length - 1];
681
692
  if (lastMessage.role === message.role) {
682
693
  const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [{ type: "text", text: lastMessage.content }];
@@ -930,8 +941,7 @@ function jigGoogleMessages(messages) {
930
941
  ];
931
942
  }
932
943
  jiggedMessages = jiggedMessages.reduce((acc, message) => {
933
- if (acc.length === 0)
934
- return [message];
944
+ if (acc.length === 0) return [message];
935
945
  const lastMessage = acc[acc.length - 1];
936
946
  if (lastMessage.role === message.role) {
937
947
  lastMessage.parts = [...lastMessage.parts, ...message.parts];
@@ -970,8 +980,7 @@ async function prepareGoogleAIPayload(_identifier, payload) {
970
980
  const toolNameById = /* @__PURE__ */ new Map();
971
981
  for (const m of payload.messages) {
972
982
  for (const fc of m.functionCalls || []) {
973
- if (fc.id)
974
- toolNameById.set(fc.id, fc.name);
983
+ if (fc.id) toolNameById.set(fc.id, fc.name);
975
984
  }
976
985
  }
977
986
  for (const message of payload.messages) {
@@ -1051,8 +1060,7 @@ async function callGoogleAI(id, payload, requestTimeoutMs = 12e4, signal) {
1051
1060
  ...payload.thinkingConfig ? { thinkingConfig: payload.thinkingConfig } : {}
1052
1061
  }
1053
1062
  };
1054
- if (payload.tools)
1055
- requestBody.tools = [payload.tools];
1063
+ if (payload.tools) requestBody.tools = [payload.tools];
1056
1064
  if (payload.tools && payload.toolConfig) {
1057
1065
  requestBody.toolConfig = payload.toolConfig;
1058
1066
  }
@@ -1110,8 +1118,7 @@ async function callGoogleAI(id, payload, requestTimeoutMs = 12e4, signal) {
1110
1118
  });
1111
1119
  continue;
1112
1120
  }
1113
- if (part.text)
1114
- text += part.text;
1121
+ if (part.text) text += part.text;
1115
1122
  if ((_m = part.inlineData) == null ? void 0 : _m.data) {
1116
1123
  files.push({ mimeType: "image/png", data: part.inlineData.data });
1117
1124
  }
@@ -1189,21 +1196,15 @@ async function callGoogleAIWithRetries(id, payload, retries = 5, requestTimeoutM
1189
1196
  finishReason: error2.finishReason,
1190
1197
  modelVersion: error2.modelVersion
1191
1198
  };
1192
- if (error2.safetyRatings)
1193
- errorDetails.safetyRatings = error2.safetyRatings;
1194
- if (error2.usageMetadata)
1195
- errorDetails.usageMetadata = error2.usageMetadata;
1199
+ if (error2.safetyRatings) errorDetails.safetyRatings = error2.safetyRatings;
1200
+ if (error2.usageMetadata) errorDetails.usageMetadata = error2.usageMetadata;
1196
1201
  if (error2.promptFeedback)
1197
1202
  errorDetails.promptFeedback = error2.promptFeedback;
1198
- if (error2.status)
1199
- errorDetails.httpStatus = error2.status;
1200
- if (error2.code)
1201
- errorDetails.errorCode = error2.code;
1202
- if (error2.details)
1203
- errorDetails.errorDetails = error2.details;
1203
+ if (error2.status) errorDetails.httpStatus = error2.status;
1204
+ if (error2.code) errorDetails.errorCode = error2.code;
1205
+ if (error2.details) errorDetails.errorDetails = error2.details;
1204
1206
  const fileUris = payload.messages.flatMap((m) => m.parts).filter((p) => "fileData" in p).map((p) => p.fileData.fileUri);
1205
- if (fileUris.length)
1206
- errorDetails.fileUris = fileUris;
1207
+ if (fileUris.length) errorDetails.fileUris = fileUris;
1207
1208
  logger_default.error(
1208
1209
  id,
1209
1210
  `Retry #${attempt} error: ${error2.message}`,
@@ -1253,7 +1254,7 @@ async function callGoogleAIWithRetries(id, payload, retries = 5, requestTimeoutM
1253
1254
  function normalizeMessageContent(content) {
1254
1255
  return Array.isArray(content) ? content.map((c) => c.type === "text" ? c.text : `[${c.type}]`).join("\n") : content;
1255
1256
  }
1256
- function prepareOpenAICompatMessages(messages) {
1257
+ function prepareOpenAICompatMessages(messages, opts = {}) {
1257
1258
  var _a;
1258
1259
  const out = [];
1259
1260
  for (const message of messages) {
@@ -1275,6 +1276,22 @@ function prepareOpenAICompatMessages(messages) {
1275
1276
  role: message.role,
1276
1277
  content
1277
1278
  };
1279
+ if (opts.imageParts) {
1280
+ const imageBlocks = (message.files || []).filter(
1281
+ (file) => ALLOWED_IMAGE_MIME_TYPES.includes(file.mimeType) && (file.url || file.data)
1282
+ ).map((file) => ({
1283
+ type: "image_url",
1284
+ image_url: {
1285
+ url: file.url || `data:${file.mimeType};base64,${file.data}`
1286
+ }
1287
+ }));
1288
+ if (imageBlocks.length) {
1289
+ outMessage.content = [
1290
+ ...content ? [{ type: "text", text: content }] : [],
1291
+ ...imageBlocks
1292
+ ];
1293
+ }
1294
+ }
1278
1295
  if ((_a = message.functionCalls) == null ? void 0 : _a.length) {
1279
1296
  outMessage.tool_calls = message.functionCalls.map((fc, i) => {
1280
1297
  var _a2;
@@ -1287,16 +1304,14 @@ function prepareOpenAICompatMessages(messages) {
1287
1304
  }
1288
1305
  };
1289
1306
  });
1290
- if (!content)
1307
+ if (!content && !Array.isArray(outMessage.content))
1291
1308
  outMessage.content = null;
1292
1309
  }
1293
- if (message.reasoning)
1294
- outMessage.reasoning = message.reasoning;
1310
+ if (message.reasoning) outMessage.reasoning = message.reasoning;
1295
1311
  const reasoningDetails = filterOpenAICompatReasoningDetails(
1296
1312
  message.reasoningDetails
1297
1313
  );
1298
- if (reasoningDetails)
1299
- outMessage.reasoning_details = reasoningDetails;
1314
+ if (reasoningDetails) outMessage.reasoning_details = reasoningDetails;
1300
1315
  out.push(outMessage);
1301
1316
  }
1302
1317
  return out;
@@ -1384,11 +1399,14 @@ async function callGroqWithRetries(id, payload, retries = 5, requestTimeoutMs =
1384
1399
  signal
1385
1400
  });
1386
1401
  }
1402
+ var openRouterImageRejectedModels = /* @__PURE__ */ new Set();
1387
1403
  function prepareOpenRouterPayload(payload) {
1388
1404
  var _a;
1389
1405
  return {
1390
1406
  model: payload.model,
1391
- messages: prepareOpenAICompatMessages(payload.messages),
1407
+ messages: prepareOpenAICompatMessages(payload.messages, {
1408
+ imageParts: !openRouterImageRejectedModels.has(String(payload.model))
1409
+ }),
1392
1410
  tools: (_a = payload.functions) == null ? void 0 : _a.map((fn) => ({
1393
1411
  type: "function",
1394
1412
  function: fn
@@ -1439,8 +1457,7 @@ function parseDsmlToolCalls(content) {
1439
1457
  let last = -1;
1440
1458
  let tag;
1441
1459
  while ((tag = DSML_ANY_TAG_RE.exec(content)) !== null) {
1442
- if (first === -1)
1443
- first = tag.index;
1460
+ if (first === -1) first = tag.index;
1444
1461
  last = tag.index + tag[0].length;
1445
1462
  }
1446
1463
  const remaining = first === -1 ? content : (content.slice(0, first) + content.slice(last)).trim();
@@ -1456,8 +1473,7 @@ function finalizeOpenRouterMessage(id, raw) {
1456
1473
  const functionCalls = [];
1457
1474
  for (let i = 0; i < raw.toolCalls.length; i++) {
1458
1475
  const tc = raw.toolCalls[i];
1459
- if (!tc.name)
1460
- continue;
1476
+ if (!tc.name) continue;
1461
1477
  functionCalls.push({
1462
1478
  id: (_a = tc.id) != null ? _a : `call_${i}`,
1463
1479
  name: tc.name,
@@ -1510,8 +1526,7 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
1510
1526
  controller.abort();
1511
1527
  };
1512
1528
  const unref = (t) => {
1513
- if (typeof t === "object" && "unref" in t)
1514
- t.unref();
1529
+ if (typeof t === "object" && "unref" in t) t.unref();
1515
1530
  return t;
1516
1531
  };
1517
1532
  const totalTimer = unref(
@@ -1579,98 +1594,88 @@ async function callOpenRouterStream(id, payload, streamTimeoutMs = OPENROUTER_ST
1579
1594
  const reader = response.body.getReader();
1580
1595
  const decoder = new TextDecoder();
1581
1596
  let lineBuffer = "";
1582
- outer:
1583
- while (true) {
1584
- const { done, value } = await reader.read();
1585
- if (done)
1586
- break;
1587
- lineBuffer += decoder.decode(value, { stream: true });
1588
- let newlineIdx;
1589
- while ((newlineIdx = lineBuffer.indexOf("\n")) !== -1) {
1590
- let line = lineBuffer.slice(0, newlineIdx);
1591
- lineBuffer = lineBuffer.slice(newlineIdx + 1);
1592
- if (line.endsWith("\r"))
1593
- line = line.slice(0, -1);
1594
- if (!line)
1595
- continue;
1596
- if (line.startsWith(":"))
1597
- continue;
1598
- if (!line.startsWith("data:"))
1599
- continue;
1600
- const dataStr = line.slice(5).trimStart();
1601
- if (dataStr === "[DONE]") {
1602
- sawDone = true;
1603
- break outer;
1604
- }
1605
- let json;
1606
- try {
1607
- json = JSON.parse(dataStr);
1608
- } catch (e) {
1609
- logger_default.error(
1610
- id,
1611
- "OpenRouter stream: unparseable data line:",
1612
- dataStr.slice(0, 200)
1613
- );
1614
- continue;
1597
+ outer: while (true) {
1598
+ const { done, value } = await reader.read();
1599
+ if (done) break;
1600
+ lineBuffer += decoder.decode(value, { stream: true });
1601
+ let newlineIdx;
1602
+ while ((newlineIdx = lineBuffer.indexOf("\n")) !== -1) {
1603
+ let line = lineBuffer.slice(0, newlineIdx);
1604
+ lineBuffer = lineBuffer.slice(newlineIdx + 1);
1605
+ if (line.endsWith("\r")) line = line.slice(0, -1);
1606
+ if (!line) continue;
1607
+ if (line.startsWith(":")) continue;
1608
+ if (!line.startsWith("data:")) continue;
1609
+ const dataStr = line.slice(5).trimStart();
1610
+ if (dataStr === "[DONE]") {
1611
+ sawDone = true;
1612
+ break outer;
1613
+ }
1614
+ let json;
1615
+ try {
1616
+ json = JSON.parse(dataStr);
1617
+ } catch (e) {
1618
+ logger_default.error(
1619
+ id,
1620
+ "OpenRouter stream: unparseable data line:",
1621
+ dataStr.slice(0, 200)
1622
+ );
1623
+ continue;
1624
+ }
1625
+ dataChunks++;
1626
+ if (json.error) {
1627
+ logger_default.error(id, "OpenRouter stream error event:", json.error);
1628
+ const error2 = new Error(
1629
+ `OpenRouter error: ${json.error.message}`
1630
+ );
1631
+ error2.data = json.error;
1632
+ throw error2;
1633
+ }
1634
+ if (json.provider) provider = json.provider;
1635
+ let useful = false;
1636
+ if (json.usage) {
1637
+ usage = json.usage;
1638
+ useful = true;
1639
+ }
1640
+ const choice = (_c = json.choices) == null ? void 0 : _c[0];
1641
+ if (choice) {
1642
+ const delta = (_d = choice.delta) != null ? _d : {};
1643
+ if (delta.content) {
1644
+ paragraph += delta.content;
1645
+ useful = true;
1615
1646
  }
1616
- dataChunks++;
1617
- if (json.error) {
1618
- logger_default.error(id, "OpenRouter stream error event:", json.error);
1619
- const error2 = new Error(
1620
- `OpenRouter error: ${json.error.message}`
1621
- );
1622
- error2.data = json.error;
1623
- throw error2;
1647
+ if (delta.reasoning) {
1648
+ reasoning += delta.reasoning;
1649
+ useful = true;
1624
1650
  }
1625
- if (json.provider)
1626
- provider = json.provider;
1627
- let useful = false;
1628
- if (json.usage) {
1629
- usage = json.usage;
1651
+ if (Array.isArray(delta.reasoning_details) && delta.reasoning_details.length) {
1652
+ reasoningDetails.push(...delta.reasoning_details);
1630
1653
  useful = true;
1631
1654
  }
1632
- const choice = (_c = json.choices) == null ? void 0 : _c[0];
1633
- if (choice) {
1634
- const delta = (_d = choice.delta) != null ? _d : {};
1635
- if (delta.content) {
1636
- paragraph += delta.content;
1637
- useful = true;
1638
- }
1639
- if (delta.reasoning) {
1640
- reasoning += delta.reasoning;
1641
- useful = true;
1642
- }
1643
- if (Array.isArray(delta.reasoning_details) && delta.reasoning_details.length) {
1644
- reasoningDetails.push(...delta.reasoning_details);
1645
- useful = true;
1646
- }
1647
- if (Array.isArray(delta.tool_calls)) {
1648
- for (const toolCall of delta.tool_calls) {
1649
- const idx = (_e = toolCall.index) != null ? _e : 0;
1650
- while (toolCalls.length <= idx) {
1651
- toolCalls.push({ name: "", argumentsJson: "" });
1652
- }
1653
- if (toolCall.id)
1654
- toolCalls[idx].id = toolCall.id;
1655
- if ((_f = toolCall.function) == null ? void 0 : _f.name)
1656
- toolCalls[idx].name += toolCall.function.name;
1657
- if ((_g = toolCall.function) == null ? void 0 : _g.arguments)
1658
- toolCalls[idx].argumentsJson += toolCall.function.arguments;
1659
- useful = true;
1655
+ if (Array.isArray(delta.tool_calls)) {
1656
+ for (const toolCall of delta.tool_calls) {
1657
+ const idx = (_e = toolCall.index) != null ? _e : 0;
1658
+ while (toolCalls.length <= idx) {
1659
+ toolCalls.push({ name: "", argumentsJson: "" });
1660
1660
  }
1661
- }
1662
- if (choice.finish_reason) {
1663
- finishReason = choice.finish_reason;
1661
+ if (toolCall.id) toolCalls[idx].id = toolCall.id;
1662
+ if ((_f = toolCall.function) == null ? void 0 : _f.name)
1663
+ toolCalls[idx].name += toolCall.function.name;
1664
+ if ((_g = toolCall.function) == null ? void 0 : _g.arguments)
1665
+ toolCalls[idx].argumentsJson += toolCall.function.arguments;
1664
1666
  useful = true;
1665
1667
  }
1666
1668
  }
1667
- if (useful)
1668
- armStallTimer();
1669
+ if (choice.finish_reason) {
1670
+ finishReason = choice.finish_reason;
1671
+ useful = true;
1672
+ }
1669
1673
  }
1674
+ if (useful) armStallTimer();
1670
1675
  }
1671
- if (sawDone)
1672
- reader.cancel().catch(() => {
1673
- });
1676
+ }
1677
+ if (sawDone) reader.cancel().catch(() => {
1678
+ });
1674
1679
  if (!sawDone && !finishReason) {
1675
1680
  logger_default.error(
1676
1681
  id,
@@ -1777,15 +1782,12 @@ var MODERATION_RE = /moderat|inappropriate content|content polic|content managem
1777
1782
  function moderationEvictionSlug(error2, payload) {
1778
1783
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1779
1784
  const body = (_c = error2 == null ? void 0 : error2.data) != null ? _c : (_b = (_a = error2 == null ? void 0 : error2.response) == null ? void 0 : _a.data) == null ? void 0 : _b.error;
1780
- if (!body)
1781
- return null;
1785
+ if (!body) return null;
1782
1786
  const message = String((_d = body.message) != null ? _d : "");
1783
1787
  const isModeration = body.code === 403 || Array.isArray((_e = body.metadata) == null ? void 0 : _e.reasons) || MODERATION_RE.test(message);
1784
- if (!isModeration)
1785
- return null;
1788
+ if (!isModeration) return null;
1786
1789
  const display = (_h = (_f = body.metadata) == null ? void 0 : _f.provider_name) != null ? _h : (_g = /Upstream error from ([^:]+):/.exec(message)) == null ? void 0 : _g[1];
1787
- if (!display)
1788
- return null;
1790
+ if (!display) return null;
1789
1791
  const key = normalizeProviderKey(display);
1790
1792
  const fromOrder = (_j = (_i = payload.provider) == null ? void 0 : _i.order) == null ? void 0 : _j.find(
1791
1793
  (entry) => normalizeProviderKey(entry) === key
@@ -1794,12 +1796,15 @@ function moderationEvictionSlug(error2, payload) {
1794
1796
  }
1795
1797
  var MIN_STREAM_ATTEMPT_MS = 1e4;
1796
1798
  var estimateTokens = (text) => Math.round(text.length / 4);
1799
+ function isImageInputRejection(error2) {
1800
+ var _a, _b, _c, _d;
1801
+ const body = (_c = error2 == null ? void 0 : error2.data) != null ? _c : (_b = (_a = error2 == null ? void 0 : error2.response) == null ? void 0 : _a.data) == null ? void 0 : _b.error;
1802
+ return /no endpoints found.*image input/i.test(String((_d = body == null ? void 0 : body.message) != null ? _d : ""));
1803
+ }
1797
1804
  function streamAttemptBudgetMs(options) {
1798
- if (options.streamDeadlineAt === void 0)
1799
- return options.streamTimeoutMs;
1805
+ if (options.streamDeadlineAt === void 0) return options.streamTimeoutMs;
1800
1806
  const remaining = options.streamDeadlineAt - Date.now();
1801
- if (remaining < MIN_STREAM_ATTEMPT_MS)
1802
- return null;
1807
+ if (remaining < MIN_STREAM_ATTEMPT_MS) return null;
1803
1808
  return Math.min(options.streamTimeoutMs, remaining);
1804
1809
  }
1805
1810
  async function callOpenRouterWithRetries(id, payload, retries = 5, options, signal) {
@@ -1846,6 +1851,16 @@ async function callOpenRouterWithRetries(id, payload, retries = 5, options, sign
1846
1851
  `OpenRouter moderation eviction: ignoring provider "${slug}" for remaining attempts`
1847
1852
  );
1848
1853
  }
1854
+ if (isImageInputRejection(error2) && options.genericMessages) {
1855
+ openRouterImageRejectedModels.add(String(payload.model));
1856
+ payload.messages = prepareOpenAICompatMessages(
1857
+ options.genericMessages
1858
+ );
1859
+ logger_default.log(
1860
+ id,
1861
+ `OpenRouter: ${payload.model} has no image-capable endpoints; retrying with images as text refs`
1862
+ );
1863
+ }
1849
1864
  throw error2;
1850
1865
  }),
1851
1866
  { retries, signal }
@@ -1946,7 +1961,8 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
1946
1961
  streamTimeoutMs: (_c = aiPayload.streamTimeoutMs) != null ? _c : OPENROUTER_STREAM_TIMEOUT_MS,
1947
1962
  streamDeadlineAt: aiPayload.streamDeadlineAt,
1948
1963
  requestTimeoutMs: (_d = aiPayload.requestTimeoutMs) != null ? _d : OPENROUTER_NONSTREAM_TIMEOUT_MS,
1949
- chunkTimeoutMs
1964
+ chunkTimeoutMs,
1965
+ genericMessages: routingPayload.messages
1950
1966
  },
1951
1967
  signal
1952
1968
  );
@@ -1955,8 +1971,7 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
1955
1971
  (_e = result.provider) != null ? _e : result.provider = provider;
1956
1972
  return result;
1957
1973
  } catch (error2) {
1958
- if ((_f = aiPayload.signal) == null ? void 0 : _f.aborted)
1959
- throw error2;
1974
+ if ((_f = aiPayload.signal) == null ? void 0 : _f.aborted) throw error2;
1960
1975
  if (aiPayload.fallbackModel) {
1961
1976
  logger_default.error(
1962
1977
  id,
@@ -1992,6 +2007,7 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
1992
2007
  OPENROUTER_STREAM_TIMEOUT_MS,
1993
2008
  OpenRouterModel,
1994
2009
  callWithRetries,
2010
+ openRouterImageRejectedModels,
1995
2011
  parseModelString
1996
2012
  });
1997
2013
  //# sourceMappingURL=index.js.map