@copilotkit/aimock 1.22.0 → 1.22.1

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 (178) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +15 -0
  4. package/dist/agui-types.d.cts.map +1 -1
  5. package/dist/agui-types.d.ts.map +1 -1
  6. package/dist/aimock-cli.cjs +0 -0
  7. package/dist/aimock-cli.js +0 -0
  8. package/dist/bedrock-converse.cjs +62 -22
  9. package/dist/bedrock-converse.cjs.map +1 -1
  10. package/dist/bedrock-converse.d.cts.map +1 -1
  11. package/dist/bedrock-converse.d.ts.map +1 -1
  12. package/dist/bedrock-converse.js +62 -22
  13. package/dist/bedrock-converse.js.map +1 -1
  14. package/dist/bedrock.cjs +59 -20
  15. package/dist/bedrock.cjs.map +1 -1
  16. package/dist/bedrock.d.cts.map +1 -1
  17. package/dist/bedrock.d.ts.map +1 -1
  18. package/dist/bedrock.js +59 -20
  19. package/dist/bedrock.js.map +1 -1
  20. package/dist/cli.cjs +1 -1
  21. package/dist/cli.cjs.map +1 -1
  22. package/dist/cli.js +1 -1
  23. package/dist/cli.js.map +1 -1
  24. package/dist/cohere.cjs +29 -9
  25. package/dist/cohere.cjs.map +1 -1
  26. package/dist/cohere.d.cts.map +1 -1
  27. package/dist/cohere.d.ts.map +1 -1
  28. package/dist/cohere.js +30 -10
  29. package/dist/cohere.js.map +1 -1
  30. package/dist/config-loader.d.cts.map +1 -1
  31. package/dist/constants.cjs +8 -0
  32. package/dist/constants.cjs.map +1 -0
  33. package/dist/constants.d.cts +8 -0
  34. package/dist/constants.d.cts.map +1 -0
  35. package/dist/constants.d.ts +8 -0
  36. package/dist/constants.d.ts.map +1 -0
  37. package/dist/constants.js +7 -0
  38. package/dist/constants.js.map +1 -0
  39. package/dist/elevenlabs-audio.cjs +41 -18
  40. package/dist/elevenlabs-audio.cjs.map +1 -1
  41. package/dist/elevenlabs-audio.d.cts.map +1 -1
  42. package/dist/elevenlabs-audio.d.ts.map +1 -1
  43. package/dist/elevenlabs-audio.js +42 -19
  44. package/dist/elevenlabs-audio.js.map +1 -1
  45. package/dist/embeddings.cjs +19 -17
  46. package/dist/embeddings.cjs.map +1 -1
  47. package/dist/embeddings.d.cts.map +1 -1
  48. package/dist/embeddings.d.ts.map +1 -1
  49. package/dist/embeddings.js +20 -18
  50. package/dist/embeddings.js.map +1 -1
  51. package/dist/fal-audio.cjs +128 -39
  52. package/dist/fal-audio.cjs.map +1 -1
  53. package/dist/fal-audio.d.cts.map +1 -1
  54. package/dist/fal-audio.d.ts.map +1 -1
  55. package/dist/fal-audio.js +129 -40
  56. package/dist/fal-audio.js.map +1 -1
  57. package/dist/fal.cjs +25 -8
  58. package/dist/fal.cjs.map +1 -1
  59. package/dist/fal.d.cts.map +1 -1
  60. package/dist/fal.d.ts.map +1 -1
  61. package/dist/fal.js +26 -9
  62. package/dist/fal.js.map +1 -1
  63. package/dist/fixture-loader.cjs +9 -1
  64. package/dist/fixture-loader.cjs.map +1 -1
  65. package/dist/fixture-loader.js +9 -1
  66. package/dist/fixture-loader.js.map +1 -1
  67. package/dist/gemini-interactions.cjs +29 -7
  68. package/dist/gemini-interactions.cjs.map +1 -1
  69. package/dist/gemini-interactions.js +28 -8
  70. package/dist/gemini-interactions.js.map +1 -1
  71. package/dist/gemini.cjs +45 -19
  72. package/dist/gemini.cjs.map +1 -1
  73. package/dist/gemini.d.cts.map +1 -1
  74. package/dist/gemini.d.ts.map +1 -1
  75. package/dist/gemini.js +45 -19
  76. package/dist/gemini.js.map +1 -1
  77. package/dist/helpers.cjs +51 -8
  78. package/dist/helpers.cjs.map +1 -1
  79. package/dist/helpers.d.cts +6 -0
  80. package/dist/helpers.d.cts.map +1 -1
  81. package/dist/helpers.d.ts +6 -0
  82. package/dist/helpers.d.ts.map +1 -1
  83. package/dist/helpers.js +51 -9
  84. package/dist/helpers.js.map +1 -1
  85. package/dist/images.cjs +26 -8
  86. package/dist/images.cjs.map +1 -1
  87. package/dist/images.d.cts.map +1 -1
  88. package/dist/images.d.ts.map +1 -1
  89. package/dist/images.js +27 -9
  90. package/dist/images.js.map +1 -1
  91. package/dist/index.cjs +2 -1
  92. package/dist/index.d.cts +2 -1
  93. package/dist/index.d.ts +2 -1
  94. package/dist/index.js +2 -1
  95. package/dist/journal.cjs +17 -7
  96. package/dist/journal.cjs.map +1 -1
  97. package/dist/journal.d.cts +2 -3
  98. package/dist/journal.d.cts.map +1 -1
  99. package/dist/journal.d.ts +2 -3
  100. package/dist/journal.d.ts.map +1 -1
  101. package/dist/journal.js +15 -4
  102. package/dist/journal.js.map +1 -1
  103. package/dist/messages.cjs +33 -12
  104. package/dist/messages.cjs.map +1 -1
  105. package/dist/messages.d.cts.map +1 -1
  106. package/dist/messages.d.ts.map +1 -1
  107. package/dist/messages.js +33 -12
  108. package/dist/messages.js.map +1 -1
  109. package/dist/ollama.cjs +59 -18
  110. package/dist/ollama.cjs.map +1 -1
  111. package/dist/ollama.d.cts.map +1 -1
  112. package/dist/ollama.d.ts.map +1 -1
  113. package/dist/ollama.js +60 -19
  114. package/dist/ollama.js.map +1 -1
  115. package/dist/recorder.cjs +11 -7
  116. package/dist/recorder.cjs.map +1 -1
  117. package/dist/recorder.d.cts.map +1 -1
  118. package/dist/recorder.d.ts.map +1 -1
  119. package/dist/recorder.js +11 -7
  120. package/dist/recorder.js.map +1 -1
  121. package/dist/responses.cjs +61 -52
  122. package/dist/responses.cjs.map +1 -1
  123. package/dist/responses.d.cts +1 -1
  124. package/dist/responses.d.cts.map +1 -1
  125. package/dist/responses.d.ts +1 -1
  126. package/dist/responses.d.ts.map +1 -1
  127. package/dist/responses.js +62 -53
  128. package/dist/responses.js.map +1 -1
  129. package/dist/server.cjs +63 -179
  130. package/dist/server.cjs.map +1 -1
  131. package/dist/server.d.cts.map +1 -1
  132. package/dist/server.d.ts.map +1 -1
  133. package/dist/server.js +39 -155
  134. package/dist/server.js.map +1 -1
  135. package/dist/speech.cjs +26 -8
  136. package/dist/speech.cjs.map +1 -1
  137. package/dist/speech.d.cts.map +1 -1
  138. package/dist/speech.d.ts.map +1 -1
  139. package/dist/speech.js +27 -9
  140. package/dist/speech.js.map +1 -1
  141. package/dist/transcription.cjs +57 -19
  142. package/dist/transcription.cjs.map +1 -1
  143. package/dist/transcription.d.cts.map +1 -1
  144. package/dist/transcription.d.ts.map +1 -1
  145. package/dist/transcription.js +58 -20
  146. package/dist/transcription.js.map +1 -1
  147. package/dist/types.d.cts +2 -0
  148. package/dist/types.d.cts.map +1 -1
  149. package/dist/types.d.ts +2 -0
  150. package/dist/types.d.ts.map +1 -1
  151. package/dist/vector-types.d.ts.map +1 -1
  152. package/dist/video.cjs +50 -14
  153. package/dist/video.cjs.map +1 -1
  154. package/dist/video.d.cts +8 -1
  155. package/dist/video.d.cts.map +1 -1
  156. package/dist/video.d.ts +8 -1
  157. package/dist/video.d.ts.map +1 -1
  158. package/dist/video.js +51 -15
  159. package/dist/video.js.map +1 -1
  160. package/dist/ws-gemini-live.cjs +34 -27
  161. package/dist/ws-gemini-live.cjs.map +1 -1
  162. package/dist/ws-gemini-live.d.cts.map +1 -1
  163. package/dist/ws-gemini-live.d.ts.map +1 -1
  164. package/dist/ws-gemini-live.js +34 -27
  165. package/dist/ws-gemini-live.js.map +1 -1
  166. package/dist/ws-realtime.cjs +251 -12
  167. package/dist/ws-realtime.cjs.map +1 -1
  168. package/dist/ws-realtime.d.cts.map +1 -1
  169. package/dist/ws-realtime.d.ts.map +1 -1
  170. package/dist/ws-realtime.js +251 -12
  171. package/dist/ws-realtime.js.map +1 -1
  172. package/dist/ws-responses.cjs +48 -12
  173. package/dist/ws-responses.cjs.map +1 -1
  174. package/dist/ws-responses.d.cts.map +1 -1
  175. package/dist/ws-responses.d.ts.map +1 -1
  176. package/dist/ws-responses.js +49 -13
  177. package/dist/ws-responses.js.map +1 -1
  178. package/package.json +2 -2
