@x12i/openrouter-runtime 1.0.4 → 1.0.6
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 +68 -0
- package/dist/index.cjs +103 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -13
- package/dist/index.d.ts +23 -13
- package/dist/index.js +100 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,33 @@ const response = await runtime.run({
|
|
|
32
32
|
console.log(response.text);
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
## Console logging
|
|
36
|
+
|
|
37
|
+
Turn on runtime console logs with:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
OPENROUTER_RUNTIME_LOGS=true
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
When that env var is `true` / `1` / `yes` / `on`, and you do not pass a custom `logger`, the runtime logs to the console:
|
|
44
|
+
|
|
45
|
+
- `runtime.request.started` — includes `streaming: false` and `entrypoint: "run"`
|
|
46
|
+
- `runtime.request.compiled` — includes `bodyStream: false` and any streaming-related warning codes
|
|
47
|
+
- `runtime.response.normalized`
|
|
48
|
+
- `runtime.executeStreamingChat.called` — if someone hits the reserved streaming method
|
|
49
|
+
- provider retry / function-tool events
|
|
50
|
+
|
|
51
|
+
This package does **not** auto-load `.env`. Put the vars in your environment (shell export, process manager, or host `dotenv`) before calling `createOpenRouterRuntime()`. See `.env.example`.
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
OPEN_ROUTER_KEY=sk-or-...
|
|
57
|
+
OPENROUTER_RUNTIME_LOGS=true
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
You can still pass an explicit `logger` to override the console logger.
|
|
61
|
+
|
|
35
62
|
## Server Tools
|
|
36
63
|
|
|
37
64
|
Enable OpenRouter server tools through `serverTools`:
|
|
@@ -70,3 +97,44 @@ const runtime = createOpenRouterRuntime({
|
|
|
70
97
|
```
|
|
71
98
|
|
|
72
99
|
Function calls are executed locally and looped back to OpenRouter until a final response is produced or `maxToolIterations` is reached.
|
|
100
|
+
|
|
101
|
+
## Streaming
|
|
102
|
+
|
|
103
|
+
### `run()` is non-streaming — always
|
|
104
|
+
|
|
105
|
+
`runtime.run(request)` never streams. It is the only supported execution path today.
|
|
106
|
+
|
|
107
|
+
Use it for background tasks, graph nodes, tool calls, structured output, research jobs, classification, extraction, patch generation, and automation.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
const response = await runtime.run({
|
|
111
|
+
model: "openai/gpt-5.2",
|
|
112
|
+
prompt: "Summarize this document."
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
This sends a normal non-streaming OpenRouter request and returns a completed `RuntimeResponse`.
|
|
117
|
+
|
|
118
|
+
`run()` always sends `stream: false` on the OpenRouter body. Any truthy `rawOpenRouterOverrides.stream` is overwritten and reported as `STREAMING_OVERRIDE_IGNORED_FOR_RUN`. Nested `serverTools.advisor.stream` is also forced off with `ADVISOR_STREAMING_IGNORED_FOR_RUN`.
|
|
119
|
+
|
|
120
|
+
### Streaming lives on a separate method: `executeStreamingChat()`
|
|
121
|
+
|
|
122
|
+
Streaming is **not** available through `run()`, and the old `runtime.stream()` name was **removed on purpose**.
|
|
123
|
+
|
|
124
|
+
Clients that still call `runtime.stream(...)` will break. That is intentional — so mistaken streaming usage is caught immediately.
|
|
125
|
+
|
|
126
|
+
The reserved streaming entrypoint is:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
for await (const event of runtime.executeStreamingChat(request)) {
|
|
130
|
+
// token deltas / stream events (not implemented yet)
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Today, `executeStreamingChat()` throws `STREAMING_NOT_IMPLEMENTED`. Treat this package as a non-streaming runtime until that method is implemented.
|
|
135
|
+
|
|
136
|
+
| Method | Streaming? | Status |
|
|
137
|
+
| --- | --- | --- |
|
|
138
|
+
| `runtime.run(request)` | No | Supported |
|
|
139
|
+
| `runtime.executeStreamingChat(request)` | Yes (future) | Throws `STREAMING_NOT_IMPLEMENTED` |
|
|
140
|
+
| `runtime.stream(request)` | — | **Removed** — breaks by design |
|
package/dist/index.cjs
CHANGED
|
@@ -24,18 +24,45 @@ __export(index_exports, {
|
|
|
24
24
|
RuntimeConfigError: () => RuntimeConfigError,
|
|
25
25
|
buildOpenRouterServerTools: () => buildOpenRouterServerTools,
|
|
26
26
|
compileRuntimeRequest: () => compileRuntimeRequest,
|
|
27
|
-
|
|
27
|
+
createConsoleLogger: () => createConsoleLogger,
|
|
28
|
+
createOpenRouterRuntime: () => createOpenRouterRuntime,
|
|
29
|
+
isRuntimeLoggingEnabled: () => isRuntimeLoggingEnabled
|
|
28
30
|
});
|
|
29
31
|
module.exports = __toCommonJS(index_exports);
|
|
30
32
|
|
|
33
|
+
// src/utils/console-logger.ts
|
|
34
|
+
var TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
35
|
+
function isRuntimeLoggingEnabled(env = typeof process === "undefined" ? void 0 : process.env) {
|
|
36
|
+
const value = env?.OPENROUTER_RUNTIME_LOGS?.trim().toLowerCase();
|
|
37
|
+
return value !== void 0 && TRUTHY.has(value);
|
|
38
|
+
}
|
|
39
|
+
function createConsoleLogger() {
|
|
40
|
+
return {
|
|
41
|
+
debug(event, data) {
|
|
42
|
+
console.debug(`[openrouter-runtime] ${event}`, data ?? "");
|
|
43
|
+
},
|
|
44
|
+
info(event, data) {
|
|
45
|
+
console.info(`[openrouter-runtime] ${event}`, data ?? "");
|
|
46
|
+
},
|
|
47
|
+
warn(event, data) {
|
|
48
|
+
console.warn(`[openrouter-runtime] ${event}`, data ?? "");
|
|
49
|
+
},
|
|
50
|
+
error(event, data) {
|
|
51
|
+
console.error(`[openrouter-runtime] ${event}`, data ?? "");
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
31
56
|
// src/config/defaults.ts
|
|
32
57
|
function resolveRuntimeOptions(options) {
|
|
58
|
+
const logger = options.logger ?? (isRuntimeLoggingEnabled() ? createConsoleLogger() : void 0);
|
|
33
59
|
return {
|
|
34
60
|
...options,
|
|
35
61
|
apiKey: options.apiKey ?? readOpenRouterApiKeyFromEnv() ?? "",
|
|
36
62
|
baseUrl: options.baseUrl ?? "https://openrouter.ai/api/v1",
|
|
37
63
|
defaultHeaders: options.defaultHeaders ?? {},
|
|
38
|
-
fetch: options.fetch ?? globalThis.fetch
|
|
64
|
+
fetch: options.fetch ?? globalThis.fetch,
|
|
65
|
+
...logger ? { logger } : {}
|
|
39
66
|
};
|
|
40
67
|
}
|
|
41
68
|
function resolveDefaults(options) {
|
|
@@ -890,8 +917,9 @@ function buildSubagentTool(policy) {
|
|
|
890
917
|
}
|
|
891
918
|
|
|
892
919
|
// src/runtime/runtime.ts
|
|
893
|
-
function compileRuntimeRequest(request, defaults, options = { apiKey: "" }) {
|
|
920
|
+
function compileRuntimeRequest(request, defaults, options = { apiKey: "" }, context = {}) {
|
|
894
921
|
const warnings = [];
|
|
922
|
+
const effectiveContext = { streaming: false, ...context };
|
|
895
923
|
const requestedModel = request.model ?? options.defaultModel ?? "";
|
|
896
924
|
const normalized = normalizeModel(requestedModel);
|
|
897
925
|
warnings.push(...normalized.warnings);
|
|
@@ -912,6 +940,7 @@ function compileRuntimeRequest(request, defaults, options = { apiKey: "" }) {
|
|
|
912
940
|
const headers = buildHeaders(options);
|
|
913
941
|
const baseBody = buildBaseBody(request, normalized.model, defaults, tools, serverTools);
|
|
914
942
|
const body = apiMode === "chat" ? buildChatBody(request, baseBody) : buildResponsesBody(request, baseBody);
|
|
943
|
+
enforceStreamingPolicyForRun(body, warnings, effectiveContext);
|
|
915
944
|
return {
|
|
916
945
|
apiMode,
|
|
917
946
|
url: endpointFor(options.baseUrl ?? "https://openrouter.ai/api/v1", apiMode),
|
|
@@ -920,6 +949,41 @@ function compileRuntimeRequest(request, defaults, options = { apiKey: "" }) {
|
|
|
920
949
|
warnings
|
|
921
950
|
};
|
|
922
951
|
}
|
|
952
|
+
function enforceStreamingPolicyForRun(body, warnings, context) {
|
|
953
|
+
if (context.streaming) return;
|
|
954
|
+
if (body.stream !== void 0 && body.stream !== false) {
|
|
955
|
+
warnings.push({
|
|
956
|
+
code: "STREAMING_OVERRIDE_IGNORED_FOR_RUN",
|
|
957
|
+
message: "runtime.run() is non-streaming. Use runtime.executeStreamingChat() when streaming is implemented.",
|
|
958
|
+
details: { ignoredStreamValue: body.stream }
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
body.stream = false;
|
|
962
|
+
stripAdvisorStreaming(body, warnings);
|
|
963
|
+
}
|
|
964
|
+
function stripAdvisorStreaming(body, warnings) {
|
|
965
|
+
const tools = body.tools;
|
|
966
|
+
if (!Array.isArray(tools)) return;
|
|
967
|
+
let stripped = 0;
|
|
968
|
+
for (const tool of tools) {
|
|
969
|
+
if (!tool || typeof tool !== "object") continue;
|
|
970
|
+
const record = tool;
|
|
971
|
+
if (record.type !== "openrouter:advisor") continue;
|
|
972
|
+
const parameters = record.parameters;
|
|
973
|
+
if (!parameters || typeof parameters !== "object") continue;
|
|
974
|
+
const params = parameters;
|
|
975
|
+
if (params.stream === void 0 || params.stream === false) continue;
|
|
976
|
+
params.stream = false;
|
|
977
|
+
stripped += 1;
|
|
978
|
+
}
|
|
979
|
+
if (stripped > 0) {
|
|
980
|
+
warnings.push({
|
|
981
|
+
code: "ADVISOR_STREAMING_IGNORED_FOR_RUN",
|
|
982
|
+
message: "runtime.run() disables advisor.stream. Nested advisor streaming is not supported on the non-streaming entrypoint.",
|
|
983
|
+
details: { advisorsStripped: stripped }
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
}
|
|
923
987
|
function resolveApiMode(request, defaultMode = "auto", serverTools) {
|
|
924
988
|
const mode = request.apiMode ?? defaultMode;
|
|
925
989
|
if (mode === "chat") return "chat";
|
|
@@ -952,7 +1016,7 @@ function buildChatBody(request, baseBody) {
|
|
|
952
1016
|
return clean({
|
|
953
1017
|
...baseBody,
|
|
954
1018
|
messages,
|
|
955
|
-
|
|
1019
|
+
max_completion_tokens: request.maxTokens,
|
|
956
1020
|
response_format: request.responseFormat
|
|
957
1021
|
});
|
|
958
1022
|
}
|
|
@@ -1293,6 +1357,14 @@ function optional6(key, value) {
|
|
|
1293
1357
|
return value === void 0 ? {} : { [key]: value };
|
|
1294
1358
|
}
|
|
1295
1359
|
|
|
1360
|
+
// src/runtime/streaming.ts
|
|
1361
|
+
async function* executeStreamingChatNotImplemented() {
|
|
1362
|
+
throw new RuntimeConfigError(
|
|
1363
|
+
"STREAMING_NOT_IMPLEMENTED",
|
|
1364
|
+
"executeStreamingChat() is reserved and not implemented yet. Use runtime.run() for non-streaming execution."
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1296
1368
|
// src/runtime/create-runtime.ts
|
|
1297
1369
|
function createOpenRouterRuntime(options) {
|
|
1298
1370
|
const resolvedOptions = resolveRuntimeOptions(options);
|
|
@@ -1302,22 +1374,42 @@ function createOpenRouterRuntime(options) {
|
|
|
1302
1374
|
async run(request) {
|
|
1303
1375
|
const requestWithId = { ...request, id: request.id ?? createRuntimeId() };
|
|
1304
1376
|
try {
|
|
1305
|
-
resolvedOptions.logger?.info?.("runtime.request.started", {
|
|
1377
|
+
resolvedOptions.logger?.info?.("runtime.request.started", {
|
|
1378
|
+
requestId: requestWithId.id,
|
|
1379
|
+
streaming: false,
|
|
1380
|
+
entrypoint: "run"
|
|
1381
|
+
});
|
|
1306
1382
|
await resolvedOptions.hooks?.beforeCompile?.(requestWithId);
|
|
1307
|
-
const compiled = compileRuntimeRequest(requestWithId, defaults, resolvedOptions);
|
|
1383
|
+
const compiled = compileRuntimeRequest(requestWithId, defaults, resolvedOptions, { streaming: false });
|
|
1308
1384
|
await resolvedOptions.hooks?.afterCompile?.(compiled);
|
|
1309
1385
|
resolvedOptions.logger?.debug?.("runtime.request.compiled", {
|
|
1310
1386
|
requestId: requestWithId.id,
|
|
1311
|
-
apiMode: compiled.apiMode
|
|
1387
|
+
apiMode: compiled.apiMode,
|
|
1388
|
+
streaming: false,
|
|
1389
|
+
bodyStream: compiled.body.stream,
|
|
1390
|
+
streamingWarnings: compiled.warnings.filter((warning) => warning.code.includes("STREAMING") || warning.code.includes("ADVISOR_STREAMING")).map((warning) => warning.code)
|
|
1312
1391
|
});
|
|
1313
1392
|
await resolvedOptions.hooks?.beforeOpenRouterRequest?.(compiled);
|
|
1314
1393
|
const response = compiled.apiMode === "chat" ? await runChat(requestWithId, compiled, context) : await runResponses(requestWithId, compiled, context);
|
|
1315
1394
|
await resolvedOptions.hooks?.afterOpenRouterResponse?.(response.raw.response);
|
|
1316
|
-
resolvedOptions.logger?.info?.("runtime.response.normalized", {
|
|
1395
|
+
resolvedOptions.logger?.info?.("runtime.response.normalized", {
|
|
1396
|
+
requestId: requestWithId.id,
|
|
1397
|
+
status: response.status,
|
|
1398
|
+
streaming: false
|
|
1399
|
+
});
|
|
1317
1400
|
return response;
|
|
1318
1401
|
} catch (error) {
|
|
1319
1402
|
return errorResponse(requestWithId, error);
|
|
1320
1403
|
}
|
|
1404
|
+
},
|
|
1405
|
+
executeStreamingChat(request) {
|
|
1406
|
+
resolvedOptions.logger?.warn?.("runtime.executeStreamingChat.called", {
|
|
1407
|
+
requestId: request.id,
|
|
1408
|
+
streaming: true,
|
|
1409
|
+
entrypoint: "executeStreamingChat",
|
|
1410
|
+
implemented: false
|
|
1411
|
+
});
|
|
1412
|
+
return executeStreamingChatNotImplemented();
|
|
1321
1413
|
}
|
|
1322
1414
|
};
|
|
1323
1415
|
function errorResponse(request, error) {
|
|
@@ -1411,6 +1503,8 @@ function operatorHintFor(code) {
|
|
|
1411
1503
|
RuntimeConfigError,
|
|
1412
1504
|
buildOpenRouterServerTools,
|
|
1413
1505
|
compileRuntimeRequest,
|
|
1414
|
-
|
|
1506
|
+
createConsoleLogger,
|
|
1507
|
+
createOpenRouterRuntime,
|
|
1508
|
+
isRuntimeLoggingEnabled
|
|
1415
1509
|
});
|
|
1416
1510
|
//# sourceMappingURL=index.cjs.map
|