@copilotkit/aimock 1.41.0 → 1.42.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 (215) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +12 -1
  4. package/README.md +2 -1
  5. package/dist/a2a-mock.cjs +18 -11
  6. package/dist/a2a-mock.cjs.map +1 -1
  7. package/dist/a2a-mock.d.cts +1 -0
  8. package/dist/a2a-mock.d.cts.map +1 -1
  9. package/dist/a2a-mock.d.ts +1 -0
  10. package/dist/a2a-mock.d.ts.map +1 -1
  11. package/dist/a2a-mock.js +18 -11
  12. package/dist/a2a-mock.js.map +1 -1
  13. package/dist/agui-handler.d.ts.map +1 -1
  14. package/dist/agui-mock.cjs +2 -0
  15. package/dist/agui-mock.cjs.map +1 -1
  16. package/dist/agui-mock.d.cts.map +1 -1
  17. package/dist/agui-mock.d.ts.map +1 -1
  18. package/dist/agui-mock.js +2 -0
  19. package/dist/agui-mock.js.map +1 -1
  20. package/dist/bedrock-converse.cjs +36 -2
  21. package/dist/bedrock-converse.cjs.map +1 -1
  22. package/dist/bedrock-converse.d.cts.map +1 -1
  23. package/dist/bedrock-converse.d.ts.map +1 -1
  24. package/dist/bedrock-converse.js +37 -3
  25. package/dist/bedrock-converse.js.map +1 -1
  26. package/dist/bedrock.cjs +50 -13
  27. package/dist/bedrock.cjs.map +1 -1
  28. package/dist/bedrock.d.cts.map +1 -1
  29. package/dist/bedrock.d.ts.map +1 -1
  30. package/dist/bedrock.js +51 -14
  31. package/dist/bedrock.js.map +1 -1
  32. package/dist/byteplus-video.cjs +2 -2
  33. package/dist/byteplus-video.cjs.map +1 -1
  34. package/dist/byteplus-video.d.cts.map +1 -1
  35. package/dist/byteplus-video.d.ts.map +1 -1
  36. package/dist/byteplus-video.js +2 -2
  37. package/dist/byteplus-video.js.map +1 -1
  38. package/dist/chaos.cjs +36 -7
  39. package/dist/chaos.cjs.map +1 -1
  40. package/dist/chaos.d.cts +8 -3
  41. package/dist/chaos.d.cts.map +1 -1
  42. package/dist/chaos.d.ts +8 -3
  43. package/dist/chaos.d.ts.map +1 -1
  44. package/dist/chaos.js +36 -8
  45. package/dist/chaos.js.map +1 -1
  46. package/dist/cohere.cjs +58 -5
  47. package/dist/cohere.cjs.map +1 -1
  48. package/dist/cohere.d.cts.map +1 -1
  49. package/dist/cohere.d.ts.map +1 -1
  50. package/dist/cohere.js +59 -6
  51. package/dist/cohere.js.map +1 -1
  52. package/dist/config-loader.d.ts.map +1 -1
  53. package/dist/elevenlabs-audio.cjs +36 -2
  54. package/dist/elevenlabs-audio.cjs.map +1 -1
  55. package/dist/elevenlabs-audio.d.cts.map +1 -1
  56. package/dist/elevenlabs-audio.d.ts.map +1 -1
  57. package/dist/elevenlabs-audio.js +37 -3
  58. package/dist/elevenlabs-audio.js.map +1 -1
  59. package/dist/embeddings.cjs +58 -3
  60. package/dist/embeddings.cjs.map +1 -1
  61. package/dist/embeddings.d.cts.map +1 -1
  62. package/dist/embeddings.d.ts.map +1 -1
  63. package/dist/embeddings.js +59 -4
  64. package/dist/embeddings.js.map +1 -1
  65. package/dist/fal-audio.cjs +38 -2
  66. package/dist/fal-audio.cjs.map +1 -1
  67. package/dist/fal-audio.d.cts.map +1 -1
  68. package/dist/fal-audio.d.ts.map +1 -1
  69. package/dist/fal-audio.js +39 -3
  70. package/dist/fal-audio.js.map +1 -1
  71. package/dist/fixture-loader.cjs +0 -0
  72. package/dist/fixture-loader.cjs.map +1 -1
  73. package/dist/fixture-loader.d.cts +9 -1
  74. package/dist/fixture-loader.d.cts.map +1 -1
  75. package/dist/fixture-loader.d.ts +9 -1
  76. package/dist/fixture-loader.d.ts.map +1 -1
  77. package/dist/fixture-loader.js +0 -0
  78. package/dist/fixture-loader.js.map +1 -1
  79. package/dist/gemini-embeddings.cjs +19 -1
  80. package/dist/gemini-embeddings.cjs.map +1 -1
  81. package/dist/gemini-embeddings.js +20 -2
  82. package/dist/gemini-embeddings.js.map +1 -1
  83. package/dist/gemini-interactions.cjs +15 -1
  84. package/dist/gemini-interactions.cjs.map +1 -1
  85. package/dist/gemini-interactions.d.cts.map +1 -1
  86. package/dist/gemini-interactions.d.ts.map +1 -1
  87. package/dist/gemini-interactions.js +16 -2
  88. package/dist/gemini-interactions.js.map +1 -1
  89. package/dist/gemini.cjs +70 -6
  90. package/dist/gemini.cjs.map +1 -1
  91. package/dist/gemini.d.cts.map +1 -1
  92. package/dist/gemini.d.ts.map +1 -1
  93. package/dist/gemini.js +71 -7
  94. package/dist/gemini.js.map +1 -1
  95. package/dist/grok-video.cjs +2 -2
  96. package/dist/grok-video.cjs.map +1 -1
  97. package/dist/grok-video.d.cts.map +1 -1
  98. package/dist/grok-video.d.ts.map +1 -1
  99. package/dist/grok-video.js +2 -2
  100. package/dist/grok-video.js.map +1 -1
  101. package/dist/helpers.cjs +160 -5
  102. package/dist/helpers.cjs.map +1 -1
  103. package/dist/helpers.d.cts.map +1 -1
  104. package/dist/helpers.d.ts.map +1 -1
  105. package/dist/helpers.js +152 -6
  106. package/dist/helpers.js.map +1 -1
  107. package/dist/images.cjs +22 -4
  108. package/dist/images.cjs.map +1 -1
  109. package/dist/images.d.cts.map +1 -1
  110. package/dist/images.d.ts.map +1 -1
  111. package/dist/images.js +23 -5
  112. package/dist/images.js.map +1 -1
  113. package/dist/llmock.cjs +15 -0
  114. package/dist/llmock.cjs.map +1 -1
  115. package/dist/llmock.d.cts +12 -0
  116. package/dist/llmock.d.cts.map +1 -1
  117. package/dist/llmock.d.ts +12 -0
  118. package/dist/llmock.d.ts.map +1 -1
  119. package/dist/llmock.js +16 -1
  120. package/dist/llmock.js.map +1 -1
  121. package/dist/mcp-mock.cjs +11 -0
  122. package/dist/mcp-mock.cjs.map +1 -1
  123. package/dist/mcp-mock.d.cts.map +1 -1
  124. package/dist/mcp-mock.d.ts.map +1 -1
  125. package/dist/mcp-mock.js +11 -0
  126. package/dist/mcp-mock.js.map +1 -1
  127. package/dist/messages.cjs +18 -1
  128. package/dist/messages.cjs.map +1 -1
  129. package/dist/messages.d.cts.map +1 -1
  130. package/dist/messages.d.ts.map +1 -1
  131. package/dist/messages.js +19 -2
  132. package/dist/messages.js.map +1 -1
  133. package/dist/moderation.cjs +67 -3
  134. package/dist/moderation.cjs.map +1 -1
  135. package/dist/moderation.d.cts +1 -0
  136. package/dist/moderation.d.cts.map +1 -1
  137. package/dist/moderation.d.ts +1 -0
  138. package/dist/moderation.d.ts.map +1 -1
  139. package/dist/moderation.js +68 -4
  140. package/dist/moderation.js.map +1 -1
  141. package/dist/ollama.cjs +75 -5
  142. package/dist/ollama.cjs.map +1 -1
  143. package/dist/ollama.d.cts.map +1 -1
  144. package/dist/ollama.d.ts.map +1 -1
  145. package/dist/ollama.js +76 -6
  146. package/dist/ollama.js.map +1 -1
  147. package/dist/openrouter-video.cjs +4 -4
  148. package/dist/openrouter-video.cjs.map +1 -1
  149. package/dist/openrouter-video.d.cts.map +1 -1
  150. package/dist/openrouter-video.d.ts.map +1 -1
  151. package/dist/openrouter-video.js +4 -4
  152. package/dist/openrouter-video.js.map +1 -1
  153. package/dist/rerank.cjs +65 -2
  154. package/dist/rerank.cjs.map +1 -1
  155. package/dist/rerank.d.cts +1 -1
  156. package/dist/rerank.d.cts.map +1 -1
  157. package/dist/rerank.d.ts +1 -1
  158. package/dist/rerank.d.ts.map +1 -1
  159. package/dist/rerank.js +66 -3
  160. package/dist/rerank.js.map +1 -1
  161. package/dist/responses.cjs +18 -1
  162. package/dist/responses.cjs.map +1 -1
  163. package/dist/responses.d.cts.map +1 -1
  164. package/dist/responses.d.ts.map +1 -1
  165. package/dist/responses.js +19 -2
  166. package/dist/responses.js.map +1 -1
  167. package/dist/search.cjs +67 -3
  168. package/dist/search.cjs.map +1 -1
  169. package/dist/search.d.cts +1 -0
  170. package/dist/search.d.cts.map +1 -1
  171. package/dist/search.d.ts +1 -0
  172. package/dist/search.d.ts.map +1 -1
  173. package/dist/search.js +68 -4
  174. package/dist/search.js.map +1 -1
  175. package/dist/server.cjs +317 -13
  176. package/dist/server.cjs.map +1 -1
  177. package/dist/server.d.cts.map +1 -1
  178. package/dist/server.d.ts.map +1 -1
  179. package/dist/server.js +320 -16
  180. package/dist/server.js.map +1 -1
  181. package/dist/speech.cjs +18 -1
  182. package/dist/speech.cjs.map +1 -1
  183. package/dist/speech.d.cts.map +1 -1
  184. package/dist/speech.d.ts.map +1 -1
  185. package/dist/speech.js +19 -2
  186. package/dist/speech.js.map +1 -1
  187. package/dist/transcription.cjs +21 -2
  188. package/dist/transcription.cjs.map +1 -1
  189. package/dist/transcription.d.cts.map +1 -1
  190. package/dist/transcription.d.ts.map +1 -1
  191. package/dist/transcription.js +21 -2
  192. package/dist/transcription.js.map +1 -1
  193. package/dist/types.d.cts +39 -2
  194. package/dist/types.d.cts.map +1 -1
  195. package/dist/types.d.ts +39 -2
  196. package/dist/types.d.ts.map +1 -1
  197. package/dist/vector-mock.cjs +88 -0
  198. package/dist/vector-mock.cjs.map +1 -1
  199. package/dist/vector-mock.d.cts.map +1 -1
  200. package/dist/vector-mock.d.ts.map +1 -1
  201. package/dist/vector-mock.js +89 -1
  202. package/dist/vector-mock.js.map +1 -1
  203. package/dist/veo-video.cjs +2 -2
  204. package/dist/veo-video.cjs.map +1 -1
  205. package/dist/veo-video.d.cts.map +1 -1
  206. package/dist/veo-video.d.ts.map +1 -1
  207. package/dist/veo-video.js +2 -2
  208. package/dist/veo-video.js.map +1 -1
  209. package/dist/video.cjs +19 -2
  210. package/dist/video.cjs.map +1 -1
  211. package/dist/video.d.cts.map +1 -1
  212. package/dist/video.d.ts.map +1 -1
  213. package/dist/video.js +20 -3
  214. package/dist/video.js.map +1 -1
  215. package/package.json +1 -1
