190proof 1.0.102 → 1.0.105

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
@@ -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
@@ -123,6 +123,11 @@ interface GenericMessage {
123
123
  * `redacted_thinking` blocks captured in
124
124
  * `ParsedResponseMessage.reasoningDetails`). Preserves the signatures /
125
125
  * encrypted payloads that providers validate on round-trip.
126
+ *
127
+ * Safe to echo regardless of which provider serves the next call: each
128
+ * serializer keeps only its own provider's block shapes (Anthropic keeps
129
+ * `thinking`/`redacted_thinking`; OpenAI-compat keeps `reasoning.*`), so a
130
+ * cross-provider fallback drops foreign blocks instead of 400ing.
126
131
  */
127
132
  reasoningDetails?: any;
128
133
  }
@@ -170,6 +175,15 @@ interface ParsedResponseMessage {
170
175
  * validate. Undefined when the model/provider returns none.
171
176
  */
172
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;
173
187
  usage: {
174
188
  prompt_tokens: number;
175
189
  completion_tokens: number;
package/dist/index.d.ts CHANGED
@@ -123,6 +123,11 @@ interface GenericMessage {
123
123
  * `redacted_thinking` blocks captured in
124
124
  * `ParsedResponseMessage.reasoningDetails`). Preserves the signatures /
125
125
  * encrypted payloads that providers validate on round-trip.
126
+ *
127
+ * Safe to echo regardless of which provider serves the next call: each
128
+ * serializer keeps only its own provider's block shapes (Anthropic keeps
129
+ * `thinking`/`redacted_thinking`; OpenAI-compat keeps `reasoning.*`), so a
130
+ * cross-provider fallback drops foreign blocks instead of 400ing.
126
131
  */
127
132
  reasoningDetails?: any;
128
133
  }
@@ -170,6 +175,15 @@ interface ParsedResponseMessage {
170
175
  * validate. Undefined when the model/provider returns none.
171
176
  */
172
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;
173
187
  usage: {
174
188
  prompt_tokens: number;
175
189
  completion_tokens: number;
package/dist/index.js CHANGED
@@ -320,6 +320,14 @@ function buildOpenAIRequestConfig(identifier, model, config) {
320
320
  }
321
321
  };
322
322
  }
323
+ function filterOpenAICompatReasoningDetails(details) {
324
+ if (!Array.isArray(details))
325
+ return details || void 0;
326
+ const blocks = details.filter(
327
+ (block) => typeof (block == null ? void 0 : block.type) === "string" && block.type.startsWith("reasoning.")
328
+ );
329
+ return blocks.length ? blocks : void 0;
330
+ }
323
331
  async function prepareOpenAIPayload(identifier, payload) {
324
332
  var _a, _b;
325
333
  const preparedPayload = {
@@ -387,8 +395,11 @@ async function prepareOpenAIPayload(identifier, payload) {
387
395
  }
388
396
  if (message.reasoning)
389
397
  outMessage.reasoning = message.reasoning;
390
- if (message.reasoningDetails)
391
- outMessage.reasoning_details = message.reasoningDetails;
398
+ const reasoningDetails = filterOpenAICompatReasoningDetails(
399
+ message.reasoningDetails
400
+ );
401
+ if (reasoningDetails)
402
+ outMessage.reasoning_details = reasoningDetails;
392
403
  preparedPayload.messages.push(outMessage);
393
404
  }
394
405
  return preparedPayload;
@@ -673,7 +684,11 @@ async function prepareAnthropicPayload(_identifier, payload) {
673
684
  model: payload.model,
674
685
  messages: [],
675
686
  functions: payload.functions,
676
- temperature: payload.temperature
687
+ temperature: payload.temperature,
688
+ // Map the generic function_call to Anthropic tool_choice ("none" forces a
689
+ // text-only turn). Only meaningful alongside tools — callAnthropic sends
690
+ // it only when tools are present (tool_choice without tools 400s).
691
+ tool_choice: payload.function_call ? typeof payload.function_call === "string" ? { type: payload.function_call } : { type: "tool", name: payload.function_call.name } : void 0
677
692
  };
678
693
  for (const message of payload.messages) {
679
694
  if (message.role === "system") {
@@ -728,7 +743,9 @@ async function prepareAnthropicPayload(_identifier, payload) {
728
743
  });
729
744
  }
730
745
  }
