190proof 1.0.104 → 1.0.106

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
@@ -1,6 +1,6 @@
1
1
  # 190proof
2
2
 
3
- A unified interface for interacting with multiple AI providers including **OpenAI**, **Anthropic**, **Google**, **Groq**, **OpenRouter**, and **AWS Bedrock**. This package provides a consistent API for making requests to different LLM providers while handling retries, streaming, and multimodal inputs.
3
+ An opinionated unified interface for interacting with multiple AI providers including **OpenAI**, **Anthropic**, **Google**, **Groq**, **OpenRouter**, and **AWS Bedrock**. This package provides a consistent API for making requests to different LLM providers while handling retries, streaming, and multimodal inputs.
4
4
 
5
5
  ## Features
6
6
 
@@ -289,6 +289,10 @@ interface ParsedResponseMessage {
289
289
  function_call: FunctionCall | null;
290
290
  function_calls: FunctionCall[];
291
291
  files: File[]; // For models that return files (e.g., image generation)
292
+ // Who actually served the response: OpenRouter's upstream provider from the
293
+ // response body (e.g. "Baidu"), or the SDK provider name ("anthropic", ...)
294
+ // for direct providers. On fallback, reflects the model that answered.
295
+ provider?: string;
292
296
  usage: {
293
297
  prompt_tokens: number;
294
298
  completion_tokens: number;
package/dist/index.d.mts CHANGED
@@ -175,6 +175,15 @@ interface ParsedResponseMessage {
175
175
  * validate. Undefined when the model/provider returns none.
176
176
  */
177
177
  reasoningDetails?: any;
178
+ /**
179
+ * Who actually served the response. For OpenRouter this is the upstream
180
+ * provider from the response body (e.g. "Baidu", "Morph") — the routing
181
+ * decision OpenRouter made, not the requested model slug. For direct
182
+ * providers it's the SDK provider name ("anthropic", "openai", "google",
183
+ * "groq"). On model fallback it reflects the model that answered, so a
184
+ * mismatch with the requested model's provider reveals the fallback.
185
+ */
186
+ provider?: string;
178
187
  usage: {
179
188
  prompt_tokens: number;
180
189
  completion_tokens: number;
package/dist/index.d.ts CHANGED
@@ -175,6 +175,15 @@ interface ParsedResponseMessage {
175
175
  * validate. Undefined when the model/provider returns none.
176
176
  */
177
177
  reasoningDetails?: any;
178
+ /**
179
+ * Who actually served the response. For OpenRouter this is the upstream
180
+ * provider from the response body (e.g. "Baidu", "Morph") — the routing
181
+ * decision OpenRouter made, not the requested model slug. For direct
182
+ * providers it's the SDK provider name ("anthropic", "openai", "google",
183
+ * "groq"). On model fallback it reflects the model that answered, so a
184
+ * mismatch with the requested model's provider reveals the fallback.
185
+ */
186
+ provider?: string;
178
187
  usage: {
179
188
  prompt_tokens: number;
180
189
  completion_tokens: number;
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,
@@ -38,7 +38,7 @@ __export(proof_exports, {
38
38
  callWithRetries: () => callWithRetries,
39
39
  parseModelString: () => parseModelString
40
40
  });
41
- module.exports = __toCommonJS(proof_exports);
41
+ module.exports = __toCommonJS(index_exports);
42
42
 
43
43
  // interfaces.ts
44
44
  var ClaudeModel = /* @__PURE__ */ ((ClaudeModel2) => {
@@ -156,8 +156,7 @@ async function withRetries(identifier, apiName, fn, options = {}) {
156
156
  return await fn();
157
157
  } catch (error3) {
158
158
  lastError = error3;
159
- if ((_a = options.signal) == null ? void 0 : _a.aborted)
160
- throw error3;
159
+ if ((_a = options.signal) == null ? void 0 : _a.aborted) throw error3;
161
160
  if (onError) {
162
161
  onError(error3, attempt);
163
162
  } else {
@@ -181,8 +180,7 @@ function parseStreamedResponse(identifier, paragraph, toolCallAccumulators, allo
181
180
  const functionCalls = [];
182
181
  for (let i = 0; i < toolCallAccumulators.length; i++) {
183
182
  const acc = toolCallAccumulators[i];
184
- if (!acc.name || !acc.arguments)
185
- continue;
183
+ if (!acc.name || !acc.arguments) continue;
186
184
  if (allowedFunctionNames && !allowedFunctionNames.has(acc.name)) {
187
185
  throw new Error(
188
186
  `Stream error: received function call with unknown name: ${acc.name}`
@@ -321,8 +319,7 @@ function buildOpenAIRequestConfig(identifier, model, config) {
321
319
  };
322
320
  }
323
321
  function filterOpenAICompatReasoningDetails(details) {
324
- if (!Array.isArray(details))
325
- return details || void 0;
322
+ if (!Array.isArray(details)) return details || void 0;
326
323
  const blocks = details.filter(
327
324
  (block) => typeof (block == null ? void 0 : block.type) === "string" && block.type.startsWith("reasoning.")
328
325
  );
@@ -393,13 +390,11 @@ async function prepareOpenAIPayload(identifier, payload) {
393
390
  };
394
391
  });
395
392
  }
396
- if (message.reasoning)
397
- outMessage.reasoning = message.reasoning;
393
+ if (message.reasoning) outMessage.reasoning = message.reasoning;
398
394
  const reasoningDetails = filterOpenAICompatReasoningDetails(
399
395
  message.reasoningDetails
400
396
  );
401
- if (reasoningDetails)
402
- outMessage.reasoning_details = reasoningDetails;
397
+ if (reasoningDetails) outMessage.reasoning_details = reasoningDetails;
403
398
  preparedPayload.messages.push(outMessage);
404
399
  }
405
400
  return preparedPayload;
@@ -461,8 +456,7 @@ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs,
461
456
  }
462
457
  const jsonStrings = chunk.split(/^data: /gm);
463
458
  for (const jsonString of jsonStrings) {
464
- if (!jsonString)
465
- continue;
459
+ if (!jsonString) continue;
466
460
  if (jsonString.includes("[DONE]")) {
467
461
  clearTimeout(overallTimeout);
468
462
  return parseStreamedResponse(
@@ -500,8 +494,7 @@ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs,
500
494
  while (toolCallAccumulators.length <= idx) {
501
495
  toolCallAccumulators.push({ name: "", arguments: "" });
502
496
  }
503
- if (toolCall.id)
504
- toolCallAccumulators[idx].id = toolCall.id;
497
+ if (toolCall.id) toolCallAccumulators[idx].id = toolCall.id;
505
498
  if ((_e = toolCall.function) == null ? void 0 : _e.name)
506
499
  toolCallAccumulators[idx].name += toolCall.function.name;
507
500
  if ((_f = toolCall.function) == null ? void 0 : _f.arguments)
@@ -509,11 +502,9 @@ async function callOpenAIStream(id, openAiPayload, openAiConfig, chunkTimeoutMs,
509
502
  }
510
503
  }
511
504
  const text = (_h = (_g = json.choices[0]) == null ? void 0 : _g.delta) == null ? void 0 : _h.content;
512
- if (text)
513
- paragraph += text;
505
+ if (text) paragraph += text;
514
506
  const reasoningDelta = (_j = (_i = json.choices[0]) == null ? void 0 : _i.delta) == null ? void 0 : _j.reasoning;
515
- if (reasoningDelta)
516
- reasoning += reasoningDelta;
507
+ if (reasoningDelta) reasoning += reasoningDelta;
517
508
  }
518
509
  }
519
510
  }
@@ -658,8 +649,7 @@ function jigAnthropicMessages(messages) {
658
649
  ];
659
650
  }
660
651
  jiggedMessages = jiggedMessages.reduce((acc, message) => {
661
- if (acc.length === 0)
662
- return [message];
652
+ if (acc.length === 0) return [message];
663
653
  const lastMessage = acc[acc.length - 1];
664
654
  if (lastMessage.role === message.role) {
665
655
  const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [{ type: "text", text: lastMessage.content }];
@@ -684,7 +674,11 @@ async function prepareAnthropicPayload(_identifier, payload) {
684
674
  model: payload.model,
685
675
  messages: [],
686
676
  functions: payload.functions,
687
- temperature: payload.temperature
677
+ temperature: payload.temperature,
678
+ // Map the generic function_call to Anthropic tool_choice ("none" forces a
679
+ // text-only turn). Only meaningful alongside tools — callAnthropic sends
680
+ // it only when tools are present (tool_choice without tools 400s).
681
+ tool_choice: payload.function_call ? typeof payload.function_call === "string" ? { type: payload.function_call } : { type: "tool", name: payload.function_call.name } : void 0
688
682
  };
689
683
  for (const message of payload.messages) {
690
684
  if (message.role === "system") {
@@ -798,6 +792,8 @@ async function callAnthropic(id, payload, config, requestTimeoutMs = 12e4, signa
798
792
  model: payload.model,
799
793
  messages: anthropicMessages,
800
794
  tools: cachedTools,
795
+ // tool_choice requires tools in the request; drop it otherwise.
796
+ tool_choice: (cachedTools == null ? void 0 : cachedTools.length) ? payload.tool_choice : void 0,
801
797
  temperature: payload.temperature,
802
798
  system: payload.system,
803
799
  max_tokens: 4096
@@ -902,8 +898,7 @@ function jigGoogleMessages(messages) {
902
898
  ];
903
899
  }
904
900
  jiggedMessages = jiggedMessages.reduce((acc, message) => {
905
- if (acc.length === 0)
906
- return [message];
901
+ if (acc.length === 0) return [message];
907
902
  const lastMessage = acc[acc.length - 1];
908
903
  if (lastMessage.role === message.role) {
909
904
  lastMessage.parts = [...lastMessage.parts, ...message.parts];
@@ -929,13 +924,19 @@ async function prepareGoogleAIPayload(_identifier, payload) {
929
924
  ...fn.parameters
930
925
  }
931
926
  }))
927
+ } : void 0,
928
+ // Map the generic function_call to Gemini's functionCallingConfig ("none"
929
+ // → mode NONE forces a text-only turn). Only meaningful alongside tools.
930
+ toolConfig: payload.functions && payload.function_call ? {
931
+ functionCallingConfig: {
932
+ mode: typeof payload.function_call === "string" ? payload.function_call === "none" ? "NONE" : "AUTO" : "ANY"
933
+ }
932
934
  } : void 0
933
935
  };
934
936
  const toolNameById = /* @__PURE__ */ new Map();
935
937
  for (const m of payload.messages) {
936
938
  for (const fc of m.functionCalls || []) {
937
- if (fc.id)
938
- toolNameById.set(fc.id, fc.name);
939
+ if (fc.id) toolNameById.set(fc.id, fc.name);
939
940
  }
940
941
  }
941
942
  for (const message of payload.messages) {
@@ -1012,8 +1013,10 @@ async function callGoogleAI(id, payload, requestTimeoutMs = 12e4, signal) {
1012
1013
  contents,
1013
1014
  generationConfig: { responseModalities: ["TEXT"] }
1014
1015
  };
1015
- if (payload.tools)
1016
- requestBody.tools = [payload.tools];
1016
+ if (payload.tools) requestBody.tools = [payload.tools];
1017
+ if (payload.tools && payload.toolConfig) {
1018
+ requestBody.toolConfig = payload.toolConfig;
1019
+ }
1017
1020
  if (payload.systemInstruction) {
1018
1021
  requestBody.systemInstruction = {
1019
1022
  parts: [{ text: payload.systemInstruction }]
@@ -1063,8 +1066,7 @@ async function callGoogleAI(id, payload, requestTimeoutMs = 12e4, signal) {
1063
1066
  });
1064
1067
  continue;
1065
1068
  }
1066
- if (part.text)
1067
- text += part.text;
1069
+ if (part.text) text += part.text;
1068
1070
  if ((_m = part.inlineData) == null ? void 0 : _m.data) {
1069
1071
  files.push({ mimeType: "image/png", data: part.inlineData.data });
1070
1072
  }
@@ -1141,21 +1143,15 @@ async function callGoogleAIWithRetries(id, payload, retries = 5, requestTimeoutM
1141
1143
  finishReason: error2.finishReason,
1142
1144
  modelVersion: error2.modelVersion
1143
1145
  };
1144
- if (error2.safetyRatings)
1145
- errorDetails.safetyRatings = error2.safetyRatings;
1146
- if (error2.usageMetadata)
1147
- errorDetails.usageMetadata = error2.usageMetadata;
1146
+ if (error2.safetyRatings) errorDetails.safetyRatings = error2.safetyRatings;
1147
+ if (error2.usageMetadata) errorDetails.usageMetadata = error2.usageMetadata;
1148
1148
  if (error2.promptFeedback)
1149
1149
  errorDetails.promptFeedback = error2.promptFeedback;
1150
- if (error2.status)
1151
- errorDetails.httpStatus = error2.status;
1152
- if (error2.code)
1153
- errorDetails.errorCode = error2.code;
1154
- if (error2.details)
1155
- errorDetails.errorDetails = error2.details;
1150
+ if (error2.status) errorDetails.httpStatus = error2.status;
1151
+ if (error2.code) errorDetails.errorCode = error2.code;
1152
+ if (error2.details) errorDetails.errorDetails = error2.details;
1156
1153
  const fileUris = payload.messages.flatMap((m) => m.parts).filter((p) => "fileData" in p).map((p) => p.fileData.fileUri);
1157
- if (fileUris.length)
1158
- errorDetails.fileUris = fileUris;
1154
+ if (fileUris.length) errorDetails.fileUris = fileUris;
1159
1155
  logger_default.error(
1160
1156
  id,
1161
1157
  `Retry #${attempt} error: ${error2.message}`,
@@ -1235,16 +1231,13 @@ function prepareOpenAICompatMessages(messages) {
1235
1231
  }
1236
1232
  };
1237
1233
  });
1238
- if (!message.content)
1239
- outMessage.content = null;
1234
+ if (!message.content) outMessage.content = null;
1240
1235
  }
1241
- if (message.reasoning)
1242
- outMessage.reasoning = message.reasoning;
1236
+ if (message.reasoning) outMessage.reasoning = message.reasoning;
1243
1237
  const reasoningDetails = filterOpenAICompatReasoningDetails(
1244
1238
  message.reasoningDetails
1245
1239
  );
1246
- if (reasoningDetails)
1247
- outMessage.reasoning_details = reasoningDetails;
1240
+ if (reasoningDetails) outMessage.reasoning_details = reasoningDetails;
1248
1241
  out.push(outMessage);
1249
1242
  }
1250
1243
  return out;
@@ -1382,15 +1375,14 @@ function parseDsmlToolCalls(content) {
1382
1375
  let last = -1;
1383
1376
  let tag;
1384
1377
  while ((tag = DSML_ANY_TAG_RE.exec(content)) !== null) {
1385
- if (first === -1)
1386
- first = tag.index;
1378
+ if (first === -1) first = tag.index;
1387
1379
  last = tag.index + tag[0].length;
1388
1380
  }
1389
1381
  const remaining = first === -1 ? content : (content.slice(0, first) + content.slice(last)).trim();
1390
1382
  return { calls, remainingContent: remaining.length ? remaining : null };
1391
1383
  }
1392
1384
  async function callOpenRouter(id, payload, requestTimeoutMs = 12e4, signal) {
1393
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1385
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1394
1386
  const response = await import_axios.default.post(
1395
1387
  "https://openrouter.ai/api/v1/chat/completions",
1396
1388
  payload,
@@ -1450,11 +1442,14 @@ async function callOpenRouter(id, payload, requestTimeoutMs = 12e4, signal) {
1450
1442
  files: [],
1451
1443
  reasoning: (_f = answer.reasoning) != null ? _f : void 0,
1452
1444
  reasoningDetails: (_g = answer.reasoning_details) != null ? _g : void 0,
1445
+ // The upstream provider OpenRouter routed to (e.g. "Baidu") — finer-grained
1446
+ // than the "openrouter" stamp callWithRetries would apply.
1447
+ provider: (_h = response.data.provider) != null ? _h : void 0,
1453
1448
  usage: response.data.usage ? {
1454
1449
  prompt_tokens: response.data.usage.prompt_tokens,
1455
1450
  completion_tokens: response.data.usage.completion_tokens,
1456
1451
  total_tokens: response.data.usage.total_tokens,
1457
- cached_tokens: (_i = (_h = response.data.usage.prompt_tokens_details) == null ? void 0 : _h.cached_tokens) != null ? _i : 0
1452
+ cached_tokens: (_j = (_i = response.data.usage.prompt_tokens_details) == null ? void 0 : _i.cached_tokens) != null ? _j : 0
1458
1453
  } : null
1459
1454
  };
1460
1455
  }
@@ -1504,15 +1499,16 @@ function parseModelString(model) {
1504
1499
  );
1505
1500
  }
1506
1501
  async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeoutMs = 15e3) {
1507
- var _a, _b;
1502
+ var _a, _b, _c;
1508
1503
  try {
1509
1504
  const { provider, modelId } = parseModelString(aiPayload.model);
1510
1505
  const routingPayload = { ...aiPayload, model: modelId };
1511
1506
  const requestTimeoutMs = (_a = aiPayload.requestTimeoutMs) != null ? _a : 12e4;
1512
1507
  const signal = aiPayload.signal;
1508
+ let result;
1513
1509
  switch (provider) {
1514
1510
  case "anthropic":
1515
- return await callAnthropicWithRetries(
1511
+ result = await callAnthropicWithRetries(
1516
1512
  id,
1517
1513
  await prepareAnthropicPayload(id, routingPayload),
1518
1514
  aiConfig,
@@ -1520,8 +1516,9 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
1520
1516
  requestTimeoutMs,
1521
1517
  signal
1522
1518
  );
1519
+ break;
1523
1520
  case "openai":
1524
- return await callOpenAiWithRetries(
1521
+ result = await callOpenAiWithRetries(
1525
1522
  id,
1526
1523
  await prepareOpenAIPayload(id, routingPayload),
1527
1524
  aiConfig,
@@ -1530,34 +1527,39 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
1530
1527
  requestTimeoutMs,
1531
1528
  signal
1532
1529
  );
1530
+ break;
1533
1531
  case "groq":
1534
- return await callGroqWithRetries(
1532
+ result = await callGroqWithRetries(
1535
1533
  id,
1536
1534
  prepareGroqPayload(routingPayload),
1537
1535
  retries,
1538
1536
  requestTimeoutMs,
1539
1537
  signal
1540
1538
  );
1539
+ break;
1541
1540
  case "google":
1542
- return await callGoogleAIWithRetries(
1541
+ result = await callGoogleAIWithRetries(
1543
1542
  id,
1544
1543
  await prepareGoogleAIPayload(id, routingPayload),
1545
1544
  retries,
1546
1545
  requestTimeoutMs,
1547
1546
  signal
1548
1547
  );
1548
+ break;
1549
1549
  case "openrouter":
1550
- return await callOpenRouterWithRetries(
1550
+ result = await callOpenRouterWithRetries(
1551
1551
  id,
1552
1552
  prepareOpenRouterPayload(routingPayload),
1553
1553
  retries,
1554
1554
  requestTimeoutMs,
1555
1555
  signal
1556
1556
  );
1557
+ break;
1557
1558
  }
1559
+ (_b = result.provider) != null ? _b : result.provider = provider;
1560
+ return result;
1558
1561
  } catch (error2) {
1559
- if ((_b = aiPayload.signal) == null ? void 0 : _b.aborted)
1560
- throw error2;
1562
+ if ((_c = aiPayload.signal) == null ? void 0 : _c.aborted) throw error2;
1561
1563
  if (aiPayload.fallbackModel) {
1562
1564
  logger_default.error(
1563
1565
  id,