package/dist/rerank.cjs CHANGED
@@ -28,7 +28,46 @@ async function handleRerank(req, res, raw, fixtures, journal, defaults, setCorsH
28
28
  } }));
29
29
  return;
30
30
  }
31
- const query = body.query ?? "";
31
+ if (!require_helpers.isJsonObject(body)) {
32
+ journal.add({
33
+ method: req.method ?? "POST",
34
+ path: req.url ?? "/v2/rerank",
35
+ headers: require_helpers.flattenHeaders(req.headers),
36
+ body: null,
37
+ service: "rerank",
38
+ response: {
39
+ status: 400,
40
+ fixture: null
41
+ }
42
+ });
43
+ res.writeHead(400, { "Content-Type": "application/json" });
44
+ res.end(JSON.stringify({ error: {
45
+ message: "Request body must be a JSON object",
46
+ type: "invalid_request_error"
47
+ } }));
48
+ return;
49
+ }
50
+ const normalizedQuery = require_helpers.normalizeTextInput(body.query ?? "");
51
+ if (normalizedQuery === null) {
52
+ journal.add({
53
+ method: req.method ?? "POST",
54
+ path: req.url ?? "/v2/rerank",
55
+ headers: require_helpers.flattenHeaders(req.headers),
56
+ body: null,
57
+ service: "rerank",
58
+ response: {
59
+ status: 400,
60
+ fixture: null
61
+ }
62
+ });
63
+ res.writeHead(400, { "Content-Type": "application/json" });
64
+ res.end(JSON.stringify({ error: {
65
+ message: "Invalid parameter: 'query' must be a string or an array of strings",
66
+ type: "invalid_request_error"
67
+ } }));
68
+ return;
69
+ }
70
+ const query = normalizedQuery;
32
71
  let matchedResults = [];
