@copilotkit/aimock 1.37.4 → 1.38.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.
Files changed (153) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +22 -0
  4. package/README.md +27 -9
  5. package/dist/agui-recorder.cjs +6 -0
  6. package/dist/agui-recorder.cjs.map +1 -1
  7. package/dist/agui-recorder.d.cts.map +1 -1
  8. package/dist/agui-recorder.d.ts.map +1 -1
  9. package/dist/agui-recorder.js +6 -0
  10. package/dist/agui-recorder.js.map +1 -1
  11. package/dist/agui-types.d.ts.map +1 -1
  12. package/dist/api-key-auth.cjs +166 -0
  13. package/dist/api-key-auth.cjs.map +1 -0
  14. package/dist/api-key-auth.d.cts +17 -0
  15. package/dist/api-key-auth.d.cts.map +1 -0
  16. package/dist/api-key-auth.d.ts +17 -0
  17. package/dist/api-key-auth.d.ts.map +1 -0
  18. package/dist/api-key-auth.js +158 -0
  19. package/dist/api-key-auth.js.map +1 -0
  20. package/dist/cli.cjs +4 -1
  21. package/dist/cli.cjs.map +1 -1
  22. package/dist/cli.js +4 -1
  23. package/dist/cli.js.map +1 -1
  24. package/dist/config-loader.cjs +4 -2
  25. package/dist/config-loader.cjs.map +1 -1
  26. package/dist/config-loader.d.cts +2 -1
  27. package/dist/config-loader.d.cts.map +1 -1
  28. package/dist/config-loader.d.ts +2 -1
  29. package/dist/config-loader.d.ts.map +1 -1
  30. package/dist/config-loader.js +5 -3
  31. package/dist/config-loader.js.map +1 -1
  32. package/dist/fal-audio.cjs +2 -0
  33. package/dist/fal-audio.cjs.map +1 -1
  34. package/dist/fal-audio.d.cts.map +1 -1
  35. package/dist/fal-audio.d.ts.map +1 -1
  36. package/dist/fal-audio.js +2 -0
  37. package/dist/fal-audio.js.map +1 -1
  38. package/dist/fal.cjs +4 -1
  39. package/dist/fal.cjs.map +1 -1
  40. package/dist/fal.d.cts.map +1 -1
  41. package/dist/fal.d.ts.map +1 -1
  42. package/dist/fal.js +4 -1
  43. package/dist/fal.js.map +1 -1
  44. package/dist/fixture-loader.cjs +0 -0
  45. package/dist/fixture-loader.cjs.map +1 -1
  46. package/dist/fixture-loader.d.cts +3 -1
  47. package/dist/fixture-loader.d.cts.map +1 -1
  48. package/dist/fixture-loader.d.ts +3 -1
  49. package/dist/fixture-loader.d.ts.map +1 -1
  50. package/dist/fixture-loader.js +0 -0
  51. package/dist/fixture-loader.js.map +1 -1
  52. package/dist/grok-video.cjs +9 -2
  53. package/dist/grok-video.cjs.map +1 -1
  54. package/dist/grok-video.js +10 -3
  55. package/dist/grok-video.js.map +1 -1
  56. package/dist/helpers.cjs +16 -22
  57. package/dist/helpers.cjs.map +1 -1
  58. package/dist/helpers.d.cts.map +1 -1
  59. package/dist/helpers.d.ts.map +1 -1
  60. package/dist/helpers.js +16 -21
  61. package/dist/helpers.js.map +1 -1
  62. package/dist/index.d.cts +2 -2
  63. package/dist/index.d.ts +2 -2
  64. package/dist/llmock.cjs +14 -3
  65. package/dist/llmock.cjs.map +1 -1
  66. package/dist/llmock.d.cts +4 -2
  67. package/dist/llmock.d.cts.map +1 -1
  68. package/dist/llmock.d.ts +4 -2
  69. package/dist/llmock.d.ts.map +1 -1
  70. package/dist/llmock.js +15 -5
  71. package/dist/llmock.js.map +1 -1
  72. package/dist/openrouter-chat.cjs +333 -0
  73. package/dist/openrouter-chat.cjs.map +1 -0
  74. package/dist/openrouter-chat.js +325 -0
  75. package/dist/openrouter-chat.js.map +1 -0
  76. package/dist/openrouter-video.cjs +24 -7
  77. package/dist/openrouter-video.cjs.map +1 -1
  78. package/dist/openrouter-video.d.cts.map +1 -1
  79. package/dist/openrouter-video.d.ts.map +1 -1
  80. package/dist/openrouter-video.js +25 -8
  81. package/dist/openrouter-video.js.map +1 -1
  82. package/dist/provider-auth.cjs +20 -0
  83. package/dist/provider-auth.cjs.map +1 -1
  84. package/dist/provider-auth.js +20 -1
  85. package/dist/provider-auth.js.map +1 -1
  86. package/dist/recorder.cjs +50 -8
  87. package/dist/recorder.cjs.map +1 -1
  88. package/dist/recorder.d.cts.map +1 -1
  89. package/dist/recorder.d.ts.map +1 -1
  90. package/dist/recorder.js +50 -10
  91. package/dist/recorder.js.map +1 -1
  92. package/dist/responses.cjs +200 -89
  93. package/dist/responses.cjs.map +1 -1
  94. package/dist/responses.d.cts +18 -3
  95. package/dist/responses.d.cts.map +1 -1
  96. package/dist/responses.d.ts +18 -3
  97. package/dist/responses.d.ts.map +1 -1
  98. package/dist/responses.js +199 -90
  99. package/dist/responses.js.map +1 -1
  100. package/dist/server.cjs +156 -66
  101. package/dist/server.cjs.map +1 -1
  102. package/dist/server.d.cts +1 -1
  103. package/dist/server.d.cts.map +1 -1
  104. package/dist/server.d.ts +1 -1
  105. package/dist/server.d.ts.map +1 -1
  106. package/dist/server.js +157 -68
  107. package/dist/server.js.map +1 -1
  108. package/dist/sse-writer.cjs +3 -0
  109. package/dist/sse-writer.cjs.map +1 -1
  110. package/dist/sse-writer.d.cts +8 -0
  111. package/dist/sse-writer.d.cts.map +1 -1
  112. package/dist/sse-writer.d.ts +8 -0
  113. package/dist/sse-writer.d.ts.map +1 -1
  114. package/dist/sse-writer.js +3 -0
  115. package/dist/sse-writer.js.map +1 -1
  116. package/dist/stream-collapse.cjs +26 -0
  117. package/dist/stream-collapse.cjs.map +1 -1
  118. package/dist/stream-collapse.d.cts +7 -0
  119. package/dist/stream-collapse.d.cts.map +1 -1
  120. package/dist/stream-collapse.d.ts +7 -0
  121. package/dist/stream-collapse.d.ts.map +1 -1
  122. package/dist/stream-collapse.js +26 -0
  123. package/dist/stream-collapse.js.map +1 -1
  124. package/dist/transcription.cjs +49 -2
  125. package/dist/transcription.cjs.map +1 -1
  126. package/dist/transcription.d.cts.map +1 -1
  127. package/dist/transcription.d.ts.map +1 -1
  128. package/dist/transcription.js +50 -3
  129. package/dist/transcription.js.map +1 -1
  130. package/dist/types.d.cts +187 -5
  131. package/dist/types.d.cts.map +1 -1
  132. package/dist/types.d.ts +187 -5
  133. package/dist/types.d.ts.map +1 -1
  134. package/dist/veo-video.cjs +9 -2
  135. package/dist/veo-video.cjs.map +1 -1
  136. package/dist/veo-video.js +10 -3
  137. package/dist/veo-video.js.map +1 -1
  138. package/dist/ws-realtime.cjs +306 -57
  139. package/dist/ws-realtime.cjs.map +1 -1
  140. package/dist/ws-realtime.d.cts +1 -0
  141. package/dist/ws-realtime.d.cts.map +1 -1
  142. package/dist/ws-realtime.d.ts +1 -0
  143. package/dist/ws-realtime.d.ts.map +1 -1
  144. package/dist/ws-realtime.js +307 -58
  145. package/dist/ws-realtime.js.map +1 -1
  146. package/dist/ws-responses.cjs +11 -6
  147. package/dist/ws-responses.cjs.map +1 -1
  148. package/dist/ws-responses.d.cts.map +1 -1
  149. package/dist/ws-responses.d.ts.map +1 -1
  150. package/dist/ws-responses.js +12 -7
  151. package/dist/ws-responses.js.map +1 -1
  152. package/package.json +1 -1
  153. package/skills/write-fixtures/SKILL.md +53 -19