731
- const leadingBlocks = message.role === "assistant" && Array.isArray(message.reasoningDetails) ? message.reasoningDetails : [];
746
+ const leadingBlocks = message.role === "assistant" && Array.isArray(message.reasoningDetails) ? message.reasoningDetails.filter(
747
+ (block) => (block == null ? void 0 : block.type) === "thinking" || (block == null ? void 0 : block.type) === "redacted_thinking"
748
+ ) : [];
732
749
  const toolUseBlocks = (message.functionCalls || []).map((fc, i) => {
733
750
  var _a;
734
751
  return {
@@ -785,6 +802,8 @@ async function callAnthropic(id, payload, config, requestTimeoutMs = 12e4, signa
785
802
  model: payload.model,
786
803
  messages: anthropicMessages,
787
804
  tools: cachedTools,
805
+ // tool_choice requires tools in the request; drop it otherwise.
806
+ tool_choice: (cachedTools == null ? void 0 : cachedTools.length) ? payload.tool_choice : void 0,
788
807
  temperature: payload.temperature,
789
808
  system: payload.system,
790
809
  max_tokens: 4096
@@ -916,6 +935,13 @@ async function prepareGoogleAIPayload(_identifier, payload) {
916
935
  ...fn.parameters
917
936
  }
918
937
  }))
938
+ } : void 0,
939
+ // Map the generic function_call to Gemini's functionCallingConfig ("none"
940
+ // → mode NONE forces a text-only turn). Only meaningful alongside tools.
941
+ toolConfig: payload.functions && payload.function_call ? {
942
+ functionCallingConfig: {
943
+ mode: typeof payload.function_call === "string" ? payload.function_call === "none" ? "NONE" : "AUTO" : "ANY"
944
+ }
919
945
  } : void 0
920
946
  };
921
947
  const toolNameById = /* @__PURE__ */ new Map();
@@ -1001,6 +1027,9 @@ async function callGoogleAI(id, payload, requestTimeoutMs = 12e4, signal) {
1001
1027
  };
1002
1028
  if (payload.tools)
1003
1029
  requestBody.tools = [payload.tools];