33
72
  let matchedFixture = null;
34
73
  for (const fixture of fixtures) if (require_helpers.matchesPattern(query, fixture.match)) {
@@ -38,6 +77,29 @@ async function handleRerank(req, res, raw, fixtures, journal, defaults, setCorsH
38
77
  }
39
78
  if (matchedFixture) logger.debug(`Rerank fixture matched for query "${query.slice(0, 80)}"`);
40
79
  else logger.debug(`No rerank fixture matched for query "${query.slice(0, 80)}" — returning empty`);
80
+ if (!matchedFixture && require_helpers.resolveStrictMode(defaults.strict, req.headers)) {
81
+ const strictMessage = require_helpers.strictNoMatchMessage(0);
82
+ logger.error(require_helpers.strictNoMatchLogLine(req.method ?? "POST", req.url ?? "/v2/rerank", 0));
83
+ journal.add({
84
+ method: req.method ?? "POST",
85
+ path: req.url ?? "/v2/rerank",
86
+ headers: require_helpers.flattenHeaders(req.headers),
87
+ body: null,
88
+ service: "rerank",
89
+ response: {
90
+ status: 503,
91
+ fixture: null,
92
+ ...require_helpers.strictOverrideField(defaults.strict, req.headers)
93
+ }
94
+ });
95
+ res.writeHead(503, { "Content-Type": "application/json" });
96
+ res.end(JSON.stringify({ error: {
97
+ message: strictMessage,
98
+ type: "invalid_request_error",
99
+ code: "no_fixture_match"
100
+ } }));
101
+ return;
102
+ }
41
103
  const results = matchedResults.map((r) => ({
42
104
  index: r.index,
43
105
  relevance_score: r.relevance_score
@@ -50,7 +112,8 @@ async function handleRerank(req, res, raw, fixtures, journal, defaults, setCorsH
50
112
  service: "rerank",
51
113
  response: {
52
114
  status: 200,
53
- fixture: null
115
+ fixture: null,
116
+ ...require_helpers.strictOverrideField(defaults.strict, req.headers)
54
117
  }
55
118
  });
56
119
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -1 +1 @@
1
- {"version":3,"file":"rerank.cjs","names":["flattenHeaders","matchesPattern","generateId"],"sources":["../src/rerank.ts"],"sourcesContent":["/**\n * Reranking API support for LLMock.\n *\n * Handles POST /v2/rerank requests (Cohere rerank-compatible). Matches\n * fixtures by comparing the request `query` field against registered\n * patterns. First match wins; no match returns empty results.\n */\n\nimport type * as http from \"node:http\";\nimport { flattenHeaders, generateId, matchesPattern } from \"./helpers.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ─── Rerank types ─────────────────────────────────────────────────────────\n\nexport interface RerankResult {\n index: number;\n relevance_score: number;\n}\n\nexport interface RerankFixture {\n match: string | RegExp;\n results: RerankResult[];\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nexport async function handleRerank(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: RerankFixture[],\n journal: Journal,\n defaults: { logger: Logger },\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let body: { query?: string; model?: string };\n try {\n body = JSON.parse(raw) as { query?: string; model?: string };\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n code: \"invalid_json\",\n },\n }),\n );\n return;\n }\n\n const query = body.query ?? \"\";\n\n // Find first matching fixture\n let matchedResults: RerankResult[] = [];\n let matchedFixture: RerankFixture | null = null;\n\n for (const fixture of fixtures) {\n if (matchesPattern(query, fixture.match)) {\n matchedFixture = fixture;\n matchedResults = fixture.results;\n break;\n }\n }\n\n if (matchedFixture) {\n logger.debug(`Rerank fixture matched for query \"${query.slice(0, 80)}\"`);\n } else {\n logger.debug(`No rerank fixture matched for query \"${query.slice(0, 80)}\" — returning empty`);\n }\n\n // Build response (Cohere rerank v2 format: index + relevance_score only)\n const results = matchedResults.map((r) => ({\n index: r.index,\n relevance_score: r.relevance_score,\n }));\n\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: { status: 200, fixture: null },\n });\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n id: generateId(\"rerank\"),\n results,\n meta: {\n billed_units: { search_units: 0 },\n },\n }),\n );\n}\n"],"mappings":";;;AA2BA,eAAsB,aACpB,KACA,KACA,KACA,UACA,SACA,UACA,gBACe;CACf,MAAM,EAAE,WAAW;AACnB,gBAAe,IAAI;CAEnB,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;UACf,UAAU;EACjB,MAAM,SAAS,oBAAoB,QAAQ,SAAS,UAAU;AAC9D,UAAQ,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,OAAO;GACjB,SAASA,+BAAe,IAAI,QAAQ;GACpC,MAAM;GACN,SAAS;GACT,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,MAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,MAAI,IACF,KAAK,UAAU,EACb,OAAO;GACL,SAAS,mBAAmB;GAC5B,MAAM;GACN,MAAM;GACP,EACF,CAAC,CACH;AACD;;CAGF,MAAM,QAAQ,KAAK,SAAS;CAG5B,IAAI,iBAAiC,EAAE;CACvC,IAAI,iBAAuC;AAE3C,MAAK,MAAM,WAAW,SACpB,KAAIC,+BAAe,OAAO,QAAQ,MAAM,EAAE;AACxC,mBAAiB;AACjB,mBAAiB,QAAQ;AACzB;;AAIJ,KAAI,eACF,QAAO,MAAM,qCAAqC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG;KAExE,QAAO,MAAM,wCAAwC,MAAM,MAAM,GAAG,GAAG,CAAC,qBAAqB;CAI/F,MAAM,UAAU,eAAe,KAAK,OAAO;EACzC,OAAO,EAAE;EACT,iBAAiB,EAAE;EACpB,EAAE;AAEH,SAAQ,IAAI;EACV,QAAQ,IAAI,UAAU;EACtB,MAAM,IAAI,OAAO;EACjB,SAASD,+BAAe,IAAI,QAAQ;EACpC,MAAM;EACN,SAAS;EACT,UAAU;GAAE,QAAQ;GAAK,SAAS;GAAM;EACzC,CAAC;AAEF,KAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,KAAI,IACF,KAAK,UAAU;EACb,IAAIE,2BAAW,SAAS;EACxB;EACA,MAAM,EACJ,cAAc,EAAE,cAAc,GAAG,EAClC;EACF,CAAC,CACH"}
1
+ {"version":3,"file":"rerank.cjs","names":["flattenHeaders","isJsonObject","normalizeTextInput","matchesPattern","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","strictOverrideField","generateId"],"sources":["../src/rerank.ts"],"sourcesContent":["/**\n * Reranking API support for LLMock.\n *\n * Handles POST /v2/rerank requests (Cohere rerank-compatible). Matches\n * fixtures by comparing the request `query` field against registered\n * patterns. First match wins; no match returns empty results.\n */\n\nimport type * as http from \"node:http\";\nimport {\n flattenHeaders,\n generateId,\n isJsonObject,\n matchesPattern,\n normalizeTextInput,\n resolveStrictMode,\n strictNoMatchLogLine,\n strictNoMatchMessage,\n strictOverrideField,\n} from \"./helpers.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ─── Rerank types ─────────────────────────────────────────────────────────\n\nexport interface RerankResult {\n index: number;\n relevance_score: number;\n}\n\nexport interface RerankFixture {\n match: string | RegExp;\n results: RerankResult[];\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nexport async function handleRerank(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: RerankFixture[],\n journal: Journal,\n defaults: { logger: Logger; strict?: boolean },\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let body: { query?: unknown; model?: string };\n try {\n body = JSON.parse(raw) as { query?: unknown; model?: string };\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n code: \"invalid_json\",\n },\n }),\n );\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object (e.g. `null`) before\n // touching fields — otherwise `body.query` throws a TypeError that surfaces\n // as a 500 instead of a 400.\n if (!isJsonObject(body)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Request body must be a JSON object\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const rawQuery: unknown = body.query ?? \"\";\n const normalizedQuery = normalizeTextInput(rawQuery);\n if (normalizedQuery === null) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Invalid parameter: 'query' must be a string or an array of strings\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n const query = normalizedQuery;\n\n // Find first matching fixture\n let matchedResults: RerankResult[] = [];\n let matchedFixture: RerankFixture | null = null;\n\n for (const fixture of fixtures) {\n if (matchesPattern(query, fixture.match)) {\n matchedFixture = fixture;\n matchedResults = fixture.results;\n break;\n }\n }\n\n if (matchedFixture) {\n logger.debug(`Rerank fixture matched for query \"${query.slice(0, 80)}\"`);\n } else {\n logger.debug(`No rerank fixture matched for query \"${query.slice(0, 80)}\" — returning empty`);\n }\n\n // Strict mode turns the lenient default (empty results) into a no-match\n // error, like every other fixture-matching handler. These fixtures carry no\n // sequence/turn state, so the skipped count is always 0.\n if (!matchedFixture && resolveStrictMode(defaults.strict, req.headers)) {\n const strictMessage = strictNoMatchMessage(0);\n logger.error(strictNoMatchLogLine(req.method ?? \"POST\", req.url ?? \"/v2/rerank\", 0));\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n res.writeHead(503, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n code: \"no_fixture_match\",\n },\n }),\n );\n return;\n }\n\n // Build response (Cohere rerank v2 format: index + relevance_score only)\n const results = matchedResults.map((r) => ({\n index: r.index,\n relevance_score: r.relevance_score,\n }));\n\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: {\n status: 200,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n id: generateId(\"rerank\"),\n results,\n meta: {\n billed_units: { search_units: 0 },\n },\n }),\n );\n}\n"],"mappings":"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"}
package/dist/rerank.d.cts CHANGED
@@ -14,9 +14,9 @@ interface RerankFixture {
14
14
  }
15
15
  declare function handleRerank(req: http$1.IncomingMessage, res: http$1.ServerResponse, raw: string, fixtures: RerankFixture[], journal: Journal, defaults: {
16
16
  logger: Logger;
17
+ strict?: boolean;
17
18
  }, setCorsHeaders: (res: http$1.ServerResponse) => void): Promise<void>;
18
19
  //# sourceMappingURL=rerank.d.ts.map
19
-
20
20
  //#endregion
21
21
  export { RerankFixture, RerankResult, handleRerank };
22
22
  //# sourceMappingURL=rerank.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rerank.d.cts","names":[],"sources":["../src/rerank.ts"],"sourcesContent":[],"mappings":";;;;;;AAsBuB,UAPN,YAAA,CAOM;EAKD,KAAA,EAAA,MAAA;EAAY,eAAA,EAAA,MAAA;;AAE3B,UATU,aAAA,CASL;OAEA,EAAA,MAAA,GAVM,MAUN;SACD,EAVA,YAUA,EAAA;;AAEa,iBAPF,YAAA,CAOO,GAAA,EANtB,MAAA,CAAK,eAMiB,EAAA,GAAA,EALtB,MAAA,CAAK,cAKiB,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAHjB,aAGiB,EAAA,EAAA,OAAA,EAFlB,OAEkB,EAAA,QAAA,EAAA;QAC1B,EAFmB,MAEnB;CAAO,EAAA,cAAA,EAAA,CAAA,GAAA,EADc,MAAA,CAAK,cACnB,EAAA,GAAA,IAAA,CAAA,EAAP,OAAO,CAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"rerank.d.cts","names":[],"sources":["../src/rerank.ts"],"sourcesContent":[],"mappings":";;;;;;AAgCuB,UAPN,YAAA,CAOM;EAKD,KAAA,EAAA,MAAA;EAAY,eAAA,EAAA,MAAA;;AAE3B,UATU,aAAA,CASL;OAEA,EAAA,MAAA,GAVM,MAUN;SACD,EAVA,YAUA,EAAA;;AAEa,iBAPF,YAAA,CAOO,GAAA,EANtB,MAAA,CAAK,eAMiB,EAAA,GAAA,EALtB,MAAA,CAAK,cAKiB,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAHjB,aAGiB,EAAA,EAAA,OAAA,EAFlB,OAEkB,EAAA,QAAA,EAAA;QAC1B,EAFmB,MAEnB;EAAO,MAAA,CAAA,EAAA,OAAA;yBADc,MAAA,CAAK,0BAC1B"}
package/dist/rerank.d.ts CHANGED
@@ -14,9 +14,9 @@ interface RerankFixture {
14
14
  }
15
15
  declare function handleRerank(req: http$1.IncomingMessage, res: http$1.ServerResponse, raw: string, fixtures: RerankFixture[], journal: Journal, defaults: {
16
16
  logger: Logger;
17
+ strict?: boolean;
17
18
  }, setCorsHeaders: (res: http$1.ServerResponse) => void): Promise<void>;
18
19
  //# sourceMappingURL=rerank.d.ts.map
19
-
20
20
  //#endregion
21
21
  export { RerankFixture, RerankResult, handleRerank };
22
22
  //# sourceMappingURL=rerank.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rerank.d.ts","names":[],"sources":["../src/rerank.ts"],"sourcesContent":[],"mappings":";;;;;;AAsBuB,UAPN,YAAA,CAOM;EAKD,KAAA,EAAA,MAAA;EAAY,eAAA,EAAA,MAAA;;AAE3B,UATU,aAAA,CASL;OAEA,EAAA,MAAA,GAVM,MAUN;SACD,EAVA,YAUA,EAAA;;AAEa,iBAPF,YAAA,CAOO,GAAA,EANtB,MAAA,CAAK,eAMiB,EAAA,GAAA,EALtB,MAAA,CAAK,cAKiB,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAHjB,aAGiB,EAAA,EAAA,OAAA,EAFlB,OAEkB,EAAA,QAAA,EAAA;QAC1B,EAFmB,MAEnB;CAAO,EAAA,cAAA,EAAA,CAAA,GAAA,EADc,MAAA,CAAK,cACnB,EAAA,GAAA,IAAA,CAAA,EAAP,OAAO,CAAA,IAAA,CAAA"}
1
+ {"version":3,"file":"rerank.d.ts","names":[],"sources":["../src/rerank.ts"],"sourcesContent":[],"mappings":";;;;;;AAgCuB,UAPN,YAAA,CAOM;EAKD,KAAA,EAAA,MAAA;EAAY,eAAA,EAAA,MAAA;;AAE3B,UATU,aAAA,CASL;OAEA,EAAA,MAAA,GAVM,MAUN;SACD,EAVA,YAUA,EAAA;;AAEa,iBAPF,YAAA,CAOO,GAAA,EANtB,MAAA,CAAK,eAMiB,EAAA,GAAA,EALtB,MAAA,CAAK,cAKiB,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAHjB,aAGiB,EAAA,EAAA,OAAA,EAFlB,OAEkB,EAAA,QAAA,EAAA;QAC1B,EAFmB,MAEnB;EAAO,MAAA,CAAA,EAAA,OAAA;yBADc,MAAA,CAAK,0BAC1B"}
package/dist/rerank.js CHANGED
@@ -1,4 +1,4 @@
1
- import { flattenHeaders, generateId, matchesPattern } from "./helpers.js";
1
+ import { flattenHeaders, generateId, isJsonObject, matchesPattern, normalizeTextInput, resolveStrictMode, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField } from "./helpers.js";
2
2
 
3
3
  //#region src/rerank.ts
4
4
  async function handleRerank(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
@@ -28,7 +28,46 @@ async function handleRerank(req, res, raw, fixtures, journal, defaults, setCorsH
28
28
  } }));
29
29
  return;
30
30
  }