package/dist/server.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
+ const require_api_key_auth = require('./api-key-auth.cjs');
2
3
  const require_helpers = require('./helpers.cjs');
3
4
  const require_logger = require('./logger.cjs');
4
5
  const require_journal = require('./journal.cjs');
@@ -6,6 +7,7 @@ const require_router = require('./router.cjs');
6
7
  const require_fixture_loader = require('./fixture-loader.cjs');
7
8
  const require_sse_writer = require('./sse-writer.cjs');
8
9
  const require_interruption = require('./interruption.cjs');
10
+ const require_openrouter_chat = require('./openrouter-chat.cjs');
9
11
  const require_chaos = require('./chaos.cjs');
10
12
  const require_recorder = require('./recorder.cjs');
11
13
  const require_responses = require('./responses.cjs');
@@ -311,7 +313,7 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
311
313
  handleNotFound(res, `Unknown control endpoint: ${pathname}`);
312
314
  return true;
313
315
  }
314
- async function handleCompletions(req, res, fixtures, journal, defaults, modelFallback, providerKey) {
316
+ async function handleCompletions(req, res, fixtures, journal, defaults, modelFallback, providerKey, openRouter = false) {
315
317
  setCorsHeaders(res);
316
318
  let raw;
317
319
  try {
@@ -328,7 +330,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
328
330
  fixture: null
329
331
  }
330
332
  });
