@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 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`:
@@ -73,7 +100,9 @@ Function calls are executed locally and looped back to OpenRouter until a final
73
100
 
74
101
  ## Streaming
75
102
 
76
- `runtime.run(request)` is non-streaming by design. Use it for background tasks, graph nodes, tool calls, structured output, research jobs, classification, extraction, patch generation, and automation.
103
+ Streaming is a **separate API**. It is never an argument on `run()`, never the default, and never available through the removed `stream()` name.
104
+
105
+ ### `run()` — non-streaming only
77
106
 
78
107
  ```ts
79
108
  const response = await runtime.run({
@@ -82,35 +111,35 @@ const response = await runtime.run({
82
111
  });
83
112
  ```
84
113
 
85
- This sends a normal non-streaming OpenRouter request and returns a completed `RuntimeResponse`.
114
+ - Always sends `stream: false`
115
+ - Returns a completed `RuntimeResponse`
116
+ - Use for tools, research, extraction, patches, automation
86
117
 
87
- ### Why `run()` is not streaming
118
+ Any truthy `rawOpenRouterOverrides.stream` is overwritten (`STREAMING_OVERRIDE_IGNORED_FOR_RUN`). Nested `advisor.stream` is forced off (`ADVISOR_STREAMING_IGNORED_FOR_RUN`).
88
119
 
89
- `run()` is designed for reliable runtime execution, not token-by-token UI delivery.
90
-
91
- Most runtime calls are background operations: graph nodes, tool loops, extraction, classification, research, patch generation, and structured output. For those workloads, streaming adds connection complexity without improving the final result.
92
-
93
- When streaming support is added, it will be available through `runtime.stream(request)`, not through `runtime.run(request)`.
120
+ ### `executeStreamingChat()` — streaming only
94
121
 
95
122
  ```ts
96
- // Future API
97
- for await (const event of runtime.stream(request)) {
98
- // send token deltas to browser/client
99
- }
100
- ```
101
-
102
- Today, `stream()` is reserved and throws `STREAMING_NOT_IMPLEMENTED`. The package should be treated as a non-streaming runtime.
103
-
104
- Do not pass streaming through raw overrides:
105
-
106
- ```ts
107
- await runtime.run({
123
+ for await (const event of runtime.executeStreamingChat({
108
124
  model: "openai/gpt-5.2",
109
- prompt: "Summarize this document.",
110
- rawOpenRouterOverrides: {
111
- stream: true
125
+ prompt: "Say hello"
126
+ })) {
127
+ if (event.type === "stream.text.delta") {
128
+ process.stdout.write(event.data.text);
112
129
  }
113
- });
130
+ if (event.type === "stream.done") {
131
+ console.log("\nfinal:", event.data.text);
132
+ }
133
+ }
114
134
  ```
115
135
 
116
- If `rawOpenRouterOverrides.stream = true` is provided to `runtime.run()`, it is ignored and reported as a `STREAMING_OVERRIDE_IGNORED_FOR_RUN` warning.
136
+ - Always sends `stream: true` (Chat Completions SSE)
137
+ - Yields typed events: `stream.start`, `stream.text.delta`, `stream.tool_call.delta`, `stream.usage`, `stream.warning`, `stream.error`, `stream.done`
138
+ - Chat Completions only — Responses / `applyPatch` must use `run()`
139
+ - Local function-tool loops are not run here; use `run()` for tool iteration
140
+
141
+ | Method | Streaming? | Notes |
142
+ | --- | --- | --- |
143
+ | `runtime.run(request)` | No | Default execution path |
144
+ | `runtime.executeStreamingChat(request)` | Yes | Explicit streaming API |
145
+ | `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) {
@@ -923,14 +950,42 @@ function compileRuntimeRequest(request, defaults, options = { apiKey: "" }, cont
923
950
  };
924
951
  }
925
952
  function enforceStreamingPolicyForRun(body, warnings, context) {
926
- if (context.streaming) return;
927
- if (body.stream === true) {
953
+ if (context.streaming) {
954
+ body.stream = true;
955
+ return;
956
+ }
957
+ if (body.stream !== void 0 && body.stream !== false) {
928
958
  warnings.push({
929
959
  code: "STREAMING_OVERRIDE_IGNORED_FOR_RUN",
930
- message: "runtime.run() is non-streaming. Use runtime.stream() when streaming is implemented."
960
+ message: "runtime.run() is non-streaming. Use runtime.executeStreamingChat() for streaming.",
961
+ details: { ignoredStreamValue: body.stream }
962
+ });
963
+ }
964
+ body.stream = false;
965
+ stripAdvisorStreaming(body, warnings);
966
+ }
967
+ function stripAdvisorStreaming(body, warnings) {
968
+ const tools = body.tools;
969
+ if (!Array.isArray(tools)) return;
970
+ let stripped = 0;
971
+ for (const tool of tools) {
972
+ if (!tool || typeof tool !== "object") continue;
973
+ const record = tool;
974
+ if (record.type !== "openrouter:advisor") continue;
975
+ const parameters = record.parameters;
976
+ if (!parameters || typeof parameters !== "object") continue;
977
+ const params = parameters;
978
+ if (params.stream === void 0 || params.stream === false) continue;
979
+ params.stream = false;
980
+ stripped += 1;
981
+ }
982
+ if (stripped > 0) {
983
+ warnings.push({
984
+ code: "ADVISOR_STREAMING_IGNORED_FOR_RUN",
985
+ message: "runtime.run() disables advisor.stream. Nested advisor streaming is not supported on the non-streaming entrypoint.",
986
+ details: { advisorsStripped: stripped }
931
987
  });
932
988
  }
933
- delete body.stream;
934
989
  }
935
990
  function resolveApiMode(request, defaultMode = "auto", serverTools) {
936
991
  const mode = request.apiMode ?? defaultMode;
@@ -964,7 +1019,7 @@ function buildChatBody(request, baseBody) {
964
1019
  return clean({
965
1020
  ...baseBody,
966
1021
  messages,
967
- max_tokens: request.maxTokens,
1022
+ max_completion_tokens: request.maxTokens,
968
1023
  response_format: request.responseFormat
969
1024
  });
970
1025
  }
@@ -1305,12 +1360,333 @@ function optional6(key, value) {
1305
1360
  return value === void 0 ? {} : { [key]: value };
1306
1361
  }
1307
1362
 
1363
+ // src/openrouter/stream-client.ts
1364
+ async function openOpenRouterStream(compiled, options, timeoutMs) {
1365
+ const controller = new AbortController();
1366
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
1367
+ const cleanup = () => clearTimeout(timeout);
1368
+ try {
1369
+ const response = await options.fetch(compiled.url, {
1370
+ method: "POST",
1371
+ headers: {
1372
+ ...compiled.headers,
1373
+ Accept: "text/event-stream"
1374
+ },
1375
+ body: JSON.stringify(compiled.body),
1376
+ signal: controller.signal
1377
+ });
1378
+ if (!response.ok) {
1379
+ const text = await response.text();
1380
+ cleanup();
1381
+ const body = text ? parseBody2(text) : void 0;
1382
+ const message = extractMessage2(body) ?? `OpenRouter stream failed with status ${response.status}.`;
1383
+ throw new OpenRouterHttpError(
1384
+ response.status,
1385
+ classifyProviderFailure2(response.status, message, body),
1386
+ message,
1387
+ body,
1388
+ isRetryableStatus2(response.status)
1389
+ );
1390
+ }
1391
+ return { response, cleanup };
1392
+ } catch (error) {
1393
+ cleanup();
1394
+ if (error instanceof OpenRouterHttpError) throw error;
1395
+ if (error instanceof Error && error.name === "AbortError") {
1396
+ throw new OpenRouterHttpError(408, "PROVIDER_GATEWAY_UNREACHABLE", "OpenRouter stream timed out.", void 0, true);
1397
+ }
1398
+ throw new OpenRouterHttpError(
1399
+ 503,
1400
+ "PROVIDER_GATEWAY_UNREACHABLE",
1401
+ error instanceof Error ? error.message : "OpenRouter stream failed before receiving a response.",
1402
+ void 0,
1403
+ true
1404
+ );
1405
+ }
1406
+ }
1407
+ async function* parseSseStream(response) {
1408
+ const body = response.body;
1409
+ if (!body) {
1410
+ throw new OpenRouterHttpError(
1411
+ 503,
1412
+ "PROVIDER_GATEWAY_UNREACHABLE",
1413
+ "OpenRouter stream response had no body.",
1414
+ void 0,
1415
+ true
1416
+ );
1417
+ }
1418
+ const reader = body.getReader();
1419
+ const decoder = new TextDecoder();
1420
+ let buffer = "";
1421
+ while (true) {
1422
+ const { done, value } = await reader.read();
1423
+ if (done) break;
1424
+ buffer += decoder.decode(value, { stream: true });
1425
+ const parts = buffer.split(/\r?\n/);
1426
+ buffer = parts.pop() ?? "";
1427
+ for (const part of parts) {
1428
+ const line = part.trim();
1429
+ if (!line || line.startsWith(":")) continue;
1430
+ if (!line.startsWith("data:")) continue;
1431
+ const data = line.slice(5).trim();
1432
+ if (!data) continue;
1433
+ if (data === "[DONE]") return;
1434
+ try {
1435
+ yield JSON.parse(data);
1436
+ } catch {
1437
+ }
1438
+ }
1439
+ }
1440
+ const trailing = buffer.trim();
1441
+ if (trailing.startsWith("data:")) {
1442
+ const data = trailing.slice(5).trim();
1443
+ if (data && data !== "[DONE]") {
1444
+ try {
1445
+ yield JSON.parse(data);
1446
+ } catch {
1447
+ }
1448
+ }
1449
+ }
1450
+ }
1451
+ function parseBody2(text) {
1452
+ try {
1453
+ return JSON.parse(text);
1454
+ } catch {
1455
+ return text;
1456
+ }
1457
+ }
1458
+ function extractMessage2(body) {
1459
+ if (typeof body !== "object" || body === null) return void 0;
1460
+ const record = body;
1461
+ const error = record.error;
1462
+ if (typeof error === "object" && error !== null && typeof error.message === "string") {
1463
+ return error.message;
1464
+ }
1465
+ return typeof record.message === "string" ? record.message : void 0;
1466
+ }
1467
+ function classifyProviderFailure2(status, message, body) {
1468
+ const haystack = `${message} ${JSON.stringify(body ?? "")}`.toLowerCase();
1469
+ if (status === 401 || status === 403) return "PROVIDER_AUTH_FAILED";
1470
+ if (status === 402 || /\b(quota|credit|credits|billing|balance|insufficient funds|insufficient credit)\b/.test(haystack)) {
1471
+ return "PROVIDER_QUOTA_EXCEEDED";
1472
+ }
1473
+ 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)) {
1474
+ return "PROVIDER_MODEL_NOT_FOUND";
1475
+ }
1476
+ if (status === 429) return "PROVIDER_RATE_LIMITED";
1477
+ if (status === 408 || status >= 500) return "PROVIDER_GATEWAY_UNREACHABLE";
1478
+ return "PROVIDER_REQUEST_FAILED";
1479
+ }
1480
+ function isRetryableStatus2(status) {
1481
+ return status === 429 || status === 500 || status === 502 || status === 503 || status === 504;
1482
+ }
1483
+
1308
1484
  // src/runtime/streaming.ts
1309
- async function* streamNotImplemented() {
1310
- throw new RuntimeConfigError(
1311
- "STREAMING_NOT_IMPLEMENTED",
1312
- "Streaming is reserved for a later phase. Use runtime.run() for non-streaming execution."
1313
- );
1485
+ async function* executeStreamingChat(request, context) {
1486
+ const requestId = request.id ?? "unknown";
1487
+ const options = context.options;
1488
+ const defaults = context.defaults;
1489
+ options.logger?.info?.("runtime.executeStreamingChat.started", {
1490
+ requestId,
1491
+ streaming: true,
1492
+ entrypoint: "executeStreamingChat"
1493
+ });
1494
+ try {
1495
+ assertStreamingChatRequest(request, defaults);
1496
+ const streamingRequest = {
1497
+ ...request,
1498
+ apiMode: "chat"
1499
+ };
1500
+ const compiled = compileRuntimeRequest(streamingRequest, defaults, options, { streaming: true });
1501
+ if (compiled.body.stream !== true) {
1502
+ throw new RuntimeConfigError(
1503
+ "STREAMING_CHAT_MISCONFIGURED",
1504
+ "executeStreamingChat() requires stream=true on the compiled OpenRouter body."
1505
+ );
1506
+ }
1507
+ for (const warning of compiled.warnings) {
1508
+ yield warningEvent(requestId, warning);
1509
+ }
1510
+ const model = typeof compiled.body.model === "string" ? compiled.body.model : "";
1511
+ yield startEvent(requestId, model);
1512
+ await options.hooks?.beforeOpenRouterRequest?.(compiled);
1513
+ const timeoutMs = request.execution?.timeoutMs ?? defaults.timeoutMs;
1514
+ const session = await openOpenRouterStream(compiled, options, timeoutMs);
1515
+ let text = "";
1516
+ let finishReason;
1517
+ let resolvedModel = model;
1518
+ try {
1519
+ for await (const chunk of parseSseStream(session.response)) {
1520
+ if (!isRecord(chunk)) continue;
1521
+ if (chunk.error) {
1522
+ const message = isRecord(chunk.error) && typeof chunk.error.message === "string" ? chunk.error.message : "OpenRouter stream returned an error chunk.";
1523
+ const error = {
1524
+ code: "PROVIDER_REQUEST_FAILED",
1525
+ message,
1526
+ source: "openrouter",
1527
+ details: chunk.error
1528
+ };
1529
+ yield errorEvent(requestId, error);
1530
+ throw new OpenRouterHttpError(500, "PROVIDER_REQUEST_FAILED", message, chunk, false);
1531
+ }
1532
+ if (typeof chunk.model === "string") resolvedModel = chunk.model;
1533
+ const choice = Array.isArray(chunk.choices) && isRecord(chunk.choices[0]) ? chunk.choices[0] : void 0;
1534
+ const delta = choice && isRecord(choice.delta) ? choice.delta : void 0;
1535
+ if (delta && typeof delta.content === "string" && delta.content.length > 0) {
1536
+ text += delta.content;
1537
+ yield textDeltaEvent(requestId, delta.content);
1538
+ }
1539
+ if (delta && Array.isArray(delta.tool_calls)) {
1540
+ for (const toolCall of delta.tool_calls) {
1541
+ if (!isRecord(toolCall)) continue;
1542
+ const index = typeof toolCall.index === "number" ? toolCall.index : 0;
1543
+ const fn = isRecord(toolCall.function) ? toolCall.function : {};
1544
+ yield toolCallDeltaEvent(requestId, {
1545
+ index,
1546
+ ...typeof toolCall.id === "string" ? { id: toolCall.id } : {},
1547
+ ...typeof fn.name === "string" ? { name: fn.name } : {},
1548
+ ...typeof fn.arguments === "string" ? { argumentsDelta: fn.arguments } : {}
1549
+ });
1550
+ }
1551
+ }
1552
+ if (choice && typeof choice.finish_reason === "string") {
1553
+ finishReason = choice.finish_reason;
1554
+ }
1555
+ if (chunk.usage) {
1556
+ const usage = normalizeUsage(chunk.usage);
1557
+ if (usage) yield usageEvent(requestId, usage);
1558
+ }
1559
+ }
1560
+ } finally {
1561
+ session.cleanup();
1562
+ }
1563
+ await options.hooks?.afterOpenRouterResponse?.({ streamed: true, text, finishReason, model: resolvedModel });
1564
+ yield doneEvent(requestId, {
1565
+ text,
1566
+ model: resolvedModel,
1567
+ ...finishReason ? { finishReason } : {}
1568
+ });
1569
+ options.logger?.info?.("runtime.executeStreamingChat.completed", {
1570
+ requestId,
1571
+ streaming: true,
1572
+ entrypoint: "executeStreamingChat",
1573
+ finishReason,
1574
+ textLength: text.length
1575
+ });
1576
+ } catch (error) {
1577
+ const runtimeError = toRuntimeError(error);
1578
+ yield errorEvent(requestId, runtimeError);
1579
+ options.logger?.error?.("runtime.executeStreamingChat.failed", {
1580
+ requestId,
1581
+ streaming: true,
1582
+ code: runtimeError.code,
1583
+ message: runtimeError.message
1584
+ });
1585
+ throw error instanceof Error ? error : new Error(runtimeError.message);
1586
+ }
1587
+ }
1588
+ function assertStreamingChatRequest(request, defaults) {
1589
+ if (request.apiMode === "responses") {
1590
+ throw new RuntimeConfigError(
1591
+ "STREAMING_CHAT_REQUIRES_CHAT_API",
1592
+ "executeStreamingChat() only supports Chat Completions. Use runtime.run() for Responses API requests."
1593
+ );
1594
+ }
1595
+ if (request.serverTools?.applyPatch?.mode && request.serverTools.applyPatch.mode !== "disabled") {
1596
+ throw new RuntimeConfigError(
1597
+ "STREAMING_CHAT_DOES_NOT_SUPPORT_APPLY_PATCH",
1598
+ "executeStreamingChat() does not support applyPatch. Use runtime.run() instead."
1599
+ );
1600
+ }
1601
+ if (defaults.serverTools?.applyPatch?.mode && defaults.serverTools.applyPatch.mode !== "disabled") {
1602
+ const requestPatch = request.serverTools?.applyPatch;
1603
+ if (!requestPatch || requestPatch.mode !== "disabled") {
1604
+ throw new RuntimeConfigError(
1605
+ "STREAMING_CHAT_DOES_NOT_SUPPORT_APPLY_PATCH",
1606
+ "executeStreamingChat() does not support applyPatch defaults. Use runtime.run() instead."
1607
+ );
1608
+ }
1609
+ }
1610
+ }
1611
+ function startEvent(requestId, model) {
1612
+ return {
1613
+ type: "stream.start",
1614
+ requestId,
1615
+ data: {
1616
+ model,
1617
+ apiMode: "chat",
1618
+ entrypoint: "executeStreamingChat"
1619
+ }
1620
+ };
1621
+ }
1622
+ function textDeltaEvent(requestId, text) {
1623
+ return {
1624
+ type: "stream.text.delta",
1625
+ requestId,
1626
+ data: { text }
1627
+ };
1628
+ }
1629
+ function toolCallDeltaEvent(requestId, data) {
1630
+ return {
1631
+ type: "stream.tool_call.delta",
1632
+ requestId,
1633
+ data
1634
+ };
1635
+ }
1636
+ function usageEvent(requestId, usage) {
1637
+ return {
1638
+ type: "stream.usage",
1639
+ requestId,
1640
+ data: { usage }
1641
+ };
1642
+ }
1643
+ function warningEvent(requestId, warning) {
1644
+ return {
1645
+ type: "stream.warning",
1646
+ requestId,
1647
+ data: { warning }
1648
+ };
1649
+ }
1650
+ function errorEvent(requestId, error) {
1651
+ return {
1652
+ type: "stream.error",
1653
+ requestId,
1654
+ data: { error }
1655
+ };
1656
+ }
1657
+ function doneEvent(requestId, data) {
1658
+ return {
1659
+ type: "stream.done",
1660
+ requestId,
1661
+ data
1662
+ };
1663
+ }
1664
+ function toRuntimeError(error) {
1665
+ if (error instanceof RuntimeConfigError) {
1666
+ return {
1667
+ code: error.code,
1668
+ message: error.message,
1669
+ source: "validation",
1670
+ details: error.details
1671
+ };
1672
+ }
1673
+ if (error instanceof OpenRouterHttpError) {
1674
+ return {
1675
+ code: error.code,
1676
+ message: error.message,
1677
+ source: "openrouter",
1678
+ retryable: error.retryable,
1679
+ details: {
1680
+ providerAttempts: error.providerAttempts,
1681
+ ...error.body === void 0 ? {} : { response: error.body }
1682
+ }
1683
+ };
1684
+ }
1685
+ return {
1686
+ code: "RUNTIME_ERROR",
1687
+ message: error instanceof Error ? error.message : String(error),
1688
+ source: "runtime"
1689
+ };
1314
1690
  }
1315
1691
 
1316
1692
  // src/runtime/create-runtime.ts
@@ -1322,24 +1698,38 @@ function createOpenRouterRuntime(options) {
1322
1698
  async run(request) {
1323
1699
  const requestWithId = { ...request, id: request.id ?? createRuntimeId() };
1324
1700
  try {
1325
- resolvedOptions.logger?.info?.("runtime.request.started", { requestId: requestWithId.id });
1701
+ resolvedOptions.logger?.info?.("runtime.request.started", {
1702
+ requestId: requestWithId.id,
1703
+ streaming: false,
1704
+ entrypoint: "run"
1705
+ });
1326
1706
  await resolvedOptions.hooks?.beforeCompile?.(requestWithId);
1327
1707
  const compiled = compileRuntimeRequest(requestWithId, defaults, resolvedOptions, { streaming: false });
1328
1708
  await resolvedOptions.hooks?.afterCompile?.(compiled);
1329
1709
  resolvedOptions.logger?.debug?.("runtime.request.compiled", {
1330
1710
  requestId: requestWithId.id,
1331
- apiMode: compiled.apiMode
1711
+ apiMode: compiled.apiMode,
1712
+ streaming: false,
1713
+ bodyStream: compiled.body.stream,
1714
+ streamingWarnings: compiled.warnings.filter((warning) => warning.code.includes("STREAMING") || warning.code.includes("ADVISOR_STREAMING")).map((warning) => warning.code)
1332
1715
  });
1333
1716
  await resolvedOptions.hooks?.beforeOpenRouterRequest?.(compiled);
1334
1717
  const response = compiled.apiMode === "chat" ? await runChat(requestWithId, compiled, context) : await runResponses(requestWithId, compiled, context);
1335
1718
  await resolvedOptions.hooks?.afterOpenRouterResponse?.(response.raw.response);
1336
- resolvedOptions.logger?.info?.("runtime.response.normalized", { requestId: requestWithId.id, status: response.status });
1719
+ resolvedOptions.logger?.info?.("runtime.response.normalized", {
1720
+ requestId: requestWithId.id,
1721
+ status: response.status,
1722
+ streaming: false
1723
+ });
1337
1724
  return response;
1338
1725
  } catch (error) {
1339
1726
  return errorResponse(requestWithId, error);
1340
1727
  }
1341
1728
  },
1342
- stream: streamNotImplemented
1729
+ executeStreamingChat(request) {
1730
+ const requestWithId = { ...request, id: request.id ?? createRuntimeId() };
1731
+ return executeStreamingChat(requestWithId, context);
1732
+ }
1343
1733
  };
1344
1734
  function errorResponse(request, error) {
1345
1735
  const runtimeError = normalizeError(error);
@@ -1432,6 +1822,8 @@ function operatorHintFor(code) {
1432
1822
  RuntimeConfigError,
1433
1823
  buildOpenRouterServerTools,
1434
1824
  compileRuntimeRequest,
1435
- createOpenRouterRuntime
1825
+ createConsoleLogger,
1826
+ createOpenRouterRuntime,
1827
+ isRuntimeLoggingEnabled
1436
1828
  });
1437
1829
  //# sourceMappingURL=index.cjs.map