31
- const query = body.query ?? "";
31
+ if (!isJsonObject(body)) {
32
+ journal.add({
33
+ method: req.method ?? "POST",
34
+ path: req.url ?? "/v2/rerank",
35
+ headers: flattenHeaders(req.headers),
36
+ body: null,
37
+ service: "rerank",
38
+ response: {
39
+ status: 400,
40
+ fixture: null
41
+ }
42
+ });
43
+ res.writeHead(400, { "Content-Type": "application/json" });
44
+ res.end(JSON.stringify({ error: {
45
+ message: "Request body must be a JSON object",
46
+ type: "invalid_request_error"
47
+ } }));
48
+ return;
49
+ }
50
+ const normalizedQuery = normalizeTextInput(body.query ?? "");
51
+ if (normalizedQuery === null) {
52
+ journal.add({
53
+ method: req.method ?? "POST",
54
+ path: req.url ?? "/v2/rerank",
55
+ headers: flattenHeaders(req.headers),
56
+ body: null,
57
+ service: "rerank",
58
+ response: {
59
+ status: 400,
60
+ fixture: null
61
+ }
62
+ });
63
+ res.writeHead(400, { "Content-Type": "application/json" });
64
+ res.end(JSON.stringify({ error: {
65
+ message: "Invalid parameter: 'query' must be a string or an array of strings",
66
+ type: "invalid_request_error"
67
+ } }));
68
+ return;
69
+ }
70
+ const query = normalizedQuery;
32
71
  let matchedResults = [];