331
- require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
333
+ require_sse_writer.writeErrorResponse(res, 500, openRouter ? require_openrouter_chat.serializeOpenRouterError(500, `Request body read failed: ${msg}`) : JSON.stringify({ error: {
332
334
  message: `Request body read failed: ${msg}`,
333
335
  type: "server_error"
334
336
  } }));
@@ -350,7 +352,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
350
352
  fixture: null
351
353
  }
352
354
  });
353
- require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
355
+ require_sse_writer.writeErrorResponse(res, 400, openRouter ? require_openrouter_chat.serializeOpenRouterError(400, `Malformed JSON: ${detail}`) : JSON.stringify({ error: {
354
356
  message: `Malformed JSON: ${detail}`,
355
357
  type: "invalid_request_error",
356
358
  param: null,
@@ -369,7 +371,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
369
371
  fixture: null
370
372
  }
371
373
  });
372
- require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
374
+ require_sse_writer.writeErrorResponse(res, 400, openRouter ? require_openrouter_chat.serializeOpenRouterError(400, "Missing required parameter: 'messages'") : JSON.stringify({ error: {
373
375
  message: "Missing required parameter: 'messages'",
374
376
  type: "invalid_request_error",
375
377
  param: null,
@@ -383,9 +385,66 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
383
385
  body._endpointType = "chat";
384
386
  body._context = require_helpers.getContext(req);
385
387
  const testId = require_helpers.getTestId(req);
386
- const { fixture, skippedBySequenceOrTurn } = require_router.matchFixtureDiagnostic(fixtures, body, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, require_router.recordMatchOptions(!!(defaults.record && providerKey), defaults.logger));
388
+ const matchOptions = require_router.recordMatchOptions(!!(defaults.record && providerKey), defaults.logger);
389
+ let fixture = null;
390
+ let skippedBySequenceOrTurn = 0;
391
+ let preResolvedResponse = null;
392
+ let responseModel = body.model;
393
+ let fixtureCountIncremented = false;
394
+ if (openRouter && Array.isArray(body.models) && body.models.length > 0) {
395
+ const candidates = require_openrouter_chat.buildOpenRouterCandidates(body);
396
+ let matchCounts = journal.getFixtureMatchCountsForTest(testId);
397
+ const allowFallbacks = body.provider?.allow_fallbacks !== false;
398
+ let lastErrorFixture = null;
399
+ let lastErrorResponse = null;
400
+ const resolveCandidate = async (candidate) => {
401
+ const probe = {
402
+ ...body,
403
+ model: candidate
404
+ };
405
+ const attempt = require_router.matchFixtureDiagnostic(fixtures, probe, matchCounts, defaults.requestTransform, matchOptions);
406
+ skippedBySequenceOrTurn = Math.max(skippedBySequenceOrTurn, attempt.skippedBySequenceOrTurn);
407
+ if (!attempt.fixture) return null;
408
+ const response = await require_helpers.resolveResponse(attempt.fixture, probe);
409
+ return {
410
+ fixture: attempt.fixture,
411
+ response,
412
+ isError: require_helpers.isErrorResponse(response)
413
+ };
414
+ };
415
+ for (const candidate of candidates) {
416
+ const outcome = await resolveCandidate(candidate);
417
+ if (!outcome) {
418
+ if (!allowFallbacks) break;
419
+ continue;
420
+ }
421
+ journal.incrementFixtureMatchCount(outcome.fixture, fixtures, testId);
422
+ fixtureCountIncremented = true;
423
+ matchCounts = journal.getFixtureMatchCountsForTest(testId);
424
+ responseModel = candidate;
425
+ if (outcome.isError) {
426
+ const errorFallsThrough = outcome.response.fallthrough !== false;
427
+ if (allowFallbacks && errorFallsThrough) {
428
+ lastErrorFixture = outcome.fixture;
429
+ lastErrorResponse = outcome.response;
430
+ continue;
431
+ }
432
+ }
433
+ fixture = outcome.fixture;
434
+ preResolvedResponse = outcome.response;
435
+ break;
436
+ }
437
+ if (!fixture && lastErrorFixture) {
438
+ fixture = lastErrorFixture;
439
+ preResolvedResponse = lastErrorResponse;
440
+ }
441
+ } else {
442
+ const single = require_router.matchFixtureDiagnostic(fixtures, body, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, matchOptions);
443
+ fixture = single.fixture;
444
+ skippedBySequenceOrTurn = single.skippedBySequenceOrTurn;
445
+ }
387
446
  if (fixture) {
388
- journal.incrementFixtureMatchCount(fixture, fixtures, testId);
447
+ if (!fixtureCountIncremented) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
389
448
  defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
390
449
  } else {
391
450
  const lastUserMsg = body.messages.filter((m) => m.role === "user").pop();
@@ -423,7 +482,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
423
482
  ...require_helpers.strictOverrideField(defaults.strict, req.headers)
424
483
  }
425
484
  });
426
- require_sse_writer.writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
485
+ require_sse_writer.writeErrorResponse(res, strictStatus, openRouter ? require_openrouter_chat.serializeOpenRouterError(strictStatus, strictMessage) : JSON.stringify({ error: {
427
486
  message: strictMessage,
428
487
  type: "invalid_request_error",
429
488
  param: null,
@@ -474,7 +533,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
474
533
  ...require_helpers.strictOverrideField(defaults.strict, req.headers)
475
534
  }
476
535
  });
477
- require_sse_writer.writeErrorResponse(res, 404, JSON.stringify({ error: {
536
+ require_sse_writer.writeErrorResponse(res, 404, openRouter ? require_openrouter_chat.serializeOpenRouterError(404, "No fixture matched") : JSON.stringify({ error: {
478
537
  message: "No fixture matched",
479
538
  type: "invalid_request_error",
480
539
  param: null,
@@ -482,10 +541,23 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
482
541
  } }));
483
542
  return;
484
543
  }
485
- const response = await require_helpers.resolveResponse(fixture, body);
544
+ const response = preResolvedResponse ?? await require_helpers.resolveResponse(fixture, body);
486
545
  const latency = fixture.latency ?? defaults.latency;
487
546
  const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
488
- const includeUsage = body.stream === true && body.stream_options?.include_usage === true;
547
+ const emitStreamingUsage = body.stream === true && body.stream_options?.include_usage === true || openRouter;
548
+ const streamingPromptText = body.messages.map((m) => typeof m.content === "string" ? m.content : Array.isArray(m.content) ? m.content.map((p) => p.text ?? "").join("") : "").join("");
549
+ const resolveStreamingUsageTokens = (overrides, completionText) => require_helpers.resolveUsage(overrides, streamingPromptText, completionText);
550
+ const shapeORCompletion = (completion, overrides) => {
551
+ if (!openRouter) return completion;
552
+ return require_openrouter_chat.shapeOpenRouterCompletion(completion, require_openrouter_chat.resolveOpenRouterShaping(overrides, overrides?.model ?? responseModel), overrides?.id !== void 0);
553
+ };
554
+ const shapeORChunks = (chunks, usageChunk, overrides) => {
555
+ if (!openRouter) return;
556
+ const shaping = require_openrouter_chat.resolveOpenRouterShaping(overrides, overrides?.model ?? responseModel);
557
+ require_openrouter_chat.shapeOpenRouterChunks(chunks, shaping, overrides?.id !== void 0);
558
+ if (usageChunk) require_openrouter_chat.shapeOpenRouterChunks([usageChunk], shaping, overrides?.id !== void 0);
559
+ };
560
+ const openRouterProcessing = !!(openRouter && fixture.openRouterProcessing);
489
561
  if (require_helpers.isErrorResponse(response)) {
490
562
  const status = response.status ?? 500;
491
563
  journal.add({
@@ -498,7 +570,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
498
570
  fixture
499
571
  }
500
572
  });
501
- require_sse_writer.writeErrorResponse(res, status, require_helpers.serializeErrorResponse(response), { retryAfter: response.retryAfter });
573
+ require_sse_writer.writeErrorResponse(res, status, openRouter ? require_openrouter_chat.serializeOpenRouterError(status, response.error.message, response.error.metadata) : require_helpers.serializeErrorResponse(response), { retryAfter: response.retryAfter });
502
574
  return;
503
575
  }
504
576
  if (require_helpers.isAudioResponse(response)) {
@@ -512,7 +584,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
512
584
  fixture
513
585
  }
514
586
  });
515
- require_sse_writer.writeErrorResponse(res, 422, JSON.stringify({ error: {
587
+ require_sse_writer.writeErrorResponse(res, 422, openRouter ? require_openrouter_chat.serializeOpenRouterError(422, "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.") : JSON.stringify({ error: {
516
588
  message: "Audio responses are not supported on the chat completions endpoint. Use Gemini generateContent or a dedicated audio endpoint.",
517
589
  type: "invalid_request_error"
518
590
  } }));
@@ -522,7 +594,9 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
522
594
  if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
523
595
  const overrides = require_helpers.extractOverrides(response);
524
596
  const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
525
- const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, body.model, effectiveStrict, defaults.logger);
597
+ const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
598
+ const streaming = body.stream === true;
599
+ const streamingBlocks = streaming && response.blocks && response.blocks.length > 0 ? require_helpers.resolveFixtureBlocks(response.blocks) : void 0;
526
600
  const journalEntry = journal.add({
527
601
  method: req.method ?? "POST",
528
602
  path: req.url ?? COMPLETIONS_PATH,
@@ -533,22 +607,15 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
533
607
  fixture
534
608
  }
535
609
  });
536
- if (body.stream !== true) {
537
- const completion = require_helpers.buildContentWithToolCallsCompletion(response.content ?? "", response.toolCalls ?? [], body.model, effReasoning, overrides, body.messages);
610
+ if (!streaming) {
611
+ const completion = require_helpers.buildContentWithToolCallsCompletion(response.content ?? "", response.toolCalls ?? [], responseModel, effReasoning, overrides, body.messages);
538
612
  res.writeHead(200, { "Content-Type": "application/json" });
539
- res.end(JSON.stringify(completion));
613
+ res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
540
614
  } else {
541
- const chunks = require_helpers.buildContentWithToolCallsChunks(response.content ?? "", response.toolCalls ?? [], body.model, chunkSize, effReasoning, overrides, response.blocks);
542
- const completionText = (response.content ?? "") + (response.toolCalls ?? []).map((tc) => tc.name + tc.arguments).join("");
543
- const usageChunk = includeUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
544
- prompt_tokens: overrides.usage.prompt_tokens ?? 0,
545
- completion_tokens: overrides.usage.completion_tokens ?? 0,
546
- total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
547
- } : {
548
- prompt_tokens: require_helpers.estimatePromptTokens(body.messages),
549
- completion_tokens: require_helpers.estimateTokens(completionText),
550
- total_tokens: require_helpers.estimatePromptTokens(body.messages) + require_helpers.estimateTokens(completionText)
551
- }, overrides?.systemFingerprint) : void 0;
615
+ const chunks = require_helpers.buildContentWithToolCallsChunks(response.content ?? "", response.toolCalls ?? [], responseModel, chunkSize, effReasoning, overrides, streamingBlocks);
616
+ const completionText = streamingBlocks ? streamingBlocks.map((b) => b.type === "text" ? b.text : b.name + b.arguments).join("") : (response.content ?? "") + (response.toolCalls ?? []).map((tc) => tc.name + tc.arguments).join("");
617
+ const usageChunk = emitStreamingUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? responseModel, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), resolveStreamingUsageTokens(overrides, completionText), overrides?.systemFingerprint) : void 0;
618
+ shapeORChunks(chunks, usageChunk, overrides);
552
619
  const interruption = require_interruption.createInterruptionSignal(fixture);
553
620
  if (!await require_sse_writer.writeSSEStream(res, chunks, {
554
621
  latency,
@@ -557,7 +624,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
557
624
  onChunkSent: interruption?.tick,
558
625
  usageChunk,
559
626
  recordedTimings: fixture.recordedTimings,
560
- replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
627
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
628
+ openRouterProcessing
561
629
  })) {
562
630
  if (!res.writableEnded) res.destroy();
563
631
  journalEntry.response.interrupted = true;
@@ -571,7 +639,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
571
639
  if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
572
640
  const overrides = require_helpers.extractOverrides(response);
573
641
  const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
574
- const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, body.model, effectiveStrict, defaults.logger);
642
+ const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
575
643
  const journalEntry = journal.add({
576
644
  method: req.method ?? "POST",
577
645
  path: req.url ?? COMPLETIONS_PATH,
@@ -583,20 +651,13 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
583
651
  }
584
652
  });
585
653
  if (body.stream !== true) {
586
- const completion = require_helpers.buildTextCompletion(response.content, body.model, effReasoning, overrides, body.messages);
654
+ const completion = require_helpers.buildTextCompletion(response.content, responseModel, effReasoning, overrides, body.messages);
587
655
  res.writeHead(200, { "Content-Type": "application/json" });
588
- res.end(JSON.stringify(completion));
656
+ res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
589
657
  } else {
590
- const chunks = require_helpers.buildTextChunks(response.content, body.model, chunkSize, effReasoning, overrides);
591
- const usageChunk = includeUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
592
- prompt_tokens: overrides.usage.prompt_tokens ?? 0,
593
- completion_tokens: overrides.usage.completion_tokens ?? 0,
594
- total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
595
- } : {
596
- prompt_tokens: require_helpers.estimatePromptTokens(body.messages),
597
- completion_tokens: require_helpers.estimateTokens(response.content),
598
- total_tokens: require_helpers.estimatePromptTokens(body.messages) + require_helpers.estimateTokens(response.content)
599
- }, overrides?.systemFingerprint) : void 0;
658
+ const chunks = require_helpers.buildTextChunks(response.content, responseModel, chunkSize, effReasoning, overrides);
659
+ const usageChunk = emitStreamingUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? responseModel, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), resolveStreamingUsageTokens(overrides, response.content), overrides?.systemFingerprint) : void 0;
660
+ shapeORChunks(chunks, usageChunk, overrides);
600
661
  const interruption = require_interruption.createInterruptionSignal(fixture);
