@x12i/openrouter-runtime 1.0.5 → 1.1.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/README.md +54 -25
- package/dist/index.cjs +409 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -3
- package/dist/index.d.ts +62 -3
- package/dist/index.js +406 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -3,7 +3,12 @@ type ResolvedApiMode = "chat" | "responses";
|
|
|
3
3
|
type ToolMode = "disabled" | "allowed" | "required";
|
|
4
4
|
interface OpenRouterRuntime {
|
|
5
5
|
run(request: RuntimeRequest): Promise<RuntimeResponse>;
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Explicit streaming chat API. Separate from `run()` by design — never an argument, never the default.
|
|
8
|
+
* Always sends OpenRouter Chat Completions with `stream: true` and yields typed SSE events.
|
|
9
|
+
* The old `stream()` name was removed so mistaken/old clients break loudly.
|
|
10
|
+
*/
|
|
11
|
+
executeStreamingChat(request: RuntimeRequest): AsyncIterable<RuntimeStreamEvent>;
|
|
7
12
|
}
|
|
8
13
|
interface OpenRouterRuntimeOptions {
|
|
9
14
|
apiKey?: string;
|
|
@@ -360,9 +365,60 @@ interface RuntimePolicyViolation {
|
|
|
360
365
|
details?: unknown;
|
|
361
366
|
}
|
|
362
367
|
interface RuntimeStreamEvent {
|
|
363
|
-
type:
|
|
368
|
+
type: RuntimeStreamEventType;
|
|
369
|
+
requestId: string;
|
|
364
370
|
data?: unknown;
|
|
365
371
|
}
|
|
372
|
+
type RuntimeStreamEventType = "stream.start" | "stream.text.delta" | "stream.tool_call.delta" | "stream.usage" | "stream.warning" | "stream.error" | "stream.done";
|
|
373
|
+
interface RuntimeStreamStartEvent extends RuntimeStreamEvent {
|
|
374
|
+
type: "stream.start";
|
|
375
|
+
data: {
|
|
376
|
+
model: string;
|
|
377
|
+
apiMode: "chat";
|
|
378
|
+
entrypoint: "executeStreamingChat";
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
interface RuntimeStreamTextDeltaEvent extends RuntimeStreamEvent {
|
|
382
|
+
type: "stream.text.delta";
|
|
383
|
+
data: {
|
|
384
|
+
text: string;
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
interface RuntimeStreamToolCallDeltaEvent extends RuntimeStreamEvent {
|
|
388
|
+
type: "stream.tool_call.delta";
|
|
389
|
+
data: {
|
|
390
|
+
index: number;
|
|
391
|
+
id?: string;
|
|
392
|
+
name?: string;
|
|
393
|
+
argumentsDelta?: string;
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
interface RuntimeStreamUsageEvent extends RuntimeStreamEvent {
|
|
397
|
+
type: "stream.usage";
|
|
398
|
+
data: {
|
|
399
|
+
usage: RuntimeUsage;
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
interface RuntimeStreamWarningEvent extends RuntimeStreamEvent {
|
|
403
|
+
type: "stream.warning";
|
|
404
|
+
data: {
|
|
405
|
+
warning: RuntimeWarning;
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
interface RuntimeStreamErrorEvent extends RuntimeStreamEvent {
|
|
409
|
+
type: "stream.error";
|
|
410
|
+
data: {
|
|
411
|
+
error: RuntimeError;
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
interface RuntimeStreamDoneEvent extends RuntimeStreamEvent {
|
|
415
|
+
type: "stream.done";
|
|
416
|
+
data: {
|
|
417
|
+
text: string;
|
|
418
|
+
finishReason?: string;
|
|
419
|
+
model: string;
|
|
420
|
+
};
|
|
421
|
+
}
|
|
366
422
|
type RuntimePatchApplier = (patch: RuntimePatchProposal) => Promise<unknown> | unknown;
|
|
367
423
|
|
|
368
424
|
declare function createOpenRouterRuntime(options: OpenRouterRuntimeOptions): OpenRouterRuntime;
|
|
@@ -407,4 +463,7 @@ declare class OpenRouterHttpError extends Error {
|
|
|
407
463
|
|
|
408
464
|
declare function buildOpenRouterServerTools(policy?: RuntimeServerToolsPolicy): OpenRouterTool[];
|
|
409
465
|
|
|
410
|
-
|
|
466
|
+
declare function isRuntimeLoggingEnabled(env?: Record<string, string | undefined> | undefined): boolean;
|
|
467
|
+
declare function createConsoleLogger(): RuntimeLogger;
|
|
468
|
+
|
|
469
|
+
export { type AdvisorPolicy, type ApplyPatchPolicy, type CompiledOpenRouterRequest, type DatetimePolicy, type FusionPolicy, type ImageGenerationPolicy, OpenRouterHttpError, type OpenRouterRuntime, type OpenRouterRuntimeOptions, type RuntimeCitation, RuntimeConfigError, type RuntimeDefaults, type RuntimeError, type RuntimeFunctionToolDefinition, type RuntimeGeneratedImage, type RuntimeLogger, type RuntimeMessage, type RuntimePatchProposal, type RuntimePolicyViolation, type RuntimeRequest, type RuntimeResponse, type RuntimeServerToolsPolicy, type RuntimeStreamDoneEvent, type RuntimeStreamErrorEvent, type RuntimeStreamEvent, type RuntimeStreamEventType, type RuntimeStreamStartEvent, type RuntimeStreamTextDeltaEvent, type RuntimeStreamToolCallDeltaEvent, type RuntimeStreamUsageEvent, type RuntimeStreamWarningEvent, type RuntimeToolExecutor, type RuntimeToolUsage, type RuntimeUsage, type RuntimeWarning, type ServerToolKey, type ServerToolUsage, type SubagentPolicy, type WebFetchPolicy, type WebSearchPolicy, buildOpenRouterServerTools, compileRuntimeRequest, createConsoleLogger, createOpenRouterRuntime, isRuntimeLoggingEnabled };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,12 @@ type ResolvedApiMode = "chat" | "responses";
|
|
|
3
3
|
type ToolMode = "disabled" | "allowed" | "required";
|
|
4
4
|
interface OpenRouterRuntime {
|
|
5
5
|
run(request: RuntimeRequest): Promise<RuntimeResponse>;
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Explicit streaming chat API. Separate from `run()` by design — never an argument, never the default.
|
|
8
|
+
* Always sends OpenRouter Chat Completions with `stream: true` and yields typed SSE events.
|
|
9
|
+
* The old `stream()` name was removed so mistaken/old clients break loudly.
|
|
10
|
+
*/
|
|
11
|
+
executeStreamingChat(request: RuntimeRequest): AsyncIterable<RuntimeStreamEvent>;
|
|
7
12
|
}
|
|
8
13
|
interface OpenRouterRuntimeOptions {
|
|
9
14
|
apiKey?: string;
|
|
@@ -360,9 +365,60 @@ interface RuntimePolicyViolation {
|
|
|
360
365
|
details?: unknown;
|
|
361
366
|
}
|
|
362
367
|
interface RuntimeStreamEvent {
|
|
363
|
-
type:
|
|
368
|
+
type: RuntimeStreamEventType;
|
|
369
|
+
requestId: string;
|
|
364
370
|
data?: unknown;
|
|
365
371
|
}
|
|
372
|
+
type RuntimeStreamEventType = "stream.start" | "stream.text.delta" | "stream.tool_call.delta" | "stream.usage" | "stream.warning" | "stream.error" | "stream.done";
|
|
373
|
+
interface RuntimeStreamStartEvent extends RuntimeStreamEvent {
|
|
374
|
+
type: "stream.start";
|
|
375
|
+
data: {
|
|
376
|
+
model: string;
|
|
377
|
+
apiMode: "chat";
|
|
378
|
+
entrypoint: "executeStreamingChat";
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
interface RuntimeStreamTextDeltaEvent extends RuntimeStreamEvent {
|
|
382
|
+
type: "stream.text.delta";
|
|
383
|
+
data: {
|
|
384
|
+
text: string;
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
interface RuntimeStreamToolCallDeltaEvent extends RuntimeStreamEvent {
|
|
388
|
+
type: "stream.tool_call.delta";
|
|
389
|
+
data: {
|
|
390
|
+
index: number;
|
|
391
|
+
id?: string;
|
|
392
|
+
name?: string;
|
|
393
|
+
argumentsDelta?: string;
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
interface RuntimeStreamUsageEvent extends RuntimeStreamEvent {
|
|
397
|
+
type: "stream.usage";
|
|
398
|
+
data: {
|
|
399
|
+
usage: RuntimeUsage;
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
interface RuntimeStreamWarningEvent extends RuntimeStreamEvent {
|
|
403
|
+
type: "stream.warning";
|
|
404
|
+
data: {
|
|
405
|
+
warning: RuntimeWarning;
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
interface RuntimeStreamErrorEvent extends RuntimeStreamEvent {
|
|
409
|
+
type: "stream.error";
|
|
410
|
+
data: {
|
|
411
|
+
error: RuntimeError;
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
interface RuntimeStreamDoneEvent extends RuntimeStreamEvent {
|
|
415
|
+
type: "stream.done";
|
|
416
|
+
data: {
|
|
417
|
+
text: string;
|
|
418
|
+
finishReason?: string;
|
|
419
|
+
model: string;
|
|
420
|
+
};
|
|
421
|
+
}
|
|
366
422
|
type RuntimePatchApplier = (patch: RuntimePatchProposal) => Promise<unknown> | unknown;
|
|
367
423
|
|
|
368
424
|
declare function createOpenRouterRuntime(options: OpenRouterRuntimeOptions): OpenRouterRuntime;
|
|
@@ -407,4 +463,7 @@ declare class OpenRouterHttpError extends Error {
|
|
|
407
463
|
|
|
408
464
|
declare function buildOpenRouterServerTools(policy?: RuntimeServerToolsPolicy): OpenRouterTool[];
|
|
409
465
|
|
|
410
|
-
|
|
466
|
+
declare function isRuntimeLoggingEnabled(env?: Record<string, string | undefined> | undefined): boolean;
|
|
467
|
+
declare function createConsoleLogger(): RuntimeLogger;
|
|
468
|
+
|
|
469
|
+
export { type AdvisorPolicy, type ApplyPatchPolicy, type CompiledOpenRouterRequest, type DatetimePolicy, type FusionPolicy, type ImageGenerationPolicy, OpenRouterHttpError, type OpenRouterRuntime, type OpenRouterRuntimeOptions, type RuntimeCitation, RuntimeConfigError, type RuntimeDefaults, type RuntimeError, type RuntimeFunctionToolDefinition, type RuntimeGeneratedImage, type RuntimeLogger, type RuntimeMessage, type RuntimePatchProposal, type RuntimePolicyViolation, type RuntimeRequest, type RuntimeResponse, type RuntimeServerToolsPolicy, type RuntimeStreamDoneEvent, type RuntimeStreamErrorEvent, type RuntimeStreamEvent, type RuntimeStreamEventType, type RuntimeStreamStartEvent, type RuntimeStreamTextDeltaEvent, type RuntimeStreamToolCallDeltaEvent, type RuntimeStreamUsageEvent, type RuntimeStreamWarningEvent, type RuntimeToolExecutor, type RuntimeToolUsage, type RuntimeUsage, type RuntimeWarning, type ServerToolKey, type ServerToolUsage, type SubagentPolicy, type WebFetchPolicy, type WebSearchPolicy, buildOpenRouterServerTools, compileRuntimeRequest, createConsoleLogger, createOpenRouterRuntime, isRuntimeLoggingEnabled };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
|
+
// src/utils/console-logger.ts
|
|
2
|
+
var TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
3
|
+
function isRuntimeLoggingEnabled(env = typeof process === "undefined" ? void 0 : process.env) {
|
|
4
|
+
const value = env?.OPENROUTER_RUNTIME_LOGS?.trim().toLowerCase();
|
|
5
|
+
return value !== void 0 && TRUTHY.has(value);
|
|
6
|
+
}
|
|
7
|
+
function createConsoleLogger() {
|
|
8
|
+
return {
|
|
9
|
+
debug(event, data) {
|
|
10
|
+
console.debug(`[openrouter-runtime] ${event}`, data ?? "");
|
|
11
|
+
},
|
|
12
|
+
info(event, data) {
|
|
13
|
+
console.info(`[openrouter-runtime] ${event}`, data ?? "");
|
|
14
|
+
},
|
|
15
|
+
warn(event, data) {
|
|
16
|
+
console.warn(`[openrouter-runtime] ${event}`, data ?? "");
|
|
17
|
+
},
|
|
18
|
+
error(event, data) {
|
|
19
|
+
console.error(`[openrouter-runtime] ${event}`, data ?? "");
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
1
24
|
// src/config/defaults.ts
|
|
2
25
|
function resolveRuntimeOptions(options) {
|
|
26
|
+
const logger = options.logger ?? (isRuntimeLoggingEnabled() ? createConsoleLogger() : void 0);
|
|
3
27
|
return {
|
|
4
28
|
...options,
|
|
5
29
|
apiKey: options.apiKey ?? readOpenRouterApiKeyFromEnv() ?? "",
|
|
6
30
|
baseUrl: options.baseUrl ?? "https://openrouter.ai/api/v1",
|
|
7
31
|
defaultHeaders: options.defaultHeaders ?? {},
|
|
8
|
-
fetch: options.fetch ?? globalThis.fetch
|
|
32
|
+
fetch: options.fetch ?? globalThis.fetch,
|
|
33
|
+
...logger ? { logger } : {}
|
|
9
34
|
};
|
|
10
35
|
}
|
|
11
36
|
function resolveDefaults(options) {
|
|
@@ -893,14 +918,42 @@ function compileRuntimeRequest(request, defaults, options = { apiKey: "" }, cont
|
|
|
893
918
|
};
|
|
894
919
|
}
|
|
895
920
|
function enforceStreamingPolicyForRun(body, warnings, context) {
|
|
896
|
-
if (context.streaming)
|
|
897
|
-
|
|
921
|
+
if (context.streaming) {
|
|
922
|
+
body.stream = true;
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
if (body.stream !== void 0 && body.stream !== false) {
|
|
898
926
|
warnings.push({
|
|
899
927
|
code: "STREAMING_OVERRIDE_IGNORED_FOR_RUN",
|
|
900
|
-
message: "runtime.run() is non-streaming. Use runtime.
|
|
928
|
+
message: "runtime.run() is non-streaming. Use runtime.executeStreamingChat() for streaming.",
|
|
929
|
+
details: { ignoredStreamValue: body.stream }
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
body.stream = false;
|
|
933
|
+
stripAdvisorStreaming(body, warnings);
|
|
934
|
+
}
|
|
935
|
+
function stripAdvisorStreaming(body, warnings) {
|
|
936
|
+
const tools = body.tools;
|
|
937
|
+
if (!Array.isArray(tools)) return;
|
|
938
|
+
let stripped = 0;
|
|
939
|
+
for (const tool of tools) {
|
|
940
|
+
if (!tool || typeof tool !== "object") continue;
|
|
941
|
+
const record = tool;
|
|
942
|
+
if (record.type !== "openrouter:advisor") continue;
|
|
943
|
+
const parameters = record.parameters;
|
|
944
|
+
if (!parameters || typeof parameters !== "object") continue;
|
|
945
|
+
const params = parameters;
|
|
946
|
+
if (params.stream === void 0 || params.stream === false) continue;
|
|
947
|
+
params.stream = false;
|
|
948
|
+
stripped += 1;
|
|
949
|
+
}
|
|
950
|
+
if (stripped > 0) {
|
|
951
|
+
warnings.push({
|
|
952
|
+
code: "ADVISOR_STREAMING_IGNORED_FOR_RUN",
|
|
953
|
+
message: "runtime.run() disables advisor.stream. Nested advisor streaming is not supported on the non-streaming entrypoint.",
|
|
954
|
+
details: { advisorsStripped: stripped }
|
|
901
955
|
});
|
|
902
956
|
}
|
|
903
|
-
delete body.stream;
|
|
904
957
|
}
|
|
905
958
|
function resolveApiMode(request, defaultMode = "auto", serverTools) {
|
|
906
959
|
const mode = request.apiMode ?? defaultMode;
|
|
@@ -934,7 +987,7 @@ function buildChatBody(request, baseBody) {
|
|
|
934
987
|
return clean({
|
|
935
988
|
...baseBody,
|
|
936
989
|
messages,
|
|
937
|
-
|
|
990
|
+
max_completion_tokens: request.maxTokens,
|
|
938
991
|
response_format: request.responseFormat
|
|
939
992
|
});
|
|
940
993
|
}
|
|
@@ -1275,12 +1328,333 @@ function optional6(key, value) {
|
|
|
1275
1328
|
return value === void 0 ? {} : { [key]: value };
|
|
1276
1329
|
}
|
|
1277
1330
|
|
|
1331
|
+
// src/openrouter/stream-client.ts
|
|
1332
|
+
async function openOpenRouterStream(compiled, options, timeoutMs) {
|
|
1333
|
+
const controller = new AbortController();
|
|
1334
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
1335
|
+
const cleanup = () => clearTimeout(timeout);
|
|
1336
|
+
try {
|
|
1337
|
+
const response = await options.fetch(compiled.url, {
|
|
1338
|
+
method: "POST",
|
|
1339
|
+
headers: {
|
|
1340
|
+
...compiled.headers,
|
|
1341
|
+
Accept: "text/event-stream"
|
|
1342
|
+
},
|
|
1343
|
+
body: JSON.stringify(compiled.body),
|
|
1344
|
+
signal: controller.signal
|
|
1345
|
+
});
|
|
1346
|
+
if (!response.ok) {
|
|
1347
|
+
const text = await response.text();
|
|
1348
|
+
cleanup();
|
|
1349
|
+
const body = text ? parseBody2(text) : void 0;
|
|
1350
|
+
const message = extractMessage2(body) ?? `OpenRouter stream failed with status ${response.status}.`;
|
|
1351
|
+
throw new OpenRouterHttpError(
|
|
1352
|
+
response.status,
|
|
1353
|
+
classifyProviderFailure2(response.status, message, body),
|
|
1354
|
+
message,
|
|
1355
|
+
body,
|
|
1356
|
+
isRetryableStatus2(response.status)
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
return { response, cleanup };
|
|
1360
|
+
} catch (error) {
|
|
1361
|
+
cleanup();
|
|
1362
|
+
if (error instanceof OpenRouterHttpError) throw error;
|
|
1363
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1364
|
+
throw new OpenRouterHttpError(408, "PROVIDER_GATEWAY_UNREACHABLE", "OpenRouter stream timed out.", void 0, true);
|
|
1365
|
+
}
|
|
1366
|
+
throw new OpenRouterHttpError(
|
|
1367
|
+
503,
|
|
1368
|
+
"PROVIDER_GATEWAY_UNREACHABLE",
|
|
1369
|
+
error instanceof Error ? error.message : "OpenRouter stream failed before receiving a response.",
|
|
1370
|
+
void 0,
|
|
1371
|
+
true
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
async function* parseSseStream(response) {
|
|
1376
|
+
const body = response.body;
|
|
1377
|
+
if (!body) {
|
|
1378
|
+
throw new OpenRouterHttpError(
|
|
1379
|
+
503,
|
|
1380
|
+
"PROVIDER_GATEWAY_UNREACHABLE",
|
|
1381
|
+
"OpenRouter stream response had no body.",
|
|
1382
|
+
void 0,
|
|
1383
|
+
true
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
const reader = body.getReader();
|
|
1387
|
+
const decoder = new TextDecoder();
|
|
1388
|
+
let buffer = "";
|
|
1389
|
+
while (true) {
|
|
1390
|
+
const { done, value } = await reader.read();
|
|
1391
|
+
if (done) break;
|
|
1392
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1393
|
+
const parts = buffer.split(/\r?\n/);
|
|
1394
|
+
buffer = parts.pop() ?? "";
|
|
1395
|
+
for (const part of parts) {
|
|
1396
|
+
const line = part.trim();
|
|
1397
|
+
if (!line || line.startsWith(":")) continue;
|
|
1398
|
+
if (!line.startsWith("data:")) continue;
|
|
1399
|
+
const data = line.slice(5).trim();
|
|
1400
|
+
if (!data) continue;
|
|
1401
|
+
if (data === "[DONE]") return;
|
|
1402
|
+
try {
|
|
1403
|
+
yield JSON.parse(data);
|
|
1404
|
+
} catch {
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
const trailing = buffer.trim();
|
|
1409
|
+
if (trailing.startsWith("data:")) {
|
|
1410
|
+
const data = trailing.slice(5).trim();
|
|
1411
|
+
if (data && data !== "[DONE]") {
|
|
1412
|
+
try {
|
|
1413
|
+
yield JSON.parse(data);
|
|
1414
|
+
} catch {
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
function parseBody2(text) {
|
|
1420
|
+
try {
|
|
1421
|
+
return JSON.parse(text);
|
|
1422
|
+
} catch {
|
|
1423
|
+
return text;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
function extractMessage2(body) {
|
|
1427
|
+
if (typeof body !== "object" || body === null) return void 0;
|
|
1428
|
+
const record = body;
|
|
1429
|
+
const error = record.error;
|
|
1430
|
+
if (typeof error === "object" && error !== null && typeof error.message === "string") {
|
|
1431
|
+
return error.message;
|
|
1432
|
+
}
|
|
1433
|
+
return typeof record.message === "string" ? record.message : void 0;
|
|
1434
|
+
}
|
|
1435
|
+
function classifyProviderFailure2(status, message, body) {
|
|
1436
|
+
const haystack = `${message} ${JSON.stringify(body ?? "")}`.toLowerCase();
|
|
1437
|
+
if (status === 401 || status === 403) return "PROVIDER_AUTH_FAILED";
|
|
1438
|
+
if (status === 402 || /\b(quota|credit|credits|billing|balance|insufficient funds|insufficient credit)\b/.test(haystack)) {
|
|
1439
|
+
return "PROVIDER_QUOTA_EXCEEDED";
|
|
1440
|
+
}
|
|
1441
|
+
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)) {
|
|
1442
|
+
return "PROVIDER_MODEL_NOT_FOUND";
|
|
1443
|
+
}
|
|
1444
|
+
if (status === 429) return "PROVIDER_RATE_LIMITED";
|
|
1445
|
+
if (status === 408 || status >= 500) return "PROVIDER_GATEWAY_UNREACHABLE";
|
|
1446
|
+
return "PROVIDER_REQUEST_FAILED";
|
|
1447
|
+
}
|
|
1448
|
+
function isRetryableStatus2(status) {
|
|
1449
|
+
return status === 429 || status === 500 || status === 502 || status === 503 || status === 504;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1278
1452
|
// src/runtime/streaming.ts
|
|
1279
|
-
async function*
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1453
|
+
async function* executeStreamingChat(request, context) {
|
|
1454
|
+
const requestId = request.id ?? "unknown";
|
|
1455
|
+
const options = context.options;
|
|
1456
|
+
const defaults = context.defaults;
|
|
1457
|
+
options.logger?.info?.("runtime.executeStreamingChat.started", {
|
|
1458
|
+
requestId,
|
|
1459
|
+
streaming: true,
|
|
1460
|
+
entrypoint: "executeStreamingChat"
|
|
1461
|
+
});
|
|
1462
|
+
try {
|
|
1463
|
+
assertStreamingChatRequest(request, defaults);
|
|
1464
|
+
const streamingRequest = {
|
|
1465
|
+
...request,
|
|
1466
|
+
apiMode: "chat"
|
|
1467
|
+
};
|
|
1468
|
+
const compiled = compileRuntimeRequest(streamingRequest, defaults, options, { streaming: true });
|
|
1469
|
+
if (compiled.body.stream !== true) {
|
|
1470
|
+
throw new RuntimeConfigError(
|
|
1471
|
+
"STREAMING_CHAT_MISCONFIGURED",
|
|
1472
|
+
"executeStreamingChat() requires stream=true on the compiled OpenRouter body."
|
|
1473
|
+
);
|
|
1474
|
+
}
|
|
1475
|
+
for (const warning of compiled.warnings) {
|
|
1476
|
+
yield warningEvent(requestId, warning);
|
|
1477
|
+
}
|
|
1478
|
+
const model = typeof compiled.body.model === "string" ? compiled.body.model : "";
|
|
1479
|
+
yield startEvent(requestId, model);
|
|
1480
|
+
await options.hooks?.beforeOpenRouterRequest?.(compiled);
|
|
1481
|
+
const timeoutMs = request.execution?.timeoutMs ?? defaults.timeoutMs;
|
|
1482
|
+
const session = await openOpenRouterStream(compiled, options, timeoutMs);
|
|
1483
|
+
let text = "";
|
|
1484
|
+
let finishReason;
|
|
1485
|
+
let resolvedModel = model;
|
|
1486
|
+
try {
|
|
1487
|
+
for await (const chunk of parseSseStream(session.response)) {
|
|
1488
|
+
if (!isRecord(chunk)) continue;
|
|
1489
|
+
if (chunk.error) {
|
|
1490
|
+
const message = isRecord(chunk.error) && typeof chunk.error.message === "string" ? chunk.error.message : "OpenRouter stream returned an error chunk.";
|
|
1491
|
+
const error = {
|
|
1492
|
+
code: "PROVIDER_REQUEST_FAILED",
|
|
1493
|
+
message,
|
|
1494
|
+
source: "openrouter",
|
|
1495
|
+
details: chunk.error
|
|
1496
|
+
};
|
|
1497
|
+
yield errorEvent(requestId, error);
|
|
1498
|
+
throw new OpenRouterHttpError(500, "PROVIDER_REQUEST_FAILED", message, chunk, false);
|
|
1499
|
+
}
|
|
1500
|
+
if (typeof chunk.model === "string") resolvedModel = chunk.model;
|
|
1501
|
+
const choice = Array.isArray(chunk.choices) && isRecord(chunk.choices[0]) ? chunk.choices[0] : void 0;
|
|
1502
|
+
const delta = choice && isRecord(choice.delta) ? choice.delta : void 0;
|
|
1503
|
+
if (delta && typeof delta.content === "string" && delta.content.length > 0) {
|
|
1504
|
+
text += delta.content;
|
|
1505
|
+
yield textDeltaEvent(requestId, delta.content);
|
|
1506
|
+
}
|
|
1507
|
+
if (delta && Array.isArray(delta.tool_calls)) {
|
|
1508
|
+
for (const toolCall of delta.tool_calls) {
|
|
1509
|
+
if (!isRecord(toolCall)) continue;
|
|
1510
|
+
const index = typeof toolCall.index === "number" ? toolCall.index : 0;
|
|
1511
|
+
const fn = isRecord(toolCall.function) ? toolCall.function : {};
|
|
1512
|
+
yield toolCallDeltaEvent(requestId, {
|
|
1513
|
+
index,
|
|
1514
|
+
...typeof toolCall.id === "string" ? { id: toolCall.id } : {},
|
|
1515
|
+
...typeof fn.name === "string" ? { name: fn.name } : {},
|
|
1516
|
+
...typeof fn.arguments === "string" ? { argumentsDelta: fn.arguments } : {}
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
if (choice && typeof choice.finish_reason === "string") {
|
|
1521
|
+
finishReason = choice.finish_reason;
|
|
1522
|
+
}
|
|
1523
|
+
if (chunk.usage) {
|
|
1524
|
+
const usage = normalizeUsage(chunk.usage);
|
|
1525
|
+
if (usage) yield usageEvent(requestId, usage);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
} finally {
|
|
1529
|
+
session.cleanup();
|
|
1530
|
+
}
|
|
1531
|
+
await options.hooks?.afterOpenRouterResponse?.({ streamed: true, text, finishReason, model: resolvedModel });
|
|
1532
|
+
yield doneEvent(requestId, {
|
|
1533
|
+
text,
|
|
1534
|
+
model: resolvedModel,
|
|
1535
|
+
...finishReason ? { finishReason } : {}
|
|
1536
|
+
});
|
|
1537
|
+
options.logger?.info?.("runtime.executeStreamingChat.completed", {
|
|
1538
|
+
requestId,
|
|
1539
|
+
streaming: true,
|
|
1540
|
+
entrypoint: "executeStreamingChat",
|
|
1541
|
+
finishReason,
|
|
1542
|
+
textLength: text.length
|
|
1543
|
+
});
|
|
1544
|
+
} catch (error) {
|
|
1545
|
+
const runtimeError = toRuntimeError(error);
|
|
1546
|
+
yield errorEvent(requestId, runtimeError);
|
|
1547
|
+
options.logger?.error?.("runtime.executeStreamingChat.failed", {
|
|
1548
|
+
requestId,
|
|
1549
|
+
streaming: true,
|
|
1550
|
+
code: runtimeError.code,
|
|
1551
|
+
message: runtimeError.message
|
|
1552
|
+
});
|
|
1553
|
+
throw error instanceof Error ? error : new Error(runtimeError.message);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
function assertStreamingChatRequest(request, defaults) {
|
|
1557
|
+
if (request.apiMode === "responses") {
|
|
1558
|
+
throw new RuntimeConfigError(
|
|
1559
|
+
"STREAMING_CHAT_REQUIRES_CHAT_API",
|
|
1560
|
+
"executeStreamingChat() only supports Chat Completions. Use runtime.run() for Responses API requests."
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
1563
|
+
if (request.serverTools?.applyPatch?.mode && request.serverTools.applyPatch.mode !== "disabled") {
|
|
1564
|
+
throw new RuntimeConfigError(
|
|
1565
|
+
"STREAMING_CHAT_DOES_NOT_SUPPORT_APPLY_PATCH",
|
|
1566
|
+
"executeStreamingChat() does not support applyPatch. Use runtime.run() instead."
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
if (defaults.serverTools?.applyPatch?.mode && defaults.serverTools.applyPatch.mode !== "disabled") {
|
|
1570
|
+
const requestPatch = request.serverTools?.applyPatch;
|
|
1571
|
+
if (!requestPatch || requestPatch.mode !== "disabled") {
|
|
1572
|
+
throw new RuntimeConfigError(
|
|
1573
|
+
"STREAMING_CHAT_DOES_NOT_SUPPORT_APPLY_PATCH",
|
|
1574
|
+
"executeStreamingChat() does not support applyPatch defaults. Use runtime.run() instead."
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
function startEvent(requestId, model) {
|
|
1580
|
+
return {
|
|
1581
|
+
type: "stream.start",
|
|
1582
|
+
requestId,
|
|
1583
|
+
data: {
|
|
1584
|
+
model,
|
|
1585
|
+
apiMode: "chat",
|
|
1586
|
+
entrypoint: "executeStreamingChat"
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
function textDeltaEvent(requestId, text) {
|
|
1591
|
+
return {
|
|
1592
|
+
type: "stream.text.delta",
|
|
1593
|
+
requestId,
|
|
1594
|
+
data: { text }
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
function toolCallDeltaEvent(requestId, data) {
|
|
1598
|
+
return {
|
|
1599
|
+
type: "stream.tool_call.delta",
|
|
1600
|
+
requestId,
|
|
1601
|
+
data
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
function usageEvent(requestId, usage) {
|
|
1605
|
+
return {
|
|
1606
|
+
type: "stream.usage",
|
|
1607
|
+
requestId,
|
|
1608
|
+
data: { usage }
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1611
|
+
function warningEvent(requestId, warning) {
|
|
1612
|
+
return {
|
|
1613
|
+
type: "stream.warning",
|
|
1614
|
+
requestId,
|
|
1615
|
+
data: { warning }
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
function errorEvent(requestId, error) {
|
|
1619
|
+
return {
|
|
1620
|
+
type: "stream.error",
|
|
1621
|
+
requestId,
|
|
1622
|
+
data: { error }
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
function doneEvent(requestId, data) {
|
|
1626
|
+
return {
|
|
1627
|
+
type: "stream.done",
|
|
1628
|
+
requestId,
|
|
1629
|
+
data
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
function toRuntimeError(error) {
|
|
1633
|
+
if (error instanceof RuntimeConfigError) {
|
|
1634
|
+
return {
|
|
1635
|
+
code: error.code,
|
|
1636
|
+
message: error.message,
|
|
1637
|
+
source: "validation",
|
|
1638
|
+
details: error.details
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
if (error instanceof OpenRouterHttpError) {
|
|
1642
|
+
return {
|
|
1643
|
+
code: error.code,
|
|
1644
|
+
message: error.message,
|
|
1645
|
+
source: "openrouter",
|
|
1646
|
+
retryable: error.retryable,
|
|
1647
|
+
details: {
|
|
1648
|
+
providerAttempts: error.providerAttempts,
|
|
1649
|
+
...error.body === void 0 ? {} : { response: error.body }
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
return {
|
|
1654
|
+
code: "RUNTIME_ERROR",
|
|
1655
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1656
|
+
source: "runtime"
|
|
1657
|
+
};
|
|
1284
1658
|
}
|
|
1285
1659
|
|
|
1286
1660
|
// src/runtime/create-runtime.ts
|
|
@@ -1292,24 +1666,38 @@ function createOpenRouterRuntime(options) {
|
|
|
1292
1666
|
async run(request) {
|
|
1293
1667
|
const requestWithId = { ...request, id: request.id ?? createRuntimeId() };
|
|
1294
1668
|
try {
|
|
1295
|
-
resolvedOptions.logger?.info?.("runtime.request.started", {
|
|
1669
|
+
resolvedOptions.logger?.info?.("runtime.request.started", {
|
|
1670
|
+
requestId: requestWithId.id,
|
|
1671
|
+
streaming: false,
|
|
1672
|
+
entrypoint: "run"
|
|
1673
|
+
});
|
|
1296
1674
|
await resolvedOptions.hooks?.beforeCompile?.(requestWithId);
|
|
1297
1675
|
const compiled = compileRuntimeRequest(requestWithId, defaults, resolvedOptions, { streaming: false });
|
|
1298
1676
|
await resolvedOptions.hooks?.afterCompile?.(compiled);
|
|
1299
1677
|
resolvedOptions.logger?.debug?.("runtime.request.compiled", {
|
|
1300
1678
|
requestId: requestWithId.id,
|
|
1301
|
-
apiMode: compiled.apiMode
|
|
1679
|
+
apiMode: compiled.apiMode,
|
|
1680
|
+
streaming: false,
|
|
1681
|
+
bodyStream: compiled.body.stream,
|
|
1682
|
+
streamingWarnings: compiled.warnings.filter((warning) => warning.code.includes("STREAMING") || warning.code.includes("ADVISOR_STREAMING")).map((warning) => warning.code)
|
|
1302
1683
|
});
|
|
1303
1684
|
await resolvedOptions.hooks?.beforeOpenRouterRequest?.(compiled);
|
|
1304
1685
|
const response = compiled.apiMode === "chat" ? await runChat(requestWithId, compiled, context) : await runResponses(requestWithId, compiled, context);
|
|
1305
1686
|
await resolvedOptions.hooks?.afterOpenRouterResponse?.(response.raw.response);
|
|
1306
|
-
resolvedOptions.logger?.info?.("runtime.response.normalized", {
|
|
1687
|
+
resolvedOptions.logger?.info?.("runtime.response.normalized", {
|
|
1688
|
+
requestId: requestWithId.id,
|
|
1689
|
+
status: response.status,
|
|
1690
|
+
streaming: false
|
|
1691
|
+
});
|
|
1307
1692
|
return response;
|
|
1308
1693
|
} catch (error) {
|
|
1309
1694
|
return errorResponse(requestWithId, error);
|
|
1310
1695
|
}
|
|
1311
1696
|
},
|
|
1312
|
-
|
|
1697
|
+
executeStreamingChat(request) {
|
|
1698
|
+
const requestWithId = { ...request, id: request.id ?? createRuntimeId() };
|
|
1699
|
+
return executeStreamingChat(requestWithId, context);
|
|
1700
|
+
}
|
|
1313
1701
|
};
|
|
1314
1702
|
function errorResponse(request, error) {
|
|
1315
1703
|
const runtimeError = normalizeError(error);
|
|
@@ -1401,6 +1789,8 @@ export {
|
|
|
1401
1789
|
RuntimeConfigError,
|
|
1402
1790
|
buildOpenRouterServerTools,
|
|
1403
1791
|
compileRuntimeRequest,
|
|
1404
|
-
|
|
1792
|
+
createConsoleLogger,
|
|
1793
|
+
createOpenRouterRuntime,
|
|
1794
|
+
isRuntimeLoggingEnabled
|
|
1405
1795
|
};
|
|
1406
1796
|
//# sourceMappingURL=index.js.map
|