33
72
  let matchedFixture = null;
34
73
  for (const fixture of fixtures) if (matchesPattern(query, fixture.match)) {
@@ -38,6 +77,29 @@ async function handleRerank(req, res, raw, fixtures, journal, defaults, setCorsH
38
77
  }
39
78
  if (matchedFixture) logger.debug(`Rerank fixture matched for query "${query.slice(0, 80)}"`);
40
79
  else logger.debug(`No rerank fixture matched for query "${query.slice(0, 80)}" — returning empty`);
80
+ if (!matchedFixture && resolveStrictMode(defaults.strict, req.headers)) {
81
+ const strictMessage = strictNoMatchMessage(0);
82
+ logger.error(strictNoMatchLogLine(req.method ?? "POST", req.url ?? "/v2/rerank", 0));
83
+ journal.add({
84
+ method: req.method ?? "POST",
85
+ path: req.url ?? "/v2/rerank",
86
+ headers: flattenHeaders(req.headers),
87
+ body: null,
88
+ service: "rerank",
89
+ response: {
90
+ status: 503,
91
+ fixture: null,
92
+ ...strictOverrideField(defaults.strict, req.headers)
93
+ }
94
+ });
95
+ res.writeHead(503, { "Content-Type": "application/json" });
96
+ res.end(JSON.stringify({ error: {
97
+ message: strictMessage,
98
+ type: "invalid_request_error",
99
+ code: "no_fixture_match"
100
+ } }));
101
+ return;
102
+ }
41
103
  const results = matchedResults.map((r) => ({
42
104
  index: r.index,
43
105
  relevance_score: r.relevance_score
@@ -50,7 +112,8 @@ async function handleRerank(req, res, raw, fixtures, journal, defaults, setCorsH
50
112
  service: "rerank",
51
113
  response: {
52
114
  status: 200,
53
- fixture: null
115
+ fixture: null,
116
+ ...strictOverrideField(defaults.strict, req.headers)
54
117
  }
55
118
  });
56
119
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -1 +1 @@
1
- {"version":3,"file":"rerank.js","names":[],"sources":["../src/rerank.ts"],"sourcesContent":["/**\n * Reranking API support for LLMock.\n *\n * Handles POST /v2/rerank requests (Cohere rerank-compatible). Matches\n * fixtures by comparing the request `query` field against registered\n * patterns. First match wins; no match returns empty results.\n */\n\nimport type * as http from \"node:http\";\nimport { flattenHeaders, generateId, matchesPattern } from \"./helpers.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ─── Rerank types ─────────────────────────────────────────────────────────\n\nexport interface RerankResult {\n index: number;\n relevance_score: number;\n}\n\nexport interface RerankFixture {\n match: string | RegExp;\n results: RerankResult[];\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nexport async function handleRerank(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: RerankFixture[],\n journal: Journal,\n defaults: { logger: Logger },\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let body: { query?: string; model?: string };\n try {\n body = JSON.parse(raw) as { query?: string; model?: string };\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n code: \"invalid_json\",\n },\n }),\n );\n return;\n }\n\n const query = body.query ?? \"\";\n\n // Find first matching fixture\n let matchedResults: RerankResult[] = [];\n let matchedFixture: RerankFixture | null = null;\n\n for (const fixture of fixtures) {\n if (matchesPattern(query, fixture.match)) {\n matchedFixture = fixture;\n matchedResults = fixture.results;\n break;\n }\n }\n\n if (matchedFixture) {\n logger.debug(`Rerank fixture matched for query \"${query.slice(0, 80)}\"`);\n } else {\n logger.debug(`No rerank fixture matched for query \"${query.slice(0, 80)}\" — returning empty`);\n }\n\n // Build response (Cohere rerank v2 format: index + relevance_score only)\n const results = matchedResults.map((r) => ({\n index: r.index,\n relevance_score: r.relevance_score,\n }));\n\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: { status: 200, fixture: null },\n });\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n id: generateId(\"rerank\"),\n results,\n meta: {\n billed_units: { search_units: 0 },\n },\n }),\n );\n}\n"],"mappings":";;;AA2BA,eAAsB,aACpB,KACA,KACA,KACA,UACA,SACA,UACA,gBACe;CACf,MAAM,EAAE,WAAW;AACnB,gBAAe,IAAI;CAEnB,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;UACf,UAAU;EACjB,MAAM,SAAS,oBAAoB,QAAQ,SAAS,UAAU;AAC9D,UAAQ,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,OAAO;GACjB,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,SAAS;GACT,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,MAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,MAAI,IACF,KAAK,UAAU,EACb,OAAO;GACL,SAAS,mBAAmB;GAC5B,MAAM;GACN,MAAM;GACP,EACF,CAAC,CACH;AACD;;CAGF,MAAM,QAAQ,KAAK,SAAS;CAG5B,IAAI,iBAAiC,EAAE;CACvC,IAAI,iBAAuC;AAE3C,MAAK,MAAM,WAAW,SACpB,KAAI,eAAe,OAAO,QAAQ,MAAM,EAAE;AACxC,mBAAiB;AACjB,mBAAiB,QAAQ;AACzB;;AAIJ,KAAI,eACF,QAAO,MAAM,qCAAqC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG;KAExE,QAAO,MAAM,wCAAwC,MAAM,MAAM,GAAG,GAAG,CAAC,qBAAqB;CAI/F,MAAM,UAAU,eAAe,KAAK,OAAO;EACzC,OAAO,EAAE;EACT,iBAAiB,EAAE;EACpB,EAAE;AAEH,SAAQ,IAAI;EACV,QAAQ,IAAI,UAAU;EACtB,MAAM,IAAI,OAAO;EACjB,SAAS,eAAe,IAAI,QAAQ;EACpC,MAAM;EACN,SAAS;EACT,UAAU;GAAE,QAAQ;GAAK,SAAS;GAAM;EACzC,CAAC;AAEF,KAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,KAAI,IACF,KAAK,UAAU;EACb,IAAI,WAAW,SAAS;EACxB;EACA,MAAM,EACJ,cAAc,EAAE,cAAc,GAAG,EAClC;EACF,CAAC,CACH"}
1
+ {"version":3,"file":"rerank.js","names":[],"sources":["../src/rerank.ts"],"sourcesContent":["/**\n * Reranking API support for LLMock.\n *\n * Handles POST /v2/rerank requests (Cohere rerank-compatible). Matches\n * fixtures by comparing the request `query` field against registered\n * patterns. First match wins; no match returns empty results.\n */\n\nimport type * as http from \"node:http\";\nimport {\n flattenHeaders,\n generateId,\n isJsonObject,\n matchesPattern,\n normalizeTextInput,\n resolveStrictMode,\n strictNoMatchLogLine,\n strictNoMatchMessage,\n strictOverrideField,\n} from \"./helpers.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ─── Rerank types ─────────────────────────────────────────────────────────\n\nexport interface RerankResult {\n index: number;\n relevance_score: number;\n}\n\nexport interface RerankFixture {\n match: string | RegExp;\n results: RerankResult[];\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nexport async function handleRerank(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: RerankFixture[],\n journal: Journal,\n defaults: { logger: Logger; strict?: boolean },\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let body: { query?: unknown; model?: string };\n try {\n body = JSON.parse(raw) as { query?: unknown; model?: string };\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n code: \"invalid_json\",\n },\n }),\n );\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object (e.g. `null`) before\n // touching fields — otherwise `body.query` throws a TypeError that surfaces\n // as a 500 instead of a 400.\n if (!isJsonObject(body)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Request body must be a JSON object\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const rawQuery: unknown = body.query ?? \"\";\n const normalizedQuery = normalizeTextInput(rawQuery);\n if (normalizedQuery === null) {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Invalid parameter: 'query' must be a string or an array of strings\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n const query = normalizedQuery;\n\n // Find first matching fixture\n let matchedResults: RerankResult[] = [];\n let matchedFixture: RerankFixture | null = null;\n\n for (const fixture of fixtures) {\n if (matchesPattern(query, fixture.match)) {\n matchedFixture = fixture;\n matchedResults = fixture.results;\n break;\n }\n }\n\n if (matchedFixture) {\n logger.debug(`Rerank fixture matched for query \"${query.slice(0, 80)}\"`);\n } else {\n logger.debug(`No rerank fixture matched for query \"${query.slice(0, 80)}\" — returning empty`);\n }\n\n // Strict mode turns the lenient default (empty results) into a no-match\n // error, like every other fixture-matching handler. These fixtures carry no\n // sequence/turn state, so the skipped count is always 0.\n if (!matchedFixture && resolveStrictMode(defaults.strict, req.headers)) {\n const strictMessage = strictNoMatchMessage(0);\n logger.error(strictNoMatchLogLine(req.method ?? \"POST\", req.url ?? \"/v2/rerank\", 0));\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n res.writeHead(503, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n code: \"no_fixture_match\",\n },\n }),\n );\n return;\n }\n\n // Build response (Cohere rerank v2 format: index + relevance_score only)\n const results = matchedResults.map((r) => ({\n index: r.index,\n relevance_score: r.relevance_score,\n }));\n\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/v2/rerank\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"rerank\",\n response: {\n status: 200,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n id: generateId(\"rerank\"),\n results,\n meta: {\n billed_units: { search_units: 0 },\n },\n }),\n );\n}\n"],"mappings":"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"}
@@ -852,6 +852,23 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
852
852
  } }));