1030
+ if (payload.tools && payload.toolConfig) {
1031
+ requestBody.toolConfig = payload.toolConfig;
1032
+ }
1004
1033
  if (payload.systemInstruction) {
1005
1034
  requestBody.systemInstruction = {
1006
1035
  parts: [{ text: payload.systemInstruction }]
@@ -1227,8 +1256,11 @@ function prepareOpenAICompatMessages(messages) {
1227
1256
  }
1228
1257
  if (message.reasoning)
1229
1258
  outMessage.reasoning = message.reasoning;
1230
- if (message.reasoningDetails)
1231
- outMessage.reasoning_details = message.reasoningDetails;
1259
+ const reasoningDetails = filterOpenAICompatReasoningDetails(
1260
+ message.reasoningDetails
1261
+ );
1262
+ if (reasoningDetails)
1263
+ outMessage.reasoning_details = reasoningDetails;
1232
1264
  out.push(outMessage);
1233
1265
  }
1234
1266
  return out;
@@ -1374,7 +1406,7 @@ function parseDsmlToolCalls(content) {
1374
1406
  return { calls, remainingContent: remaining.length ? remaining : null };
1375
1407
  }
1376
1408
  async function callOpenRouter(id, payload, requestTimeoutMs = 12e4, signal) {
1377
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1409
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1378
1410
  const response = await import_axios.default.post(
1379
1411
  "https://openrouter.ai/api/v1/chat/completions",
1380
1412
  payload,
@@ -1434,11 +1466,14 @@ async function callOpenRouter(id, payload, requestTimeoutMs = 12e4, signal) {
1434
1466
  files: [],
1435
1467
  reasoning: (_f = answer.reasoning) != null ? _f : void 0,
1436
1468
  reasoningDetails: (_g = answer.reasoning_details) != null ? _g : void 0,
1469
+ // The upstream provider OpenRouter routed to (e.g. "Baidu") — finer-grained
1470
+ // than the "openrouter" stamp callWithRetries would apply.
1471
+ provider: (_h = response.data.provider) != null ? _h : void 0,
1437
1472
  usage: response.data.usage ? {
1438
1473
  prompt_tokens: response.data.usage.prompt_tokens,
1439
1474
  completion_tokens: response.data.usage.completion_tokens,
1440
1475
  total_tokens: response.data.usage.total_tokens,
1441
- cached_tokens: (_i = (_h = response.data.usage.prompt_tokens_details) == null ? void 0 : _h.cached_tokens) != null ? _i : 0
1476
+ cached_tokens: (_j = (_i = response.data.usage.prompt_tokens_details) == null ? void 0 : _i.cached_tokens) != null ? _j : 0
1442
1477
  } : null
1443
1478
  };
1444
1479
  }
@@ -1488,15 +1523,16 @@ function parseModelString(model) {
1488
1523
  );
1489
1524
  }
1490
1525
  async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeoutMs = 15e3) {
1491
- var _a, _b;
1526
+ var _a, _b, _c;
1492
1527
  try {
1493
1528
  const { provider, modelId } = parseModelString(aiPayload.model);
1494
1529
  const routingPayload = { ...aiPayload, model: modelId };
1495
1530
  const requestTimeoutMs = (_a = aiPayload.requestTimeoutMs) != null ? _a : 12e4;
1496
1531
  const signal = aiPayload.signal;
1532
+ let result;
1497
1533
  switch (provider) {
1498
1534
  case "anthropic":
1499
- return await callAnthropicWithRetries(
1535
+ result = await callAnthropicWithRetries(
1500
1536
  id,
1501
1537
  await prepareAnthropicPayload(id, routingPayload),
1502
1538
  aiConfig,
@@ -1504,8 +1540,9 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
1504
1540
  requestTimeoutMs,
1505
1541
  signal
1506
1542
  );
1543
+ break;
1507
1544
  case "openai":
1508
- return await callOpenAiWithRetries(
1545
+ result = await callOpenAiWithRetries(
1509
1546
  id,
1510
1547
  await prepareOpenAIPayload(id, routingPayload),
1511
1548
  aiConfig,
@@ -1514,33 +1551,39 @@ async function callWithRetries(id, aiPayload, aiConfig, retries = 5, chunkTimeou
1514
1551
  requestTimeoutMs,
1515
1552
  signal
1516
1553
  );
1554
+ break;
1517
1555
  case "groq":
1518
- return await callGroqWithRetries(
1556
+ result = await callGroqWithRetries(
1519
1557
  id,
1520
1558
  prepareGroqPayload(routingPayload),
1521
1559
  retries,
1522
1560
  requestTimeoutMs,
1523
1561
  signal
1524
1562
  );
1563
+ break;
1525
1564
  case "google":
1526
- return await callGoogleAIWithRetries(
1565
+ result = await callGoogleAIWithRetries(
1527
1566
  id,
1528
1567
  await prepareGoogleAIPayload(id, routingPayload),
1529
1568
  retries,
1530
1569
  requestTimeoutMs,
1531
1570
  signal
1532
1571
  );
1572
+ break;
1533
1573
  case "openrouter":
1534
- return await callOpenRouterWithRetries(
1574
+ result = await callOpenRouterWithRetries(
1535
1575
  id,
1536
1576
  prepareOpenRouterPayload(routingPayload),
1537
1577
  retries,
1538
1578
  requestTimeoutMs,
1539
1579
  signal
1540
1580
  );
1581
+ break;
1541
1582
  }
1583
+ (_b = result.provider) != null ? _b : result.provider = provider;
1584
+ return result;
1542
1585
  } catch (error2) {
1543
- if ((_b = aiPayload.signal) == null ? void 0 : _b.aborted)
1586
+ if ((_c = aiPayload.signal) == null ? void 0 : _c.aborted)
1544
1587
  throw error2;
1545
1588
  if (aiPayload.fallbackModel) {
1546
1589
  logger_default.error(