@x12i/openrouter-runtime 1.1.0 → 1.4.0

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
@@ -20,13 +20,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ OPENROUTER_METADATA_LIMITS: () => OPENROUTER_METADATA_LIMITS,
23
24
  OpenRouterHttpError: () => OpenRouterHttpError,
24
25
  RuntimeConfigError: () => RuntimeConfigError,
25
26
  buildOpenRouterServerTools: () => buildOpenRouterServerTools,
26
27
  compileRuntimeRequest: () => compileRuntimeRequest,
27
28
  createConsoleLogger: () => createConsoleLogger,
28
29
  createOpenRouterRuntime: () => createOpenRouterRuntime,
29
- isRuntimeLoggingEnabled: () => isRuntimeLoggingEnabled
30
+ decodeProviderMetadata: () => decodeProviderMetadata,
31
+ isRuntimeLoggingEnabled: () => isRuntimeLoggingEnabled,
32
+ toProviderMetadata: () => toProviderMetadata
30
33
  });
31
34
  module.exports = __toCommonJS(index_exports);
32
35
 
@@ -754,13 +757,22 @@ function endpointFor(baseUrl, apiMode) {
754
757
  }
755
758
 
756
759
  // src/openrouter/headers.ts
757
- function buildHeaders(options) {
760
+ function buildHeaders(options, agentId) {
761
+ const requestAppName = agentId?.trim();
758
762
  return {
759
763
  "content-type": "application/json",
760
764
  authorization: `Bearer ${options.apiKey}`,
761
765
  ...options.appAttribution?.siteUrl ? { "HTTP-Referer": options.appAttribution.siteUrl } : {},
762
- ...options.appAttribution?.appName ? { "X-Title": options.appAttribution.appName } : {},
763
- ...options.defaultHeaders ?? {}
766
+ ...appTitleHeaders(options.appAttribution?.appName),
767
+ ...options.defaultHeaders ?? {},
768
+ ...requestAppName ? appTitleHeaders(requestAppName) : {}
769
+ };
770
+ }
771
+ function appTitleHeaders(name) {
772
+ if (!name) return {};
773
+ return {
774
+ "X-Title": name,
775
+ "X-OpenRouter-Title": name
764
776
  };
765
777
  }
766
778
 
@@ -789,7 +801,7 @@ function cleanParameters(parameters) {
789
801
  function enabled(policy) {
790
802
  return !!policy && policy.mode !== "disabled";
791
803
  }
792
- function buildOpenRouterServerTools(policy) {
804
+ function buildOpenRouterServerTools(policy, fallbackReasoning) {
793
805
  const tools = [];
794
806
  const webSearch = buildWebSearchTool(policy?.webSearch);
795
807
  if (webSearch) tools.push(webSearch);
@@ -801,10 +813,10 @@ function buildOpenRouterServerTools(policy) {
801
813
  if (image) tools.push(image);
802
814
  const patch = buildApplyPatchTool(policy?.applyPatch);
803
815
  if (patch) tools.push(patch);
804
- const fusion = buildFusionTool(policy?.fusion);
816
+ const fusion = buildFusionTool(policy?.fusion, fallbackReasoning);
805
817
  if (fusion) tools.push(fusion);
806
- tools.push(...buildAdvisorTools(policy?.advisor));
807
- const subagent = buildSubagentTool(policy?.subagent);
818
+ tools.push(...buildAdvisorTools(policy?.advisor, fallbackReasoning));
819
+ const subagent = buildSubagentTool(policy?.subagent, fallbackReasoning);
808
820
  if (subagent) tools.push(subagent);
809
821
  return tools;
810
822
  }
@@ -863,7 +875,7 @@ function buildApplyPatchTool(policy) {
863
875
  if (!enabled(policy)) return void 0;
864
876
  return { type: "openrouter:apply_patch" };
865
877
  }
866
- function buildFusionTool(policy) {
878
+ function buildFusionTool(policy, fallbackReasoning) {
867
879
  if (!enabled(policy)) return void 0;
868
880
  return {
869
881
  type: "openrouter:fusion",
@@ -872,12 +884,12 @@ function buildFusionTool(policy) {
872
884
  model: policy?.judgeModel,
873
885
  max_tool_calls: policy?.maxToolCalls ?? 8,
874
886
  max_completion_tokens: policy?.maxCompletionTokens,
875
- reasoning: policy?.reasoning,
887
+ reasoning: policy?.reasoning ?? fallbackReasoning,
876
888
  temperature: policy?.temperature
877
889
  })
878
890
  };
879
891
  }
880
- function buildAdvisorTools(policy) {
892
+ function buildAdvisorTools(policy, fallbackReasoning) {
881
893
  const advisors = Array.isArray(policy) ? policy : policy ? [policy] : [];
882
894
  return advisors.filter(enabled).map((advisor) => {
883
895
  validateNestedTools(advisor.tools);
@@ -892,13 +904,13 @@ function buildAdvisorTools(policy) {
892
904
  stream: advisor.stream,
893
905
  max_tool_calls: advisor.maxToolCalls,
894
906
  max_completion_tokens: advisor.maxCompletionTokens,
895
- reasoning: advisor.reasoning,
907
+ reasoning: advisor.reasoning ?? fallbackReasoning,
896
908
  temperature: advisor.temperature
897
909
  })
898
910
  };
899
911
  });
900
912
  }
901
- function buildSubagentTool(policy) {
913
+ function buildSubagentTool(policy, fallbackReasoning) {
902
914
  if (!policy || policy.mode === "disabled") return void 0;
903
915
  const subagent = policy;
904
916
  validateNestedTools(subagent.tools);
@@ -910,12 +922,86 @@ function buildSubagentTool(policy) {
910
922
  tools: subagent.tools,
911
923
  max_tool_calls: subagent.maxToolCalls,
912
924
  max_completion_tokens: subagent.maxCompletionTokens,
913
- reasoning: subagent.reasoning,
925
+ reasoning: subagent.reasoning ?? fallbackReasoning,
914
926
  temperature: subagent.temperature
915
927
  })
916
928
  };
917
929
  }
918
930
 
931
+ // src/utils/provider-metadata.ts
932
+ var OPENROUTER_METADATA_LIMITS = {
933
+ maxPairs: 16,
934
+ maxKeyLength: 64,
935
+ maxValueLength: 512
936
+ };
937
+ var PROVIDER_RECORD_CHUNK = 256;
938
+ var PROVIDER_RECORD_MAX_CHUNKS = 16;
939
+ function toProviderMetadata(metadata, agentId, _limits, base) {
940
+ return encodeProviderRecord(collectProviderStrings(metadata, agentId, base));
941
+ }
942
+ function decodeProviderMetadata(wire) {
943
+ if (!isRecord2(wire)) return {};
944
+ const parts = [];
945
+ for (let index = 0; ; index += 1) {
946
+ const chunk = wire[`m.${index}`];
947
+ if (typeof chunk !== "string") break;
948
+ parts.push(chunk);
949
+ }
950
+ if (!parts.length) return {};
951
+ const parsed = JSON.parse(Buffer.from(parts.join(""), "base64").toString("utf8"));
952
+ if (!isRecord2(parsed)) return {};
953
+ const record = {};
954
+ for (const [key, value] of Object.entries(parsed)) {
955
+ if (typeof value === "string") record[key] = value;
956
+ }
957
+ return record;
958
+ }
959
+ function encodeProviderRecord(map) {
960
+ const keys = Object.keys(map).sort();
961
+ if (!keys.length) return void 0;
962
+ const ordered = {};
963
+ for (const key of keys) {
964
+ const value = map[key];
965
+ if (value !== void 0) ordered[key] = value;
966
+ }
967
+ const encoded = Buffer.from(JSON.stringify(ordered), "utf8").toString("base64");
968
+ const chunks = Math.ceil(encoded.length / PROVIDER_RECORD_CHUNK);
969
+ if (chunks > PROVIDER_RECORD_MAX_CHUNKS) {
970
+ throw new Error(
971
+ `METADATA_RECORD_TOO_LARGE: provider metadata is ${encoded.length} base64 characters; the maximum is ${PROVIDER_RECORD_CHUNK * PROVIDER_RECORD_MAX_CHUNKS}.`
972
+ );
973
+ }
974
+ const wire = {};
975
+ for (let index = 0; index < chunks; index += 1) {
976
+ wire[`m.${index}`] = encoded.slice(index * PROVIDER_RECORD_CHUNK, (index + 1) * PROVIDER_RECORD_CHUNK);
977
+ }
978
+ return wire;
979
+ }
980
+ function collectProviderStrings(metadata, agentId, base) {
981
+ const map = {};
982
+ appendStrings(map, base);
983
+ appendStrings(map, metadata);
984
+ const trimmedAgentId = agentId?.trim();
985
+ if (trimmedAgentId) map.agentId = trimmedAgentId;
986
+ return map;
987
+ }
988
+ function appendStrings(target, metadata) {
989
+ if (!metadata) return;
990
+ for (const [key, value] of Object.entries(metadata)) {
991
+ const asString = asMetadataString(value);
992
+ if (asString !== void 0) target[key] = asString;
993
+ }
994
+ }
995
+ function asMetadataString(value) {
996
+ if (typeof value === "string") return value;
997
+ if (typeof value === "boolean") return value ? "true" : "false";
998
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
999
+ return void 0;
1000
+ }
1001
+ function isRecord2(value) {
1002
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1003
+ }
1004
+
919
1005
  // src/runtime/runtime.ts
920
1006
  function compileRuntimeRequest(request, defaults, options = { apiKey: "" }, context = {}) {
921
1007
  const warnings = [];
@@ -927,7 +1013,10 @@ function compileRuntimeRequest(request, defaults, options = { apiKey: "" }, cont
927
1013
  const apiMode = resolveApiMode(request, defaults.apiMode, serverTools);
928
1014
  validateRuntimeRequest(request, options, apiMode, normalized.model, serverTools);
929
1015
  const tools = [
930
- ...buildOpenRouterServerTools(serverTools),
1016
+ ...buildOpenRouterServerTools(
1017
+ serverTools,
1018
+ effectiveContext.suppressNestedReasoningFallback ? void 0 : request.reasoning
1019
+ ),
931
1020
  ...(request.functionTools ?? []).map((tool) => ({
932
1021
  type: "function",
933
1022
  function: {
@@ -937,10 +1026,12 @@ function compileRuntimeRequest(request, defaults, options = { apiKey: "" }, cont
937
1026
  }
938
1027
  }))
939
1028
  ];
940
- const headers = buildHeaders(options);
1029
+ const headers = buildHeaders(options, request.agentId);
941
1030
  const baseBody = buildBaseBody(request, normalized.model, defaults, tools, serverTools);
942
- const body = apiMode === "chat" ? buildChatBody(request, baseBody) : buildResponsesBody(request, baseBody);
1031
+ const assembled = apiMode === "chat" ? buildChatBody(request, baseBody) : buildResponsesBody(request, baseBody);
1032
+ let body = effectiveContext.finalizeBody ? effectiveContext.finalizeBody(assembled) : assembled;
943
1033
  enforceStreamingPolicyForRun(body, warnings, effectiveContext);
1034
+ body = attachProviderMetadata(body, request);
944
1035
  return {
945
1036
  apiMode,
946
1037
  url: endpointFor(options.baseUrl ?? "https://openrouter.ai/api/v1", apiMode),
@@ -949,6 +1040,15 @@ function compileRuntimeRequest(request, defaults, options = { apiKey: "" }, cont
949
1040
  warnings
950
1041
  };
951
1042
  }
1043
+ function attachProviderMetadata(body, request) {
1044
+ const fromBody = isRecord(body.metadata) ? body.metadata : void 0;
1045
+ const metadata = toProviderMetadata(request.metadata, request.agentId, OPENROUTER_METADATA_LIMITS, fromBody);
1046
+ if (metadata) return { ...body, metadata };
1047
+ if (body.metadata === void 0) return body;
1048
+ const rest = { ...body };
1049
+ delete rest.metadata;
1050
+ return rest;
1051
+ }
952
1052
  function enforceStreamingPolicyForRun(body, warnings, context) {
953
1053
  if (context.streaming) {
954
1054
  body.stream = true;
@@ -1482,7 +1582,7 @@ function isRetryableStatus2(status) {
1482
1582
  }
1483
1583
 
1484
1584
  // src/runtime/streaming.ts
1485
- async function* executeStreamingChat(request, context) {
1585
+ async function* executeStreamingChat(request, context, call = {}) {
1486
1586
  const requestId = request.id ?? "unknown";
1487
1587
  const options = context.options;
1488
1588
  const defaults = context.defaults;
@@ -1497,7 +1597,11 @@ async function* executeStreamingChat(request, context) {
1497
1597
  ...request,
1498
1598
  apiMode: "chat"
1499
1599
  };
1500
- const compiled = compileRuntimeRequest(streamingRequest, defaults, options, { streaming: true });
1600
+ const compiled = compileRuntimeRequest(streamingRequest, defaults, options, {
1601
+ streaming: true,
1602
+ ...call.finalizeBody ? { finalizeBody: call.finalizeBody } : {},
1603
+ ...call.suppressNestedReasoningFallback ? { suppressNestedReasoningFallback: true } : {}
1604
+ });
1501
1605
  if (compiled.body.stream !== true) {
1502
1606
  throw new RuntimeConfigError(
1503
1607
  "STREAMING_CHAT_MISCONFIGURED",
@@ -1532,6 +1636,10 @@ async function* executeStreamingChat(request, context) {
1532
1636
  if (typeof chunk.model === "string") resolvedModel = chunk.model;
1533
1637
  const choice = Array.isArray(chunk.choices) && isRecord(chunk.choices[0]) ? chunk.choices[0] : void 0;
1534
1638
  const delta = choice && isRecord(choice.delta) ? choice.delta : void 0;
1639
+ if (delta && call.extractStreamReasoning) {
1640
+ const reasoningText = call.extractStreamReasoning(delta);
1641
+ if (reasoningText) yield reasoningDeltaEvent(requestId, reasoningText);
1642
+ }
1535
1643
  if (delta && typeof delta.content === "string" && delta.content.length > 0) {
1536
1644
  text += delta.content;
1537
1645
  yield textDeltaEvent(requestId, delta.content);
@@ -1626,6 +1734,13 @@ function textDeltaEvent(requestId, text) {
1626
1734
  data: { text }
1627
1735
  };
1628
1736
  }
1737
+ function reasoningDeltaEvent(requestId, text) {
1738
+ return {
1739
+ type: "stream.reasoning.delta",
1740
+ requestId,
1741
+ data: { text }
1742
+ };
1743
+ }
1629
1744
  function toolCallDeltaEvent(requestId, data) {
1630
1745
  return {
1631
1746
  type: "stream.tool_call.delta",
@@ -1695,7 +1810,14 @@ function createOpenRouterRuntime(options) {
1695
1810
  const defaults = resolveDefaults(options);
1696
1811
  const context = { options: resolvedOptions, defaults };
1697
1812
  return {
1698
- async run(request) {
1813
+ compile(request, call) {
1814
+ return compileRuntimeRequest(request, defaults, resolvedOptions, {
1815
+ streaming: false,
1816
+ ...call?.finalizeBody ? { finalizeBody: call.finalizeBody } : {},
1817
+ ...call?.suppressNestedReasoningFallback ? { suppressNestedReasoningFallback: true } : {}
1818
+ });
1819
+ },
1820
+ async run(request, call) {
1699
1821
  const requestWithId = { ...request, id: request.id ?? createRuntimeId() };
1700
1822
  try {
1701
1823
  resolvedOptions.logger?.info?.("runtime.request.started", {
@@ -1704,7 +1826,11 @@ function createOpenRouterRuntime(options) {
1704
1826
  entrypoint: "run"
1705
1827
  });
1706
1828
  await resolvedOptions.hooks?.beforeCompile?.(requestWithId);
1707
- const compiled = compileRuntimeRequest(requestWithId, defaults, resolvedOptions, { streaming: false });
1829
+ const compiled = compileRuntimeRequest(requestWithId, defaults, resolvedOptions, {
1830
+ streaming: false,
1831
+ ...call?.finalizeBody ? { finalizeBody: call.finalizeBody } : {},
1832
+ ...call?.suppressNestedReasoningFallback ? { suppressNestedReasoningFallback: true } : {}
1833
+ });
1708
1834
  await resolvedOptions.hooks?.afterCompile?.(compiled);
1709
1835
  resolvedOptions.logger?.debug?.("runtime.request.compiled", {
1710
1836
  requestId: requestWithId.id,
@@ -1726,9 +1852,9 @@ function createOpenRouterRuntime(options) {
1726
1852
  return errorResponse(requestWithId, error);
1727
1853
  }
1728
1854
  },
1729
- executeStreamingChat(request) {
1855
+ executeStreamingChat(request, call) {
1730
1856
  const requestWithId = { ...request, id: request.id ?? createRuntimeId() };
1731
- return executeStreamingChat(requestWithId, context);
1857
+ return executeStreamingChat(requestWithId, context, call);
1732
1858
  }
1733
1859
  };
1734
1860
  function errorResponse(request, error) {
@@ -1818,12 +1944,15 @@ function operatorHintFor(code) {
1818
1944
  }
1819
1945
  // Annotate the CommonJS export names for ESM import in node:
1820
1946
  0 && (module.exports = {
1947
+ OPENROUTER_METADATA_LIMITS,
1821
1948
  OpenRouterHttpError,
1822
1949
  RuntimeConfigError,
1823
1950
  buildOpenRouterServerTools,
1824
1951
  compileRuntimeRequest,
1825
1952
  createConsoleLogger,
1826
1953
  createOpenRouterRuntime,
1827
- isRuntimeLoggingEnabled
1954
+ decodeProviderMetadata,
1955
+ isRuntimeLoggingEnabled,
1956
+ toProviderMetadata
1828
1957
  });
1829
1958
  //# sourceMappingURL=index.cjs.map