@x12i/openrouter-runtime 1.0.6 → 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/LICENSE +21 -21
- package/README.md +160 -140
- package/dist/index.cjs +483 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +113 -6
- package/dist/index.d.ts +113 -6
- package/dist/index.js +479 -34
- package/dist/index.js.map +1 -1
- package/package.json +41 -41
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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,12 +1040,24 @@ 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
|
-
if (context.streaming)
|
|
1053
|
+
if (context.streaming) {
|
|
1054
|
+
body.stream = true;
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
954
1057
|
if (body.stream !== void 0 && body.stream !== false) {
|
|
955
1058
|
warnings.push({
|
|
956
1059
|
code: "STREAMING_OVERRIDE_IGNORED_FOR_RUN",
|
|
957
|
-
message: "runtime.run() is non-streaming. Use runtime.executeStreamingChat()
|
|
1060
|
+
message: "runtime.run() is non-streaming. Use runtime.executeStreamingChat() for streaming.",
|
|
958
1061
|
details: { ignoredStreamValue: body.stream }
|
|
959
1062
|
});
|
|
960
1063
|
}
|
|
@@ -1357,12 +1460,348 @@ function optional6(key, value) {
|
|
|
1357
1460
|
return value === void 0 ? {} : { [key]: value };
|
|
1358
1461
|
}
|
|
1359
1462
|
|
|
1463
|
+
// src/openrouter/stream-client.ts
|
|
1464
|
+
async function openOpenRouterStream(compiled, options, timeoutMs) {
|
|
1465
|
+
const controller = new AbortController();
|
|
1466
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
1467
|
+
const cleanup = () => clearTimeout(timeout);
|
|
1468
|
+
try {
|
|
1469
|
+
const response = await options.fetch(compiled.url, {
|
|
1470
|
+
method: "POST",
|
|
1471
|
+
headers: {
|
|
1472
|
+
...compiled.headers,
|
|
1473
|
+
Accept: "text/event-stream"
|
|
1474
|
+
},
|
|
1475
|
+
body: JSON.stringify(compiled.body),
|
|
1476
|
+
signal: controller.signal
|
|
1477
|
+
});
|
|
1478
|
+
if (!response.ok) {
|
|
1479
|
+
const text = await response.text();
|
|
1480
|
+
cleanup();
|
|
1481
|
+
const body = text ? parseBody2(text) : void 0;
|
|
1482
|
+
const message = extractMessage2(body) ?? `OpenRouter stream failed with status ${response.status}.`;
|
|
1483
|
+
throw new OpenRouterHttpError(
|
|
1484
|
+
response.status,
|
|
1485
|
+
classifyProviderFailure2(response.status, message, body),
|
|
1486
|
+
message,
|
|
1487
|
+
body,
|
|
1488
|
+
isRetryableStatus2(response.status)
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
1491
|
+
return { response, cleanup };
|
|
1492
|
+
} catch (error) {
|
|
1493
|
+
cleanup();
|
|
1494
|
+
if (error instanceof OpenRouterHttpError) throw error;
|
|
1495
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1496
|
+
throw new OpenRouterHttpError(408, "PROVIDER_GATEWAY_UNREACHABLE", "OpenRouter stream timed out.", void 0, true);
|
|
1497
|
+
}
|
|
1498
|
+
throw new OpenRouterHttpError(
|
|
1499
|
+
503,
|
|
1500
|
+
"PROVIDER_GATEWAY_UNREACHABLE",
|
|
1501
|
+
error instanceof Error ? error.message : "OpenRouter stream failed before receiving a response.",
|
|
1502
|
+
void 0,
|
|
1503
|
+
true
|
|
1504
|
+
);
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
async function* parseSseStream(response) {
|
|
1508
|
+
const body = response.body;
|
|
1509
|
+
if (!body) {
|
|
1510
|
+
throw new OpenRouterHttpError(
|
|
1511
|
+
503,
|
|
1512
|
+
"PROVIDER_GATEWAY_UNREACHABLE",
|
|
1513
|
+
"OpenRouter stream response had no body.",
|
|
1514
|
+
void 0,
|
|
1515
|
+
true
|
|
1516
|
+
);
|
|
1517
|
+
}
|
|
1518
|
+
const reader = body.getReader();
|
|
1519
|
+
const decoder = new TextDecoder();
|
|
1520
|
+
let buffer = "";
|
|
1521
|
+
while (true) {
|
|
1522
|
+
const { done, value } = await reader.read();
|
|
1523
|
+
if (done) break;
|
|
1524
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1525
|
+
const parts = buffer.split(/\r?\n/);
|
|
1526
|
+
buffer = parts.pop() ?? "";
|
|
1527
|
+
for (const part of parts) {
|
|
1528
|
+
const line = part.trim();
|
|
1529
|
+
if (!line || line.startsWith(":")) continue;
|
|
1530
|
+
if (!line.startsWith("data:")) continue;
|
|
1531
|
+
const data = line.slice(5).trim();
|
|
1532
|
+
if (!data) continue;
|
|
1533
|
+
if (data === "[DONE]") return;
|
|
1534
|
+
try {
|
|
1535
|
+
yield JSON.parse(data);
|
|
1536
|
+
} catch {
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
const trailing = buffer.trim();
|
|
1541
|
+
if (trailing.startsWith("data:")) {
|
|
1542
|
+
const data = trailing.slice(5).trim();
|
|
1543
|
+
if (data && data !== "[DONE]") {
|
|
1544
|
+
try {
|
|
1545
|
+
yield JSON.parse(data);
|
|
1546
|
+
} catch {
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
function parseBody2(text) {
|
|
1552
|
+
try {
|
|
1553
|
+
return JSON.parse(text);
|
|
1554
|
+
} catch {
|
|
1555
|
+
return text;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
function extractMessage2(body) {
|
|
1559
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
1560
|
+
const record = body;
|
|
1561
|
+
const error = record.error;
|
|
1562
|
+
if (typeof error === "object" && error !== null && typeof error.message === "string") {
|
|
1563
|
+
return error.message;
|
|
1564
|
+
}
|
|
1565
|
+
return typeof record.message === "string" ? record.message : void 0;
|
|
1566
|
+
}
|
|
1567
|
+
function classifyProviderFailure2(status, message, body) {
|
|
1568
|
+
const haystack = `${message} ${JSON.stringify(body ?? "")}`.toLowerCase();
|
|
1569
|
+
if (status === 401 || status === 403) return "PROVIDER_AUTH_FAILED";
|
|
1570
|
+
if (status === 402 || /\b(quota|credit|credits|billing|balance|insufficient funds|insufficient credit)\b/.test(haystack)) {
|
|
1571
|
+
return "PROVIDER_QUOTA_EXCEEDED";
|
|
1572
|
+
}
|
|
1573
|
+
if (status === 404 || /\bmodel\b.*\b(not found|not a valid|invalid|unavailable)\b/.test(haystack) || /\b(no endpoints found|not a valid model id)\b/.test(haystack)) {
|
|
1574
|
+
return "PROVIDER_MODEL_NOT_FOUND";
|
|
1575
|
+
}
|
|
1576
|
+
if (status === 429) return "PROVIDER_RATE_LIMITED";
|
|
1577
|
+
if (status === 408 || status >= 500) return "PROVIDER_GATEWAY_UNREACHABLE";
|
|
1578
|
+
return "PROVIDER_REQUEST_FAILED";
|
|
1579
|
+
}
|
|
1580
|
+
function isRetryableStatus2(status) {
|
|
1581
|
+
return status === 429 || status === 500 || status === 502 || status === 503 || status === 504;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1360
1584
|
// src/runtime/streaming.ts
|
|
1361
|
-
async function*
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1585
|
+
async function* executeStreamingChat(request, context, call = {}) {
|
|
1586
|
+
const requestId = request.id ?? "unknown";
|
|
1587
|
+
const options = context.options;
|
|
1588
|
+
const defaults = context.defaults;
|
|
1589
|
+
options.logger?.info?.("runtime.executeStreamingChat.started", {
|
|
1590
|
+
requestId,
|
|
1591
|
+
streaming: true,
|
|
1592
|
+
entrypoint: "executeStreamingChat"
|
|
1593
|
+
});
|
|
1594
|
+
try {
|
|
1595
|
+
assertStreamingChatRequest(request, defaults);
|
|
1596
|
+
const streamingRequest = {
|
|
1597
|
+
...request,
|
|
1598
|
+
apiMode: "chat"
|
|
1599
|
+
};
|
|
1600
|
+
const compiled = compileRuntimeRequest(streamingRequest, defaults, options, {
|
|
1601
|
+
streaming: true,
|
|
1602
|
+
...call.finalizeBody ? { finalizeBody: call.finalizeBody } : {},
|
|
1603
|
+
...call.suppressNestedReasoningFallback ? { suppressNestedReasoningFallback: true } : {}
|
|
1604
|
+
});
|
|
1605
|
+
if (compiled.body.stream !== true) {
|
|
1606
|
+
throw new RuntimeConfigError(
|
|
1607
|
+
"STREAMING_CHAT_MISCONFIGURED",
|
|
1608
|
+
"executeStreamingChat() requires stream=true on the compiled OpenRouter body."
|
|
1609
|
+
);
|
|
1610
|
+
}
|
|
1611
|
+
for (const warning of compiled.warnings) {
|
|
1612
|
+
yield warningEvent(requestId, warning);
|
|
1613
|
+
}
|
|
1614
|
+
const model = typeof compiled.body.model === "string" ? compiled.body.model : "";
|
|
1615
|
+
yield startEvent(requestId, model);
|
|
1616
|
+
await options.hooks?.beforeOpenRouterRequest?.(compiled);
|
|
1617
|
+
const timeoutMs = request.execution?.timeoutMs ?? defaults.timeoutMs;
|
|
1618
|
+
const session = await openOpenRouterStream(compiled, options, timeoutMs);
|
|
1619
|
+
let text = "";
|
|
1620
|
+
let finishReason;
|
|
1621
|
+
let resolvedModel = model;
|
|
1622
|
+
try {
|
|
1623
|
+
for await (const chunk of parseSseStream(session.response)) {
|
|
1624
|
+
if (!isRecord(chunk)) continue;
|
|
1625
|
+
if (chunk.error) {
|
|
1626
|
+
const message = isRecord(chunk.error) && typeof chunk.error.message === "string" ? chunk.error.message : "OpenRouter stream returned an error chunk.";
|
|
1627
|
+
const error = {
|
|
1628
|
+
code: "PROVIDER_REQUEST_FAILED",
|
|
1629
|
+
message,
|
|
1630
|
+
source: "openrouter",
|
|
1631
|
+
details: chunk.error
|
|
1632
|
+
};
|
|
1633
|
+
yield errorEvent(requestId, error);
|
|
1634
|
+
throw new OpenRouterHttpError(500, "PROVIDER_REQUEST_FAILED", message, chunk, false);
|
|
1635
|
+
}
|
|
1636
|
+
if (typeof chunk.model === "string") resolvedModel = chunk.model;
|
|
1637
|
+
const choice = Array.isArray(chunk.choices) && isRecord(chunk.choices[0]) ? chunk.choices[0] : void 0;
|
|
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
|
+
}
|
|
1643
|
+
if (delta && typeof delta.content === "string" && delta.content.length > 0) {
|
|
1644
|
+
text += delta.content;
|
|
1645
|
+
yield textDeltaEvent(requestId, delta.content);
|
|
1646
|
+
}
|
|
1647
|
+
if (delta && Array.isArray(delta.tool_calls)) {
|
|
1648
|
+
for (const toolCall of delta.tool_calls) {
|
|
1649
|
+
if (!isRecord(toolCall)) continue;
|
|
1650
|
+
const index = typeof toolCall.index === "number" ? toolCall.index : 0;
|
|
1651
|
+
const fn = isRecord(toolCall.function) ? toolCall.function : {};
|
|
1652
|
+
yield toolCallDeltaEvent(requestId, {
|
|
1653
|
+
index,
|
|
1654
|
+
...typeof toolCall.id === "string" ? { id: toolCall.id } : {},
|
|
1655
|
+
...typeof fn.name === "string" ? { name: fn.name } : {},
|
|
1656
|
+
...typeof fn.arguments === "string" ? { argumentsDelta: fn.arguments } : {}
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
if (choice && typeof choice.finish_reason === "string") {
|
|
1661
|
+
finishReason = choice.finish_reason;
|
|
1662
|
+
}
|
|
1663
|
+
if (chunk.usage) {
|
|
1664
|
+
const usage = normalizeUsage(chunk.usage);
|
|
1665
|
+
if (usage) yield usageEvent(requestId, usage);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
} finally {
|
|
1669
|
+
session.cleanup();
|
|
1670
|
+
}
|
|
1671
|
+
await options.hooks?.afterOpenRouterResponse?.({ streamed: true, text, finishReason, model: resolvedModel });
|
|
1672
|
+
yield doneEvent(requestId, {
|
|
1673
|
+
text,
|
|
1674
|
+
model: resolvedModel,
|
|
1675
|
+
...finishReason ? { finishReason } : {}
|
|
1676
|
+
});
|
|
1677
|
+
options.logger?.info?.("runtime.executeStreamingChat.completed", {
|
|
1678
|
+
requestId,
|
|
1679
|
+
streaming: true,
|
|
1680
|
+
entrypoint: "executeStreamingChat",
|
|
1681
|
+
finishReason,
|
|
1682
|
+
textLength: text.length
|
|
1683
|
+
});
|
|
1684
|
+
} catch (error) {
|
|
1685
|
+
const runtimeError = toRuntimeError(error);
|
|
1686
|
+
yield errorEvent(requestId, runtimeError);
|
|
1687
|
+
options.logger?.error?.("runtime.executeStreamingChat.failed", {
|
|
1688
|
+
requestId,
|
|
1689
|
+
streaming: true,
|
|
1690
|
+
code: runtimeError.code,
|
|
1691
|
+
message: runtimeError.message
|
|
1692
|
+
});
|
|
1693
|
+
throw error instanceof Error ? error : new Error(runtimeError.message);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
function assertStreamingChatRequest(request, defaults) {
|
|
1697
|
+
if (request.apiMode === "responses") {
|
|
1698
|
+
throw new RuntimeConfigError(
|
|
1699
|
+
"STREAMING_CHAT_REQUIRES_CHAT_API",
|
|
1700
|
+
"executeStreamingChat() only supports Chat Completions. Use runtime.run() for Responses API requests."
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
if (request.serverTools?.applyPatch?.mode && request.serverTools.applyPatch.mode !== "disabled") {
|
|
1704
|
+
throw new RuntimeConfigError(
|
|
1705
|
+
"STREAMING_CHAT_DOES_NOT_SUPPORT_APPLY_PATCH",
|
|
1706
|
+
"executeStreamingChat() does not support applyPatch. Use runtime.run() instead."
|
|
1707
|
+
);
|
|
1708
|
+
}
|
|
1709
|
+
if (defaults.serverTools?.applyPatch?.mode && defaults.serverTools.applyPatch.mode !== "disabled") {
|
|
1710
|
+
const requestPatch = request.serverTools?.applyPatch;
|
|
1711
|
+
if (!requestPatch || requestPatch.mode !== "disabled") {
|
|
1712
|
+
throw new RuntimeConfigError(
|
|
1713
|
+
"STREAMING_CHAT_DOES_NOT_SUPPORT_APPLY_PATCH",
|
|
1714
|
+
"executeStreamingChat() does not support applyPatch defaults. Use runtime.run() instead."
|
|
1715
|
+
);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
function startEvent(requestId, model) {
|
|
1720
|
+
return {
|
|
1721
|
+
type: "stream.start",
|
|
1722
|
+
requestId,
|
|
1723
|
+
data: {
|
|
1724
|
+
model,
|
|
1725
|
+
apiMode: "chat",
|
|
1726
|
+
entrypoint: "executeStreamingChat"
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
function textDeltaEvent(requestId, text) {
|
|
1731
|
+
return {
|
|
1732
|
+
type: "stream.text.delta",
|
|
1733
|
+
requestId,
|
|
1734
|
+
data: { text }
|
|
1735
|
+
};
|
|
1736
|
+
}
|
|
1737
|
+
function reasoningDeltaEvent(requestId, text) {
|
|
1738
|
+
return {
|
|
1739
|
+
type: "stream.reasoning.delta",
|
|
1740
|
+
requestId,
|
|
1741
|
+
data: { text }
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1744
|
+
function toolCallDeltaEvent(requestId, data) {
|
|
1745
|
+
return {
|
|
1746
|
+
type: "stream.tool_call.delta",
|
|
1747
|
+
requestId,
|
|
1748
|
+
data
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
function usageEvent(requestId, usage) {
|
|
1752
|
+
return {
|
|
1753
|
+
type: "stream.usage",
|
|
1754
|
+
requestId,
|
|
1755
|
+
data: { usage }
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
function warningEvent(requestId, warning) {
|
|
1759
|
+
return {
|
|
1760
|
+
type: "stream.warning",
|
|
1761
|
+
requestId,
|
|
1762
|
+
data: { warning }
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
function errorEvent(requestId, error) {
|
|
1766
|
+
return {
|
|
1767
|
+
type: "stream.error",
|
|
1768
|
+
requestId,
|
|
1769
|
+
data: { error }
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
function doneEvent(requestId, data) {
|
|
1773
|
+
return {
|
|
1774
|
+
type: "stream.done",
|
|
1775
|
+
requestId,
|
|
1776
|
+
data
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
function toRuntimeError(error) {
|
|
1780
|
+
if (error instanceof RuntimeConfigError) {
|
|
1781
|
+
return {
|
|
1782
|
+
code: error.code,
|
|
1783
|
+
message: error.message,
|
|
1784
|
+
source: "validation",
|
|
1785
|
+
details: error.details
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
if (error instanceof OpenRouterHttpError) {
|
|
1789
|
+
return {
|
|
1790
|
+
code: error.code,
|
|
1791
|
+
message: error.message,
|
|
1792
|
+
source: "openrouter",
|
|
1793
|
+
retryable: error.retryable,
|
|
1794
|
+
details: {
|
|
1795
|
+
providerAttempts: error.providerAttempts,
|
|
1796
|
+
...error.body === void 0 ? {} : { response: error.body }
|
|
1797
|
+
}
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
return {
|
|
1801
|
+
code: "RUNTIME_ERROR",
|
|
1802
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1803
|
+
source: "runtime"
|
|
1804
|
+
};
|
|
1366
1805
|
}
|
|
1367
1806
|
|
|
1368
1807
|
// src/runtime/create-runtime.ts
|
|
@@ -1371,7 +1810,14 @@ function createOpenRouterRuntime(options) {
|
|
|
1371
1810
|
const defaults = resolveDefaults(options);
|
|
1372
1811
|
const context = { options: resolvedOptions, defaults };
|
|
1373
1812
|
return {
|
|
1374
|
-
|
|
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) {
|
|
1375
1821
|
const requestWithId = { ...request, id: request.id ?? createRuntimeId() };
|
|
1376
1822
|
try {
|
|
1377
1823
|
resolvedOptions.logger?.info?.("runtime.request.started", {
|
|
@@ -1380,7 +1826,11 @@ function createOpenRouterRuntime(options) {
|
|
|
1380
1826
|
entrypoint: "run"
|
|
1381
1827
|
});
|
|
1382
1828
|
await resolvedOptions.hooks?.beforeCompile?.(requestWithId);
|
|
1383
|
-
const compiled = compileRuntimeRequest(requestWithId, defaults, resolvedOptions, {
|
|
1829
|
+
const compiled = compileRuntimeRequest(requestWithId, defaults, resolvedOptions, {
|
|
1830
|
+
streaming: false,
|
|
1831
|
+
...call?.finalizeBody ? { finalizeBody: call.finalizeBody } : {},
|
|
1832
|
+
...call?.suppressNestedReasoningFallback ? { suppressNestedReasoningFallback: true } : {}
|
|
1833
|
+
});
|
|
1384
1834
|
await resolvedOptions.hooks?.afterCompile?.(compiled);
|
|
1385
1835
|
resolvedOptions.logger?.debug?.("runtime.request.compiled", {
|
|
1386
1836
|
requestId: requestWithId.id,
|
|
@@ -1402,14 +1852,9 @@ function createOpenRouterRuntime(options) {
|
|
|
1402
1852
|
return errorResponse(requestWithId, error);
|
|
1403
1853
|
}
|
|
1404
1854
|
},
|
|
1405
|
-
executeStreamingChat(request) {
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
streaming: true,
|
|
1409
|
-
entrypoint: "executeStreamingChat",
|
|
1410
|
-
implemented: false
|
|
1411
|
-
});
|
|
1412
|
-
return executeStreamingChatNotImplemented();
|
|
1855
|
+
executeStreamingChat(request, call) {
|
|
1856
|
+
const requestWithId = { ...request, id: request.id ?? createRuntimeId() };
|
|
1857
|
+
return executeStreamingChat(requestWithId, context, call);
|
|
1413
1858
|
}
|
|
1414
1859
|
};
|
|
1415
1860
|
function errorResponse(request, error) {
|
|
@@ -1499,12 +1944,15 @@ function operatorHintFor(code) {
|
|
|
1499
1944
|
}
|
|
1500
1945
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1501
1946
|
0 && (module.exports = {
|
|
1947
|
+
OPENROUTER_METADATA_LIMITS,
|
|
1502
1948
|
OpenRouterHttpError,
|
|
1503
1949
|
RuntimeConfigError,
|
|
1504
1950
|
buildOpenRouterServerTools,
|
|
1505
1951
|
compileRuntimeRequest,
|
|
1506
1952
|
createConsoleLogger,
|
|
1507
1953
|
createOpenRouterRuntime,
|
|
1508
|
-
|
|
1954
|
+
decodeProviderMetadata,
|
|
1955
|
+
isRuntimeLoggingEnabled,
|
|
1956
|
+
toProviderMetadata
|
|
1509
1957
|
});
|
|
1510
1958
|
//# sourceMappingURL=index.cjs.map
|