601
662
  if (!await require_sse_writer.writeSSEStream(res, chunks, {
602
663
  latency,
@@ -605,7 +666,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
605
666
  onChunkSent: interruption?.tick,
606
667
  usageChunk,
607
668
  recordedTimings: fixture.recordedTimings,
608
- replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
669
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
670
+ openRouterProcessing
609
671
  })) {
610
672
  if (!res.writableEnded) res.destroy();
611
673
  journalEntry.response.interrupted = true;
@@ -619,7 +681,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
619
681
  if (response.webSearches?.length) defaults.logger.warn("webSearches in fixture response are not supported for Chat Completions API — ignoring");
620
682
  const overrides = require_helpers.extractOverrides(response);
621
683
  const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
622
- const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, body.model, effectiveStrict, defaults.logger);
684
+ const effReasoning = require_helpers.resolveReasoningForModel(response.reasoning, responseModel, effectiveStrict, defaults.logger);
623
685
  const journalEntry = journal.add({
624
686
  method: req.method ?? "POST",
625
687
  path: req.url ?? COMPLETIONS_PATH,
@@ -631,21 +693,14 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
631
693
  }
632
694
  });
633
695
  if (body.stream !== true) {
634
- const completion = require_helpers.buildToolCallCompletion(response.toolCalls, body.model, effReasoning, overrides, body.messages);
696
+ const completion = require_helpers.buildToolCallCompletion(response.toolCalls, responseModel, effReasoning, overrides, body.messages);
635
697
  res.writeHead(200, { "Content-Type": "application/json" });
636
- res.end(JSON.stringify(completion));
698
+ res.end(JSON.stringify(shapeORCompletion(completion, overrides)));
637
699
  } else {
638
- const chunks = require_helpers.buildToolCallChunks(response.toolCalls, body.model, chunkSize, effReasoning, overrides);
700
+ const chunks = require_helpers.buildToolCallChunks(response.toolCalls, responseModel, chunkSize, effReasoning, overrides);
639
701
  const completionText = response.toolCalls.map((tc) => tc.name + tc.arguments).join("");
640
- const usageChunk = includeUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? body.model, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), overrides?.usage ? {
641
- prompt_tokens: overrides.usage.prompt_tokens ?? 0,
642
- completion_tokens: overrides.usage.completion_tokens ?? 0,
643
- total_tokens: overrides.usage.total_tokens ?? (overrides.usage.prompt_tokens ?? 0) + (overrides.usage.completion_tokens ?? 0)
644
- } : {
645
- prompt_tokens: require_helpers.estimatePromptTokens(body.messages),
646
- completion_tokens: require_helpers.estimateTokens(completionText),
647
- total_tokens: require_helpers.estimatePromptTokens(body.messages) + require_helpers.estimateTokens(completionText)
648
- }, overrides?.systemFingerprint) : void 0;
702
+ const usageChunk = emitStreamingUsage ? require_helpers.buildUsageChunk(chunks[0]?.id ?? "chatcmpl-unknown", overrides?.model ?? responseModel, chunks[0]?.created ?? Math.floor(Date.now() / 1e3), resolveStreamingUsageTokens(overrides, completionText), overrides?.systemFingerprint) : void 0;
703
+ shapeORChunks(chunks, usageChunk, overrides);
649
704
  const interruption = require_interruption.createInterruptionSignal(fixture);
