@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 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
- createOpenRouterRuntime: () => createOpenRouterRuntime
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
- max_tokens: request.maxTokens,
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", { requestId: requestWithId.id });
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", { requestId: requestWithId.id, status: response.status });
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
- createOpenRouterRuntime
1506
+ createConsoleLogger,
1507
+ createOpenRouterRuntime,
1508
+ isRuntimeLoggingEnabled
1415
1509
  });
1416
1510
  //# sourceMappingURL=index.cjs.map