853
853
  return;
854
854
  }
855
+ if (!require_helpers.isJsonObject(responsesReq)) {
856
+ journal.add({
857
+ method: req.method ?? "POST",
858
+ path: req.url ?? "/v1/responses",
859
+ headers: require_helpers.flattenHeaders(req.headers),
860
+ body: null,
861
+ response: {
862
+ status: 400,
863
+ fixture: null
864
+ }
865
+ });
866
+ require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
867
+ message: "Request body must be a JSON object",
868
+ type: "invalid_request_error"
869
+ } }));
870
+ return;
871
+ }
855
872
  const completionReq = responsesToCompletionRequest(responsesReq);
856
873
  completionReq._endpointType = "chat";
857
874
  completionReq._context = require_helpers.getContext(req);
@@ -863,7 +880,7 @@ async function handleResponses(req, res, raw, fixtures, journal, defaults, setCo
863
880
  defaults.logger.debug(`Responses fixture matched for ${req.method ?? "POST"} ${req.url ?? "/v1/responses"}`);
864
881
  journal.incrementFixtureMatchCount(fixture, fixtures, testId);
865
882
  } else defaults.logger.debug(`No responses fixture matched for ${req.method ?? "POST"} ${req.url ?? "/v1/responses"}`);
866
- if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
883
+ if (require_chaos.applyChaos(res, fixture, defaults.chaos, req.headers, req.url, journal, {
867
884
  method: req.method ?? "POST",
868
885
  path: req.url ?? "/v1/responses",
869
886
  headers: require_helpers.flattenHeaders(req.headers),