650
705
  if (!await require_sse_writer.writeSSEStream(res, chunks, {
651
706
  latency,
@@ -654,7 +709,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
654
709
  onChunkSent: interruption?.tick,
655
710
  usageChunk,
656
711
  recordedTimings: fixture.recordedTimings,
657
- replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed
712
+ replaySpeed: fixture.replaySpeed ?? defaults.replaySpeed,
713
+ openRouterProcessing
658
714
  })) {
659
715
  if (!res.writableEnded) res.destroy();
660
716
  journalEntry.response.interrupted = true;
@@ -674,12 +730,19 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
674
730
  fixture
675
731
  }
676
732
  });
677
- require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
733
+ require_sse_writer.writeErrorResponse(res, 500, openRouter ? require_openrouter_chat.serializeOpenRouterError(500, "Fixture response did not match any known type") : JSON.stringify({ error: {
678
734
  message: "Fixture response did not match any known type",
679
735
  type: "server_error"
680
736
  } }));
681
737
  }
682
738
  async function createServer(fixtures, options, mounts, serviceFixtures) {
739
+ return createServerWithResolvedAuth(fixtures, options, require_api_key_auth.resolveInboundAuth({
740
+ value: options?.auth,
741
+ label: "options.auth"
742
+ }), mounts, serviceFixtures);
743
+ }
744
+ /** @internal Config startup uses this to avoid parsing a selected auth source twice. */
745
+ async function createServerWithResolvedAuth(fixtures, options, resolvedAuth, mounts, serviceFixtures) {
683
746
  const host = options?.host ?? "127.0.0.1";
684
747
  const port = options?.port ?? 0;
685
748
  const logger = new require_logger.Logger(options?.logLevel ?? "silent");
@@ -793,13 +856,10 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
793
856
  });