package/dist/ollama.cjs CHANGED
@@ -362,8 +362,31 @@ async function handleOllama(req, res, raw, fixtures, journal, defaults, setCorsH
362
362
  body: completionReq
363
363
  }, fixture ? "fixture" : "proxy", defaults.registry, defaults.logger)) return;
364
364
  if (!fixture) {
365
+ if (require_helpers.resolveStrictMode(defaults.strict, req.headers)) {
366
+ const strictStatus = 503;
367
+ const strictMessage = "Strict mode: no fixture matched";
368
+ logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${urlPath}`);
369
+ journal.add({
370
+ method: req.method ?? "POST",
371
+ path: urlPath,
372
+ headers: require_helpers.flattenHeaders(req.headers),
373
+ body: completionReq,
374
+ response: {
375
+ status: strictStatus,
376
+ fixture: null,
377
+ ...require_helpers.strictOverrideField(defaults.strict, req.headers)
378
+ }
379
+ });
380
+ require_sse_writer.writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
381
+ message: strictMessage,
382
+ type: "invalid_request_error"
383
+ } }));
384
+ return;
385
+ }
365
386
  if (defaults.record) {
366
- if (await require_recorder.proxyAndRecord(req, res, completionReq, "ollama", urlPath, fixtures, defaults, raw) !== "not_configured") {
387
+ const outcome = await require_recorder.proxyAndRecord(req, res, completionReq, "ollama", urlPath, fixtures, defaults, raw);
388
+ if (outcome === "handled_by_hook") return;
389
+ if (outcome !== "not_configured") {
367
390
  journal.add({
368
391
  method: req.method ?? "POST",
369
392
  path: urlPath,
@@ -378,23 +401,19 @@ async function handleOllama(req, res, raw, fixtures, journal, defaults, setCorsH
378
401
  return;
379
402
  }
380
403
  }
381
- const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
382
- const strictStatus = effectiveStrict ? 503 : 404;
383
- const strictMessage = effectiveStrict ? "Strict mode: no fixture matched" : "No fixture matched";
384
- if (effectiveStrict) logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${urlPath}`);
385
404
  journal.add({
386
405
  method: req.method ?? "POST",
387
406
  path: urlPath,
388
407
  headers: require_helpers.flattenHeaders(req.headers),
389
408
  body: completionReq,
390
409
  response: {
391
- status: strictStatus,
410
+ status: 404,
392
411
  fixture: null,
393
412
  ...require_helpers.strictOverrideField(defaults.strict, req.headers)
394
413
  }
395
414
  });
396
- require_sse_writer.writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
397
- message: strictMessage,
415
+ require_sse_writer.writeErrorResponse(res, 404, JSON.stringify({ error: {
416
+ message: "No fixture matched",
398
417
  type: "invalid_request_error"
399
418
  } }));
400
419
  return;
@@ -415,10 +434,11 @@ async function handleOllama(req, res, raw, fixtures, journal, defaults, setCorsH
415
434
  fixture
416
435
  }
417
436
  });
418
- require_sse_writer.writeErrorResponse(res, status, JSON.stringify(response));
437
+ require_sse_writer.writeErrorResponse(res, status, require_helpers.serializeErrorResponse(response));
419
438
  return;
420
439
  }