794
857
  });
795
858
  async function handleHttpRequest(req, res) {
796
- if (req.method === "OPTIONS") {
797
- handleOptions(res);
798
- return;
799
- }
800
859
  const startTime = registry ? process.hrtime.bigint() : 0n;
801
860
  const parsedUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
802
861
  let pathname = parsedUrl.pathname;
862
+ const isOpenRouter = require_openrouter_chat.isOpenRouterPath(pathname);
803
863
  if (registry) res.on("finish", () => {
804
864
  try {
805
865
  const normalizedPath = require_metrics.normalizePathLabel(pathname);
@@ -819,6 +879,17 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
819
879
  defaults.logger.warn("metrics instrumentation error", err);
820
880
  }
821
881
  });
882
+ if (req.method === "OPTIONS" && (!resolvedAuth.policy.enabled || req.headers.origin !== void 0 && req.headers["access-control-request-method"] !== void 0)) {
883
+ handleOptions(res);
884
+ return;
885
+ }
886
+ if (!(req.method === "GET" && (pathname === HEALTH_PATH || pathname === READY_PATH || pathname === "/metrics"))) {
887
+ if (!require_api_key_auth.validateRequestApiKey(req, resolvedAuth.policy).ok) {
888
+ require_api_key_auth.writeApiKeyHttpRejection(res, setCorsHeaders);
889
+ return;
890
+ }
891
+ require_api_key_auth.markAuthenticatedRequest(req, resolvedAuth.policy);
892
+ }
822
893
  if (pathname.startsWith(CONTROL_PREFIX)) {
823
894
  await handleControlAPI(req, res, pathname, fixtures, journal, videoStates, openRouterVideoJobs, veoVideoJobs, grokVideoJobs, defaults);
824
895
  return;
@@ -1003,6 +1074,18 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1003
1074
  }
1004
1075
  return;
1005
1076
  }
1077
+ if (pathname === "/api/v1/models" && req.method === "GET") {
1078
+ require_openrouter_chat.handleOpenRouterModels(req, res, fixtures, journal, defaults, setCorsHeaders);
1079
+ return;
1080
+ }
1081
+ if (pathname === "/api/v1/key" && req.method === "GET") {
1082
+ require_openrouter_chat.handleOpenRouterKey(req, res, journal, setCorsHeaders);
1083
+ return;
1084
+ }
1085
+ if (pathname === "/api/v1/credits" && req.method === "GET") {
1086
+ require_openrouter_chat.handleOpenRouterCredits(req, res, journal, setCorsHeaders);
1087
+ return;
1088
+ }
1006
1089
  let azureDeploymentId;
1007
1090
  const azureMatch = pathname.match(AZURE_DEPLOYMENT_RE);
1008
1091
  if (azureMatch && req.method === "POST") {
@@ -1728,17 +1811,17 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1728
1811
  handleNotFound(res, "Not found");
1729
1812
  return;
1730
1813
  }
1731
- const completionsProvider = azureDeploymentId ? "azure" : "openai";
1814
+ const completionsProvider = azureDeploymentId ? "azure" : isOpenRouter ? "openrouter" : "openai";
1732
1815
  try {
1733
- await handleCompletions(req, res, fixtures, journal, defaults, azureDeploymentId, completionsProvider);
1816
+ await handleCompletions(req, res, fixtures, journal, defaults, azureDeploymentId, completionsProvider, isOpenRouter);
1734
1817
  } catch (err) {
1735
1818
  const msg = err instanceof Error ? err.message : "Internal error";
1736
- if (!res.headersSent) require_sse_writer.writeErrorResponse(res, 500, JSON.stringify({ error: {
1819
+ if (!res.headersSent) require_sse_writer.writeErrorResponse(res, 500, isOpenRouter ? require_openrouter_chat.serializeOpenRouterError(500, msg) : JSON.stringify({ error: {
1737
1820
  message: msg,
1738
1821
  type: "server_error"
1739
1822
  } }));
1740
1823
  else if (!res.writableEnded) try {
1741
- res.write(`data: ${JSON.stringify({ error: {
1824
+ res.write(`data: ${isOpenRouter ? require_openrouter_chat.serializeOpenRouterError(500, msg) : JSON.stringify({ error: {
1742
1825
  message: msg,
1743
1826
  type: "server_error"
1744
1827
  } })}\n\n`);
@@ -1759,6 +1842,10 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1759
1842
  async function handleUpgradeRequest(req, socket, head) {
1760
1843
  const parsedUrl = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
1761
1844
  let pathname = parsedUrl.pathname;
1845
+ if (!require_api_key_auth.validateRequestApiKey(req, resolvedAuth.policy).ok) {
1846
+ require_api_key_auth.writeApiKeyUpgradeRejection(socket);
1847
+ return;
1848
+ }
1762
1849
  if (mounts) {
1763
1850
  for (const { path: mountPath, handler } of mounts) if ((pathname === mountPath || pathname.startsWith(mountPath + "/")) && handler.handleUpgrade) {
1764
1851
  const subPath = pathname.slice(mountPath.length) || "/";
@@ -1797,10 +1884,12 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1797
1884
  upgradeHeaders: req.headers
1798
1885
  });
1799
1886
  else if (pathname === REALTIME_PATH) {
1800
- const model = parsedUrl.searchParams.get("model") ?? "gpt-realtime-2";
1887
+ const transcriptionIntent = parsedUrl.searchParams.get("intent") === "transcription";
1888
+ const model = transcriptionIntent ? "gpt-transcribe" : parsedUrl.searchParams.get("model") ?? "gpt-realtime-2";
1801
1889
  require_ws_realtime.handleWebSocketRealtime(ws, fixtures, journal, {
1802
1890
  ...defaults,
1803
1891
  model,
1892
+ transcriptionIntent,
1804
1893
  testId: wsTestId,
1805
1894
  upgradeHeaders: req.headers
1806
1895
  });
@@ -1850,4 +1939,5 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1850
1939
 
1851
1940
  //#endregion
1852
1941
  exports.createServer = createServer;
1942
+ exports.createServerWithResolvedAuth = createServerWithResolvedAuth;
1853
1943
  //# sourceMappingURL=server.cjs.map