421
440
  if (require_helpers.isContentWithToolCallsResponse(response)) {
441
+ if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Ollama API -- ignoring");
422
442
  const journalEntry = journal.add({
423
443
  method: req.method ?? "POST",
424
444
  path: urlPath,
@@ -451,6 +471,7 @@ async function handleOllama(req, res, raw, fixtures, journal, defaults, setCorsH
451
471
  return;
452
472
  }
453
473
  if (require_helpers.isTextResponse(response)) {
474
+ if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Ollama API -- ignoring");
454
475
  const journalEntry = journal.add({
455
476
  method: req.method ?? "POST",
456
477
  path: urlPath,
@@ -483,6 +504,7 @@ async function handleOllama(req, res, raw, fixtures, journal, defaults, setCorsH
483
504
  return;
484
505
  }
485
506
  if (require_helpers.isToolCallResponse(response)) {
507
+ if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Ollama API — ignoring");
486
508
  const journalEntry = journal.add({
487
509
  method: req.method ?? "POST",
488
510
  path: urlPath,
@@ -585,8 +607,31 @@ async function handleOllamaGenerate(req, res, raw, fixtures, journal, defaults,
585
607
  body: completionReq
586
608
  }, fixture ? "fixture" : "proxy", defaults.registry, defaults.logger)) return;
587
609
  if (!fixture) {
610
+ if (require_helpers.resolveStrictMode(defaults.strict, req.headers)) {
611
+ const strictStatus = 503;
612
+ const strictMessage = "Strict mode: no fixture matched";
613
+ defaults.logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${urlPath}`);
614
+ journal.add({
615
+ method: req.method ?? "POST",
616
+ path: urlPath,
617
+ headers: require_helpers.flattenHeaders(req.headers),
618
+ body: completionReq,
619
+ response: {
620
+ status: strictStatus,
621
+ fixture: null,
622
+ ...require_helpers.strictOverrideField(defaults.strict, req.headers)
623
+ }
624
+ });
625
+ require_sse_writer.writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
626
+ message: strictMessage,
627
+ type: "invalid_request_error"
628
+ } }));
629
+ return;
630
+ }
588
631
  if (defaults.record) {
589
- if (await require_recorder.proxyAndRecord(req, res, completionReq, "ollama", urlPath, fixtures, defaults, raw) !== "not_configured") {
632
+ const outcome = await require_recorder.proxyAndRecord(req, res, completionReq, "ollama", urlPath, fixtures, defaults, raw);
633
+ if (outcome === "handled_by_hook") return;
634
+ if (outcome !== "not_configured") {
590
635
  journal.add({
591
636
  method: req.method ?? "POST",
592
637
  path: urlPath,
@@ -601,23 +646,19 @@ async function handleOllamaGenerate(req, res, raw, fixtures, journal, defaults,
601
646
  return;
602
647
  }
603
648
  }
604
- const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
605
- const strictStatus = effectiveStrict ? 503 : 404;
606
- const strictMessage = effectiveStrict ? "Strict mode: no fixture matched" : "No fixture matched";
607
- if (effectiveStrict) defaults.logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${urlPath}`);
608
649
  journal.add({
609
650
  method: req.method ?? "POST",
610
651
  path: urlPath,
611
652
  headers: require_helpers.flattenHeaders(req.headers),
612
653
  body: completionReq,
613
654
  response: {
614
- status: strictStatus,
655
+ status: 404,
615
656
  fixture: null,
616
657
  ...require_helpers.strictOverrideField(defaults.strict, req.headers)
617
658
  }
618
659
  });
619
- require_sse_writer.writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
620
- message: strictMessage,
660
+ require_sse_writer.writeErrorResponse(res, 404, JSON.stringify({ error: {
661
+ message: "No fixture matched",
621
662
  type: "invalid_request_error"
622
663
  } }));
623
664
  return;
@@ -638,7 +679,7 @@ async function handleOllamaGenerate(req, res, raw, fixtures, journal, defaults,
638
679
  fixture
639
680
  }
640
681
  });
641
- require_sse_writer.writeErrorResponse(res, status, JSON.stringify(response));
682
+ require_sse_writer.writeErrorResponse(res, status, require_helpers.serializeErrorResponse(response));
642
683
  return;
643
684
  }
644
685
  if (require_helpers.isTextResponse(response)) {
@@ -1 +1 @@
1
- {"version":3,"file":"ollama.cjs","names":["flattenHeaders","getTestId","matchFixture","applyChaos","proxyAndRecord","resolveStrictMode","strictOverrideField","resolveResponse","isErrorResponse","isContentWithToolCallsResponse","createInterruptionSignal","writeNDJSONStream","isTextResponse","isToolCallResponse"],"sources":["../src/ollama.ts"],"sourcesContent":["/**\n * Ollama API endpoint support.\n *\n * Translates incoming /api/chat and /api/generate requests into the\n * ChatCompletionRequest format used by the fixture router, and converts\n * fixture responses back into Ollama's NDJSON streaming or non-streaming format.\n *\n * Key differences from OpenAI:\n * - Ollama defaults to stream: true (opposite of OpenAI)\n * - Streaming uses NDJSON, not SSE\n * - Tool call arguments are objects, not JSON strings\n * - Tool calls have no id field\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n flattenHeaders,\n getTestId,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { writeNDJSONStream } from \"./ndjson-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n// ─── Ollama request types ────────────────────────────────────────────────────\n\ninterface OllamaMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string;\n tool_calls?: Array<{ function: { name: string; arguments: unknown } }>;\n images?: string[];\n}\n\ninterface OllamaToolDef {\n type: string;\n function: {\n name: string;\n description?: string;\n parameters?: object;\n };\n}\n\ninterface OllamaRequest {\n model: string;\n messages: OllamaMessage[];\n stream?: boolean; // default true!\n options?: { temperature?: number; num_predict?: number };\n tools?: OllamaToolDef[];\n}\n\ninterface OllamaGenerateRequest {\n model: string;\n prompt: string;\n stream?: boolean; // default true!\n options?: { temperature?: number; num_predict?: number };\n system?: string;\n images?: string[];\n}\n\n// ─── Duration fields (zeroed, required on final/non-streaming responses) ────\n\nconst DURATION_FIELDS = {\n done_reason: \"stop\" as const,\n total_duration: 0,\n load_duration: 0,\n prompt_eval_count: 0,\n prompt_eval_duration: 0,\n eval_count: 0,\n eval_duration: 0,\n};\n\n// ─── Input conversion: Ollama → ChatCompletionRequest ────────────────────────\n\nexport function ollamaToCompletionRequest(req: OllamaRequest): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n for (const msg of req.messages) {\n const chatMsg: ChatMessage = {\n role: msg.role as ChatMessage[\"role\"],\n content: msg.content,\n };\n\n // Map inbound tool_calls on assistant messages to the internal format\n if (msg.tool_calls && msg.tool_calls.length > 0) {\n chatMsg.tool_calls = msg.tool_calls.map((tc, i) => ({\n id: `call_${i}`,\n type: \"function\" as const,\n function: {\n name: tc.function.name,\n arguments:\n typeof tc.function.arguments === \"string\"\n ? tc.function.arguments\n : JSON.stringify(tc.function.arguments),\n },\n }));\n }\n\n messages.push(chatMsg);\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n tools = req.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n },\n }));\n }\n\n return {\n model: req.model,\n messages,\n stream: req.stream ?? true,\n temperature: req.options?.temperature,\n max_tokens: req.options?.num_predict,\n tools,\n };\n}\n\nfunction ollamaGenerateToCompletionRequest(req: OllamaGenerateRequest): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // Prepend system message if present\n if (req.system) {\n messages.push({ role: \"system\", content: req.system });\n }\n\n messages.push({ role: \"user\", content: req.prompt });\n\n return {\n model: req.model,\n messages,\n stream: req.stream ?? true,\n temperature: req.options?.temperature,\n max_tokens: req.options?.num_predict,\n };\n}\n\n// ─── Response builders: /api/chat ────────────────────────────────────────────\n\nfunction buildOllamaChatTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n): object[] {\n const chunks: object[] = [];\n const createdAt = new Date().toISOString();\n\n // Reasoning chunks (before content)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: \"\", reasoning_content: slice },\n done: false,\n });\n }\n }\n\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: slice },\n done: false,\n });\n }\n\n // Final chunk with done: true and all duration fields\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: \"\" },\n done: true,\n ...DURATION_FIELDS,\n });\n\n return chunks;\n}\n\nfunction buildOllamaChatTextResponse(content: string, model: string, reasoning?: string): object {\n return {\n model,\n created_at: new Date().toISOString(),\n message: {\n role: \"assistant\",\n content,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n },\n done: true,\n ...DURATION_FIELDS,\n };\n}\n\nfunction buildOllamaChatToolCallChunks(\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n): object[] {\n const ollamaToolCalls = toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n function: {\n name: tc.name,\n arguments: argsObj,\n },\n };\n });\n\n // Tool calls are sent in a single chunk (no streaming of individual args)\n const chunks: object[] = [];\n const createdAt = new Date().toISOString();\n chunks.push({\n model,\n created_at: createdAt,\n message: {\n role: \"assistant\",\n content: \"\",\n tool_calls: ollamaToolCalls,\n },\n done: false,\n });\n\n // Final chunk\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: \"\" },\n done: true,\n ...DURATION_FIELDS,\n });\n\n return chunks;\n}\n\nfunction buildOllamaChatToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n): object {\n const ollamaToolCalls = toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n function: {\n name: tc.name,\n arguments: argsObj,\n },\n };\n });\n\n return {\n model,\n created_at: new Date().toISOString(),\n message: {\n role: \"assistant\",\n content: \"\",\n tool_calls: ollamaToolCalls,\n },\n done: true,\n ...DURATION_FIELDS,\n };\n}\n\n// ─── Response builders: /api/chat — content + tool calls ────────────────────\n\nfunction buildOllamaChatContentWithToolCallsChunks(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n logger: Logger,\n): object[] {\n const chunks: object[] = [];\n const createdAt = new Date().toISOString();\n\n // Content chunks first\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: slice },\n done: false,\n });\n }\n\n // Tool calls in a single chunk (same as tool-call-only path)\n const ollamaToolCalls = toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n function: {\n name: tc.name,\n arguments: argsObj,\n },\n };\n });\n\n chunks.push({\n model,\n created_at: createdAt,\n message: {\n role: \"assistant\",\n content: \"\",\n tool_calls: ollamaToolCalls,\n },\n done: false,\n });\n\n // Final chunk\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: \"\" },\n done: true,\n ...DURATION_FIELDS,\n });\n\n return chunks;\n}\n\nfunction buildOllamaChatContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n): object {\n const ollamaToolCalls = toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n function: {\n name: tc.name,\n arguments: argsObj,\n },\n };\n });\n\n return {\n model,\n created_at: new Date().toISOString(),\n message: {\n role: \"assistant\",\n content,\n tool_calls: ollamaToolCalls,\n },\n done: true,\n ...DURATION_FIELDS,\n };\n}\n\n// ─── Response builders: /api/generate ────────────────────────────────────────\n\nfunction buildOllamaGenerateTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n): object[] {\n const chunks: object[] = [];\n const createdAt = new Date().toISOString();\n\n // Reasoning chunks (before content)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n model,\n created_at: createdAt,\n response: \"\",\n reasoning_content: slice,\n done: false,\n });\n }\n }\n\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n model,\n created_at: createdAt,\n response: slice,\n done: false,\n });\n }\n\n // Final chunk\n chunks.push({\n model,\n created_at: createdAt,\n response: \"\",\n done: true,\n ...DURATION_FIELDS,\n context: [],\n });\n\n return chunks;\n}\n\nfunction buildOllamaGenerateTextResponse(\n content: string,\n model: string,\n reasoning?: string,\n): object {\n return {\n model,\n created_at: new Date().toISOString(),\n response: content,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n done: true,\n ...DURATION_FIELDS,\n context: [],\n };\n}\n\n// ─── Request handler: /api/chat ──────────────────────────────────────────────\n\nexport async function handleOllama(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? \"/api/chat\";\n\n let ollamaReq: OllamaRequest;\n try {\n ollamaReq = JSON.parse(raw) as OllamaRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Malformed JSON body: ${detail}`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!ollamaReq.messages || !Array.isArray(ollamaReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = ollamaToCompletionRequest(ollamaReq);\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n logger.debug(`No fixture matched for request`);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"ollama\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const strictStatus = effectiveStrict ? 503 : 404;\n const strictMessage = effectiveStrict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (effectiveStrict) {\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Ollama defaults to streaming when stream is absent or true\n const streaming = ollamaReq.stream !== false;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Content + tool calls response (must be checked before text/tool-only branches)\n if (isContentWithToolCallsResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (!streaming) {\n const body = buildOllamaChatContentWithToolCallsResponse(\n response.content,\n response.toolCalls,\n completionReq.model,\n logger,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildOllamaChatContentWithToolCallsChunks(\n response.content,\n response.toolCalls,\n completionReq.model,\n chunkSize,\n logger,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeNDJSONStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (!streaming) {\n const body = buildOllamaChatTextResponse(\n response.content,\n completionReq.model,\n response.reasoning,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildOllamaChatTextChunks(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeNDJSONStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (!streaming) {\n const body = buildOllamaChatToolCallResponse(response.toolCalls, completionReq.model, logger);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildOllamaChatToolCallChunks(response.toolCalls, completionReq.model, logger);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeNDJSONStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n\n// ─── Request handler: /api/generate ──────────────────────────────────────────\n\nexport async function handleOllamaGenerate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n setCorsHeaders(res);\n\n const urlPath = req.url ?? \"/api/generate\";\n\n let generateReq: OllamaGenerateRequest;\n try {\n generateReq = JSON.parse(raw) as OllamaGenerateRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Malformed JSON body: ${detail}`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!generateReq.prompt || typeof generateReq.prompt !== \"string\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: prompt field is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = ollamaGenerateToCompletionRequest(generateReq);\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n defaults.logger.debug(`No fixture matched for request`);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"ollama\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n const strictStatus = effectiveStrict ? 503 : 404;\n const strictMessage = effectiveStrict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n if (effectiveStrict) {\n defaults.logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Ollama defaults to streaming when stream is absent or true\n const streaming = generateReq.stream !== false;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n // Text response (only type supported for /api/generate)\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (!streaming) {\n const body = buildOllamaGenerateTextResponse(\n response.content,\n completionReq.model,\n response.reasoning,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildOllamaGenerateTextChunks(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeNDJSONStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call fixtures matched but not supported on /api/generate\n if (isToolCallResponse(response) || isContentWithToolCallsResponse(response)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 400, fixture },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Tool call fixtures are not supported on /api/generate — use /api/chat instead\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ollama.cjs","names":["flattenHeaders","getTestId","matchFixture","applyChaos","resolveStrictMode","strictOverrideField","proxyAndRecord","resolveResponse","isErrorResponse","serializeErrorResponse","isContentWithToolCallsResponse","createInterruptionSignal","writeNDJSONStream","isTextResponse","isToolCallResponse"],"sources":["../src/ollama.ts"],"sourcesContent":["/**\n * Ollama API endpoint support.\n *\n * Translates incoming /api/chat and /api/generate requests into the\n * ChatCompletionRequest format used by the fixture router, and converts\n * fixture responses back into Ollama's NDJSON streaming or non-streaming format.\n *\n * Key differences from OpenAI:\n * - Ollama defaults to stream: true (opposite of OpenAI)\n * - Streaming uses NDJSON, not SSE\n * - Tool call arguments are objects, not JSON strings\n * - Tool calls have no id field\n */\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n HandlerDefaults,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n isTextResponse,\n isToolCallResponse,\n isContentWithToolCallsResponse,\n isErrorResponse,\n serializeErrorResponse,\n flattenHeaders,\n getTestId,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n} from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { writeNDJSONStream } from \"./ndjson-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n// ─── Ollama request types ────────────────────────────────────────────────────\n\ninterface OllamaMessage {\n role: \"system\" | \"user\" | \"assistant\" | \"tool\";\n content: string;\n tool_calls?: Array<{ function: { name: string; arguments: unknown } }>;\n images?: string[];\n}\n\ninterface OllamaToolDef {\n type: string;\n function: {\n name: string;\n description?: string;\n parameters?: object;\n };\n}\n\ninterface OllamaRequest {\n model: string;\n messages: OllamaMessage[];\n stream?: boolean; // default true!\n options?: { temperature?: number; num_predict?: number };\n tools?: OllamaToolDef[];\n}\n\ninterface OllamaGenerateRequest {\n model: string;\n prompt: string;\n stream?: boolean; // default true!\n options?: { temperature?: number; num_predict?: number };\n system?: string;\n images?: string[];\n}\n\n// ─── Duration fields (zeroed, required on final/non-streaming responses) ────\n\nconst DURATION_FIELDS = {\n done_reason: \"stop\" as const,\n total_duration: 0,\n load_duration: 0,\n prompt_eval_count: 0,\n prompt_eval_duration: 0,\n eval_count: 0,\n eval_duration: 0,\n};\n\n// ─── Input conversion: Ollama → ChatCompletionRequest ────────────────────────\n\nexport function ollamaToCompletionRequest(req: OllamaRequest): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n for (const msg of req.messages) {\n const chatMsg: ChatMessage = {\n role: msg.role as ChatMessage[\"role\"],\n content: msg.content,\n };\n\n // Map inbound tool_calls on assistant messages to the internal format\n if (msg.tool_calls && msg.tool_calls.length > 0) {\n chatMsg.tool_calls = msg.tool_calls.map((tc, i) => ({\n id: `call_${i}`,\n type: \"function\" as const,\n function: {\n name: tc.function.name,\n arguments:\n typeof tc.function.arguments === \"string\"\n ? tc.function.arguments\n : JSON.stringify(tc.function.arguments),\n },\n }));\n }\n\n messages.push(chatMsg);\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n tools = req.tools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.function.name,\n description: t.function.description,\n parameters: t.function.parameters,\n },\n }));\n }\n\n return {\n model: req.model,\n messages,\n stream: req.stream ?? true,\n temperature: req.options?.temperature,\n max_tokens: req.options?.num_predict,\n tools,\n };\n}\n\nfunction ollamaGenerateToCompletionRequest(req: OllamaGenerateRequest): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n\n // Prepend system message if present\n if (req.system) {\n messages.push({ role: \"system\", content: req.system });\n }\n\n messages.push({ role: \"user\", content: req.prompt });\n\n return {\n model: req.model,\n messages,\n stream: req.stream ?? true,\n temperature: req.options?.temperature,\n max_tokens: req.options?.num_predict,\n };\n}\n\n// ─── Response builders: /api/chat ────────────────────────────────────────────\n\nfunction buildOllamaChatTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n): object[] {\n const chunks: object[] = [];\n const createdAt = new Date().toISOString();\n\n // Reasoning chunks (before content)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: \"\", reasoning_content: slice },\n done: false,\n });\n }\n }\n\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: slice },\n done: false,\n });\n }\n\n // Final chunk with done: true and all duration fields\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: \"\" },\n done: true,\n ...DURATION_FIELDS,\n });\n\n return chunks;\n}\n\nfunction buildOllamaChatTextResponse(content: string, model: string, reasoning?: string): object {\n return {\n model,\n created_at: new Date().toISOString(),\n message: {\n role: \"assistant\",\n content,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n },\n done: true,\n ...DURATION_FIELDS,\n };\n}\n\nfunction buildOllamaChatToolCallChunks(\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n): object[] {\n const ollamaToolCalls = toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n function: {\n name: tc.name,\n arguments: argsObj,\n },\n };\n });\n\n // Tool calls are sent in a single chunk (no streaming of individual args)\n const chunks: object[] = [];\n const createdAt = new Date().toISOString();\n chunks.push({\n model,\n created_at: createdAt,\n message: {\n role: \"assistant\",\n content: \"\",\n tool_calls: ollamaToolCalls,\n },\n done: false,\n });\n\n // Final chunk\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: \"\" },\n done: true,\n ...DURATION_FIELDS,\n });\n\n return chunks;\n}\n\nfunction buildOllamaChatToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n): object {\n const ollamaToolCalls = toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n function: {\n name: tc.name,\n arguments: argsObj,\n },\n };\n });\n\n return {\n model,\n created_at: new Date().toISOString(),\n message: {\n role: \"assistant\",\n content: \"\",\n tool_calls: ollamaToolCalls,\n },\n done: true,\n ...DURATION_FIELDS,\n };\n}\n\n// ─── Response builders: /api/chat — content + tool calls ────────────────────\n\nfunction buildOllamaChatContentWithToolCallsChunks(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n chunkSize: number,\n logger: Logger,\n): object[] {\n const chunks: object[] = [];\n const createdAt = new Date().toISOString();\n\n // Content chunks first\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: slice },\n done: false,\n });\n }\n\n // Tool calls in a single chunk (same as tool-call-only path)\n const ollamaToolCalls = toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n function: {\n name: tc.name,\n arguments: argsObj,\n },\n };\n });\n\n chunks.push({\n model,\n created_at: createdAt,\n message: {\n role: \"assistant\",\n content: \"\",\n tool_calls: ollamaToolCalls,\n },\n done: false,\n });\n\n // Final chunk\n chunks.push({\n model,\n created_at: createdAt,\n message: { role: \"assistant\", content: \"\" },\n done: true,\n ...DURATION_FIELDS,\n });\n\n return chunks;\n}\n\nfunction buildOllamaChatContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n logger: Logger,\n): object {\n const ollamaToolCalls = toolCalls.map((tc) => {\n let argsObj: unknown;\n try {\n argsObj = JSON.parse(tc.arguments || \"{}\");\n } catch {\n logger.warn(\n `Malformed JSON in fixture tool call arguments for \"${tc.name}\": ${tc.arguments}`,\n );\n argsObj = {};\n }\n return {\n function: {\n name: tc.name,\n arguments: argsObj,\n },\n };\n });\n\n return {\n model,\n created_at: new Date().toISOString(),\n message: {\n role: \"assistant\",\n content,\n tool_calls: ollamaToolCalls,\n },\n done: true,\n ...DURATION_FIELDS,\n };\n}\n\n// ─── Response builders: /api/generate ────────────────────────────────────────\n\nfunction buildOllamaGenerateTextChunks(\n content: string,\n model: string,\n chunkSize: number,\n reasoning?: string,\n): object[] {\n const chunks: object[] = [];\n const createdAt = new Date().toISOString();\n\n // Reasoning chunks (before content)\n if (reasoning) {\n for (let i = 0; i < reasoning.length; i += chunkSize) {\n const slice = reasoning.slice(i, i + chunkSize);\n chunks.push({\n model,\n created_at: createdAt,\n response: \"\",\n reasoning_content: slice,\n done: false,\n });\n }\n }\n\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n chunks.push({\n model,\n created_at: createdAt,\n response: slice,\n done: false,\n });\n }\n\n // Final chunk\n chunks.push({\n model,\n created_at: createdAt,\n response: \"\",\n done: true,\n ...DURATION_FIELDS,\n context: [],\n });\n\n return chunks;\n}\n\nfunction buildOllamaGenerateTextResponse(\n content: string,\n model: string,\n reasoning?: string,\n): object {\n return {\n model,\n created_at: new Date().toISOString(),\n response: content,\n ...(reasoning ? { reasoning_content: reasoning } : {}),\n done: true,\n ...DURATION_FIELDS,\n context: [],\n };\n}\n\n// ─── Request handler: /api/chat ──────────────────────────────────────────────\n\nexport async function handleOllama(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? \"/api/chat\";\n\n let ollamaReq: OllamaRequest;\n try {\n ollamaReq = JSON.parse(raw) as OllamaRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Malformed JSON body: ${detail}`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!ollamaReq.messages || !Array.isArray(ollamaReq.messages)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: messages array is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = ollamaToCompletionRequest(ollamaReq);\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n logger.debug(`No fixture matched for request`);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n if (effectiveStrict) {\n const strictStatus = 503;\n const strictMessage = \"Strict mode: no fixture matched\";\n logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"ollama\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({\n error: {\n message: \"No fixture matched\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Ollama defaults to streaming when stream is absent or true\n const streaming = ollamaReq.stream !== false;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, serializeErrorResponse(response));\n return;\n }\n\n // Content + tool calls response (must be checked before text/tool-only branches)\n if (isContentWithToolCallsResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Ollama API -- ignoring\");\n }\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (!streaming) {\n const body = buildOllamaChatContentWithToolCallsResponse(\n response.content,\n response.toolCalls,\n completionReq.model,\n logger,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildOllamaChatContentWithToolCallsChunks(\n response.content,\n response.toolCalls,\n completionReq.model,\n chunkSize,\n logger,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeNDJSONStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Ollama API -- ignoring\");\n }\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (!streaming) {\n const body = buildOllamaChatTextResponse(\n response.content,\n completionReq.model,\n response.reasoning,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildOllamaChatTextChunks(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeNDJSONStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\"webSearches in fixture response are not supported for Ollama API — ignoring\");\n }\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (!streaming) {\n const body = buildOllamaChatToolCallResponse(response.toolCalls, completionReq.model, logger);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildOllamaChatToolCallChunks(response.toolCalls, completionReq.model, logger);\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeNDJSONStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n\n// ─── Request handler: /api/generate ──────────────────────────────────────────\n\nexport async function handleOllamaGenerate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n setCorsHeaders(res);\n\n const urlPath = req.url ?? \"/api/generate\";\n\n let generateReq: OllamaGenerateRequest;\n try {\n generateReq = JSON.parse(raw) as OllamaGenerateRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: `Malformed JSON body: ${detail}`,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n if (!generateReq.prompt || typeof generateReq.prompt !== \"string\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Invalid request: prompt field is required\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = ollamaGenerateToCompletionRequest(generateReq);\n completionReq._endpointType = \"chat\";\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n } else {\n defaults.logger.debug(`No fixture matched for request`);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n if (effectiveStrict) {\n const strictStatus = 503;\n const strictMessage = \"Strict mode: no fixture matched\";\n defaults.logger.error(`STRICT: No fixture matched for ${req.method ?? \"POST\"} ${urlPath}`);\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n if (defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"ollama\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({\n error: {\n message: \"No fixture matched\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n const response = await resolveResponse(fixture, completionReq);\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n\n // Ollama defaults to streaming when stream is absent or true\n const streaming = generateReq.stream !== false;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, serializeErrorResponse(response));\n return;\n }\n\n // Text response (only type supported for /api/generate)\n if (isTextResponse(response)) {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n if (!streaming) {\n const body = buildOllamaGenerateTextResponse(\n response.content,\n completionReq.model,\n response.reasoning,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n } else {\n const chunks = buildOllamaGenerateTextChunks(\n response.content,\n completionReq.model,\n chunkSize,\n response.reasoning,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeNDJSONStream(res, chunks, {\n latency,\n streamingProfile: fixture.streamingProfile,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call fixtures matched but not supported on /api/generate\n if (isToolCallResponse(response) || isContentWithToolCallsResponse(response)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 400, fixture },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: {\n message: \"Tool call fixtures are not supported on /api/generate — use /api/chat instead\",\n type: \"invalid_request_error\",\n },\n }),\n );\n return;\n }\n\n // Unknown response type\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response did not match any known type\",\n type: \"server_error\",\n },\n }),\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ollama.d.cts","names":[],"sources":["../src/ollama.ts"],"sourcesContent":[],"mappings":";;;;;;UA6CU,aAAA,CA+C0E;EA8X9D,IAAA,EAAA,QAAA,GAAY,MAAA,GAAA,WAAA,GAAA,MAAA;EAAA,OAAA,EAAA,MAAA;YACtB,CAAA,EA3aG,KA2aH,CAAA;IACL,QAAK,EAAA;MAEA,IAAA,EAAA,MAAA;MACD,SAAA,EAAA,OAAA;IACC,CAAA;;QAET,CAAA,EAAA,MAAA,EAAA;;AAiTH,UA/tBU,aAAA,CA+tBY;EAAoB,IAAA,EAAA,MAAA;UACnC,EAAK;IACL,IAAA,EAAK,MAAA;IAEA,WAAA,CAAA,EAAA,MAAA;IACD,UAAA,CAAA,EAAA,MAAA;;;UA3tBD,aAAA,CA8tBP;EAAO,KAAA,EAAA,MAAA;YA5tBE;;;;;;UAGF;;iBA0BM,yBAAA,MAA+B,gBAAgB;iBA8XzC,YAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B;iBAiTmB,oBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B"}
1
+ {"version":3,"file":"ollama.d.cts","names":[],"sources":["../src/ollama.ts"],"sourcesContent":[],"mappings":";;;;;;UA8CU,aAAA,CA+C0E;EA8X9D,IAAA,EAAA,QAAA,GAAY,MAAA,GAAA,WAAA,GAAA,MAAA;EAAA,OAAA,EAAA,MAAA;YACtB,CAAA,EA3aG,KA2aH,CAAA;IACL,QAAK,EAAA;MAEA,IAAA,EAAA,MAAA;MACD,SAAA,EAAA,OAAA;IACC,CAAA;;QAET,CAAA,EAAA,MAAA,EAAA;;AA+UH,UA7vBU,aAAA,CA6vBY;EAAoB,IAAA,EAAA,MAAA;UACnC,EAAK;IACL,IAAA,EAAK,MAAA;IAEA,WAAA,CAAA,EAAA,MAAA;IACD,UAAA,CAAA,EAAA,MAAA;;;UAzvBD,aAAA,CA4vBP;EAAO,KAAA,EAAA,MAAA;YA1vBE;;;;;;UAGF;;iBA0BM,yBAAA,MAA+B,gBAAgB;iBA8XzC,YAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B;iBA+UmB,oBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"ollama.d.ts","names":[],"sources":["../src/ollama.ts"],"sourcesContent":[],"mappings":";;;;;;UA6CU,aAAA,CA+C0E;EA8X9D,IAAA,EAAA,QAAA,GAAY,MAAA,GAAA,WAAA,GAAA,MAAA;EAAA,OAAA,EAAA,MAAA;YACtB,CAAA,EA3aG,KA2aH,CAAA;IACL,QAAK,EAAA;MAEA,IAAA,EAAA,MAAA;MACD,SAAA,EAAA,OAAA;IACC,CAAA;;QAET,CAAA,EAAA,MAAA,EAAA;;AAiTH,UA/tBU,aAAA,CA+tBY;EAAoB,IAAA,EAAA,MAAA;UACnC,EAAK;IACL,IAAA,EAAK,MAAA;IAEA,WAAA,CAAA,EAAA,MAAA;IACD,UAAA,CAAA,EAAA,MAAA;;;UA3tBD,aAAA,CA8tBP;EAAO,KAAA,EAAA,MAAA;YA5tBE;;;;;;UAGF;;iBA0BM,yBAAA,MAA+B,gBAAgB;iBA8XzC,YAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B;iBAiTmB,oBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B"}
1
+ {"version":3,"file":"ollama.d.ts","names":[],"sources":["../src/ollama.ts"],"sourcesContent":[],"mappings":";;;;;;UA8CU,aAAA,CA+C0E;EA8X9D,IAAA,EAAA,QAAA,GAAY,MAAA,GAAA,WAAA,GAAA,MAAA;EAAA,OAAA,EAAA,MAAA;YACtB,CAAA,EA3aG,KA2aH,CAAA;IACL,QAAK,EAAA;MAEA,IAAA,EAAA,MAAA;MACD,SAAA,EAAA,OAAA;IACC,CAAA;;QAET,CAAA,EAAA,MAAA,EAAA;;AA+UH,UA7vBU,aAAA,CA6vBY;EAAoB,IAAA,EAAA,MAAA;UACnC,EAAK;IACL,IAAA,EAAK,MAAA;IAEA,WAAA,CAAA,EAAA,MAAA;IACD,UAAA,CAAA,EAAA,MAAA;;;UAzvBD,aAAA,CA4vBP;EAAO,KAAA,EAAA,MAAA;YA1vBE;;;;;;UAGF;;iBA0BM,yBAAA,MAA+B,gBAAgB;iBA8XzC,YAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B;iBA+UmB,oBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,0BAC1B"}
package/dist/ollama.js CHANGED
@@ -1,4 +1,4 @@
1
- import { flattenHeaders, getTestId, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse, resolveResponse, resolveStrictMode, strictOverrideField } from "./helpers.js";
1
+ import { flattenHeaders, getTestId, isContentWithToolCallsResponse, isErrorResponse, isTextResponse, isToolCallResponse, resolveResponse, resolveStrictMode, serializeErrorResponse, strictOverrideField } from "./helpers.js";
2
2
  import { matchFixture } from "./router.js";
3
3
  import { writeErrorResponse } from "./sse-writer.js";
4
4
  import { createInterruptionSignal } from "./interruption.js";
@@ -362,8 +362,31 @@ async function handleOllama(req, res, raw, fixtures, journal, defaults, setCorsH
362
362
  body: completionReq
363
363
  }, fixture ? "fixture" : "proxy", defaults.registry, defaults.logger)) return;
364
364
  if (!fixture) {
365
+ if (resolveStrictMode(defaults.strict, req.headers)) {
366
+ const strictStatus = 503;
367
+ const strictMessage = "Strict mode: no fixture matched";
368
+ logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${urlPath}`);
369
+ journal.add({
370
+ method: req.method ?? "POST",
371
+ path: urlPath,
372
+ headers: flattenHeaders(req.headers),
373
+ body: completionReq,
374
+ response: {
375
+ status: strictStatus,
376
+ fixture: null,
377
+ ...strictOverrideField(defaults.strict, req.headers)
378
+ }
379
+ });
380
+ writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
381
+ message: strictMessage,
382
+ type: "invalid_request_error"
383
+ } }));
384
+ return;
385
+ }
365
386
  if (defaults.record) {
366
- if (await proxyAndRecord(req, res, completionReq, "ollama", urlPath, fixtures, defaults, raw) !== "not_configured") {
387
+ const outcome = await proxyAndRecord(req, res, completionReq, "ollama", urlPath, fixtures, defaults, raw);
388
+ if (outcome === "handled_by_hook") return;
389
+ if (outcome !== "not_configured") {
367
390
  journal.add({
368
391
  method: req.method ?? "POST",
369
392
  path: urlPath,
@@ -378,23 +401,19 @@ async function handleOllama(req, res, raw, fixtures, journal, defaults, setCorsH
378
401
  return;
379
402
  }
380
403
  }
381
- const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
382
- const strictStatus = effectiveStrict ? 503 : 404;
383
- const strictMessage = effectiveStrict ? "Strict mode: no fixture matched" : "No fixture matched";
384
- if (effectiveStrict) logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${urlPath}`);
385
404
  journal.add({
386
405
  method: req.method ?? "POST",
387
406
  path: urlPath,
388
407
  headers: flattenHeaders(req.headers),
389
408
  body: completionReq,
390
409
  response: {
391
- status: strictStatus,
410
+ status: 404,
392
411
  fixture: null,
393
412
  ...strictOverrideField(defaults.strict, req.headers)
394
413
  }
395
414
  });
396
- writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
397
- message: strictMessage,
415
+ writeErrorResponse(res, 404, JSON.stringify({ error: {
416
+ message: "No fixture matched",
398
417
  type: "invalid_request_error"
399
418
  } }));
400
419
  return;
@@ -415,10 +434,11 @@ async function handleOllama(req, res, raw, fixtures, journal, defaults, setCorsH
415
434
  fixture
416
435
  }
417
436
  });
418
- writeErrorResponse(res, status, JSON.stringify(response));
437
+ writeErrorResponse(res, status, serializeErrorResponse(response));
419
438
  return;
420
439
  }
421
440
  if (isContentWithToolCallsResponse(response)) {
441
+ if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Ollama API -- ignoring");
422
442
  const journalEntry = journal.add({
423
443
  method: req.method ?? "POST",
424
444
  path: urlPath,
@@ -451,6 +471,7 @@ async function handleOllama(req, res, raw, fixtures, journal, defaults, setCorsH
451
471
  return;
452
472
  }
453
473
  if (isTextResponse(response)) {
474
+ if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Ollama API -- ignoring");
454
475
  const journalEntry = journal.add({
455
476
  method: req.method ?? "POST",
456
477
  path: urlPath,
@@ -483,6 +504,7 @@ async function handleOllama(req, res, raw, fixtures, journal, defaults, setCorsH
483
504
  return;
484
505
  }
485
506
  if (isToolCallResponse(response)) {
507
+ if (response.webSearches?.length) logger.warn("webSearches in fixture response are not supported for Ollama API — ignoring");
486
508
  const journalEntry = journal.add({
487
509
  method: req.method ?? "POST",
488
510
  path: urlPath,
@@ -585,8 +607,31 @@ async function handleOllamaGenerate(req, res, raw, fixtures, journal, defaults,
585
607
  body: completionReq
586
608
  }, fixture ? "fixture" : "proxy", defaults.registry, defaults.logger)) return;
587
609
  if (!fixture) {
610
+ if (resolveStrictMode(defaults.strict, req.headers)) {
611
+ const strictStatus = 503;
612
+ const strictMessage = "Strict mode: no fixture matched";
613
+ defaults.logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${urlPath}`);
614
+ journal.add({
615
+ method: req.method ?? "POST",
616
+ path: urlPath,
617
+ headers: flattenHeaders(req.headers),
618
+ body: completionReq,
619
+ response: {
620
+ status: strictStatus,
621
+ fixture: null,
622
+ ...strictOverrideField(defaults.strict, req.headers)
623
+ }
624
+ });
625
+ writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
626
+ message: strictMessage,
627
+ type: "invalid_request_error"
628
+ } }));
629
+ return;
630
+ }
588
631
  if (defaults.record) {
589
- if (await proxyAndRecord(req, res, completionReq, "ollama", urlPath, fixtures, defaults, raw) !== "not_configured") {
632
+ const outcome = await proxyAndRecord(req, res, completionReq, "ollama", urlPath, fixtures, defaults, raw);
633
+ if (outcome === "handled_by_hook") return;
634
+ if (outcome !== "not_configured") {
590
635
  journal.add({
591
636
  method: req.method ?? "POST",
592
637
  path: urlPath,
@@ -601,23 +646,19 @@ async function handleOllamaGenerate(req, res, raw, fixtures, journal, defaults,
601
646
  return;
602
647
  }
603
648
  }
604
- const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);
605
- const strictStatus = effectiveStrict ? 503 : 404;
606
- const strictMessage = effectiveStrict ? "Strict mode: no fixture matched" : "No fixture matched";
607
- if (effectiveStrict) defaults.logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${urlPath}`);
608
649
  journal.add({
609
650
  method: req.method ?? "POST",
610
651
  path: urlPath,
611
652
  headers: flattenHeaders(req.headers),
612
653
  body: completionReq,
613
654
  response: {
614
- status: strictStatus,
655
+ status: 404,
615
656
  fixture: null,
616
657
  ...strictOverrideField(defaults.strict, req.headers)
617
658
  }
618
659
  });
619
- writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
620
- message: strictMessage,
660
+ writeErrorResponse(res, 404, JSON.stringify({ error: {
661
+ message: "No fixture matched",
621
662
  type: "invalid_request_error"
622
663
  } }));
623
664
  return;
@@ -638,7 +679,7 @@ async function handleOllamaGenerate(req, res, raw, fixtures, journal, defaults,
638
679
  fixture
639
680
  }
640
681
  });
641
- writeErrorResponse(res, status, JSON.stringify(response));
682
+ writeErrorResponse(res, status, serializeErrorResponse(response));
642
683
  return;
643
684
  }
644
685
  if (isTextResponse(response)) {