modelfusion 0.104.0 → 0.105.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 (164) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/model-function/Delta.d.ts +1 -2
  3. package/model-function/executeStreamCall.cjs +6 -4
  4. package/model-function/executeStreamCall.d.ts +2 -2
  5. package/model-function/executeStreamCall.js +6 -4
  6. package/model-function/generate-speech/streamSpeech.cjs +1 -2
  7. package/model-function/generate-speech/streamSpeech.js +1 -2
  8. package/model-function/generate-structure/StructureFromTextStreamingModel.cjs +25 -29
  9. package/model-function/generate-structure/StructureFromTextStreamingModel.d.ts +3 -1
  10. package/model-function/generate-structure/StructureFromTextStreamingModel.js +25 -29
  11. package/model-function/generate-structure/StructureGenerationModel.d.ts +2 -0
  12. package/model-function/generate-structure/streamStructure.cjs +7 -8
  13. package/model-function/generate-structure/streamStructure.d.ts +1 -1
  14. package/model-function/generate-structure/streamStructure.js +7 -8
  15. package/model-function/generate-text/PromptTemplateFullTextModel.cjs +35 -0
  16. package/model-function/generate-text/PromptTemplateFullTextModel.d.ts +41 -0
  17. package/model-function/generate-text/PromptTemplateFullTextModel.js +31 -0
  18. package/model-function/generate-text/PromptTemplateTextStreamingModel.cjs +3 -0
  19. package/model-function/generate-text/PromptTemplateTextStreamingModel.d.ts +2 -1
  20. package/model-function/generate-text/PromptTemplateTextStreamingModel.js +3 -0
  21. package/model-function/generate-text/TextGenerationModel.d.ts +2 -1
  22. package/model-function/generate-text/index.cjs +1 -0
  23. package/model-function/generate-text/index.d.ts +1 -0
  24. package/model-function/generate-text/index.js +1 -0
  25. package/model-function/generate-text/prompt-template/AlpacaPromptTemplate.cjs +2 -2
  26. package/model-function/generate-text/prompt-template/AlpacaPromptTemplate.js +1 -1
  27. package/model-function/generate-text/prompt-template/ChatMLPromptTemplate.cjs +8 -5
  28. package/model-function/generate-text/prompt-template/ChatMLPromptTemplate.js +7 -4
  29. package/model-function/generate-text/prompt-template/ChatPrompt.cjs +42 -0
  30. package/model-function/generate-text/prompt-template/ChatPrompt.d.ts +27 -5
  31. package/model-function/generate-text/prompt-template/ChatPrompt.js +41 -1
  32. package/model-function/generate-text/prompt-template/{Content.cjs → ContentPart.cjs} +1 -1
  33. package/model-function/generate-text/prompt-template/ContentPart.d.ts +30 -0
  34. package/model-function/generate-text/prompt-template/{Content.js → ContentPart.js} +1 -1
  35. package/model-function/generate-text/prompt-template/InstructionPrompt.d.ts +3 -2
  36. package/model-function/generate-text/prompt-template/Llama2PromptTemplate.cjs +7 -4
  37. package/model-function/generate-text/prompt-template/Llama2PromptTemplate.js +5 -2
  38. package/model-function/generate-text/prompt-template/NeuralChatPromptTemplate.cjs +8 -4
  39. package/model-function/generate-text/prompt-template/NeuralChatPromptTemplate.js +6 -2
  40. package/model-function/generate-text/prompt-template/TextPromptTemplate.cjs +8 -4
  41. package/model-function/generate-text/prompt-template/TextPromptTemplate.js +6 -2
  42. package/model-function/generate-text/prompt-template/VicunaPromptTemplate.cjs +7 -3
  43. package/model-function/generate-text/prompt-template/VicunaPromptTemplate.js +6 -2
  44. package/model-function/generate-text/prompt-template/index.cjs +1 -1
  45. package/model-function/generate-text/prompt-template/index.d.ts +1 -1
  46. package/model-function/generate-text/prompt-template/index.js +1 -1
  47. package/model-function/generate-text/streamText.cjs +27 -28
  48. package/model-function/generate-text/streamText.d.ts +1 -0
  49. package/model-function/generate-text/streamText.js +27 -28
  50. package/model-provider/anthropic/AnthropicPromptTemplate.cjs +7 -3
  51. package/model-provider/anthropic/AnthropicPromptTemplate.js +5 -1
  52. package/model-provider/anthropic/AnthropicTextGenerationModel.cjs +8 -14
  53. package/model-provider/anthropic/AnthropicTextGenerationModel.d.ts +11 -2
  54. package/model-provider/anthropic/AnthropicTextGenerationModel.js +8 -14
  55. package/model-provider/anthropic/AnthropicTextGenerationModel.test.cjs +44 -0
  56. package/model-provider/anthropic/AnthropicTextGenerationModel.test.js +42 -0
  57. package/model-provider/cohere/CohereTextGenerationModel.cjs +6 -44
  58. package/model-provider/cohere/CohereTextGenerationModel.d.ts +45 -11
  59. package/model-provider/cohere/CohereTextGenerationModel.js +7 -45
  60. package/model-provider/cohere/CohereTextGenerationModel.test.cjs +33 -0
  61. package/model-provider/cohere/CohereTextGenerationModel.test.d.ts +1 -0
  62. package/model-provider/cohere/CohereTextGenerationModel.test.js +31 -0
  63. package/model-provider/elevenlabs/ElevenLabsSpeechModel.cjs +1 -2
  64. package/model-provider/elevenlabs/ElevenLabsSpeechModel.js +1 -2
  65. package/model-provider/llamacpp/LlamaCppBakLLaVA1PromptTemplate.cjs +6 -1
  66. package/model-provider/llamacpp/LlamaCppBakLLaVA1PromptTemplate.js +6 -1
  67. package/model-provider/llamacpp/LlamaCppTextGenerationModel.cjs +7 -14
  68. package/model-provider/llamacpp/LlamaCppTextGenerationModel.d.ts +157 -6
  69. package/model-provider/llamacpp/LlamaCppTextGenerationModel.js +8 -15
  70. package/model-provider/llamacpp/LlamaCppTextGenerationModel.test.cjs +37 -0
  71. package/model-provider/llamacpp/LlamaCppTextGenerationModel.test.d.ts +1 -0
  72. package/model-provider/llamacpp/LlamaCppTextGenerationModel.test.js +35 -0
  73. package/model-provider/mistral/MistralChatModel.cjs +30 -104
  74. package/model-provider/mistral/MistralChatModel.d.ts +47 -14
  75. package/model-provider/mistral/MistralChatModel.js +30 -104
  76. package/model-provider/mistral/MistralChatModel.test.cjs +51 -0
  77. package/model-provider/mistral/MistralChatModel.test.d.ts +1 -0
  78. package/model-provider/mistral/MistralChatModel.test.js +49 -0
  79. package/model-provider/mistral/MistralPromptTemplate.cjs +11 -4
  80. package/model-provider/mistral/MistralPromptTemplate.js +9 -2
  81. package/model-provider/ollama/OllamaChatModel.cjs +7 -43
  82. package/model-provider/ollama/OllamaChatModel.d.ts +61 -9
  83. package/model-provider/ollama/OllamaChatModel.js +7 -43
  84. package/model-provider/ollama/OllamaChatModel.test.cjs +27 -0
  85. package/model-provider/ollama/OllamaChatModel.test.d.ts +1 -0
  86. package/model-provider/ollama/OllamaChatModel.test.js +25 -0
  87. package/model-provider/ollama/OllamaChatPromptTemplate.cjs +34 -4
  88. package/model-provider/ollama/OllamaChatPromptTemplate.js +34 -4
  89. package/model-provider/ollama/OllamaCompletionModel.cjs +22 -43
  90. package/model-provider/ollama/OllamaCompletionModel.d.ts +65 -9
  91. package/model-provider/ollama/OllamaCompletionModel.js +23 -44
  92. package/model-provider/ollama/OllamaCompletionModel.test.cjs +101 -13
  93. package/model-provider/ollama/OllamaCompletionModel.test.js +78 -13
  94. package/model-provider/openai/{chat/AbstractOpenAIChatModel.cjs → AbstractOpenAIChatModel.cjs} +71 -15
  95. package/model-provider/openai/{chat/AbstractOpenAIChatModel.d.ts → AbstractOpenAIChatModel.d.ts} +273 -19
  96. package/model-provider/openai/{chat/AbstractOpenAIChatModel.js → AbstractOpenAIChatModel.js} +71 -15
  97. package/model-provider/openai/{chat/OpenAIChatFunctionCallStructureGenerationModel.cjs → OpenAIChatFunctionCallStructureGenerationModel.cjs} +18 -2
  98. package/model-provider/openai/{chat/OpenAIChatFunctionCallStructureGenerationModel.d.ts → OpenAIChatFunctionCallStructureGenerationModel.d.ts} +41 -11
  99. package/model-provider/openai/{chat/OpenAIChatFunctionCallStructureGenerationModel.js → OpenAIChatFunctionCallStructureGenerationModel.js} +18 -2
  100. package/model-provider/openai/{chat/OpenAIChatMessage.d.ts → OpenAIChatMessage.d.ts} +3 -3
  101. package/model-provider/openai/{chat/OpenAIChatModel.cjs → OpenAIChatModel.cjs} +5 -5
  102. package/model-provider/openai/{chat/OpenAIChatModel.d.ts → OpenAIChatModel.d.ts} +12 -12
  103. package/model-provider/openai/{chat/OpenAIChatModel.js → OpenAIChatModel.js} +5 -5
  104. package/model-provider/openai/OpenAIChatModel.test.cjs +94 -0
  105. package/model-provider/openai/OpenAIChatModel.test.d.ts +1 -0
  106. package/model-provider/openai/OpenAIChatModel.test.js +92 -0
  107. package/model-provider/openai/OpenAIChatPromptTemplate.cjs +114 -0
  108. package/model-provider/openai/{chat/OpenAIChatPromptTemplate.d.ts → OpenAIChatPromptTemplate.d.ts} +3 -3
  109. package/model-provider/openai/OpenAIChatPromptTemplate.js +107 -0
  110. package/model-provider/openai/OpenAICompletionModel.cjs +32 -84
  111. package/model-provider/openai/OpenAICompletionModel.d.ts +27 -10
  112. package/model-provider/openai/OpenAICompletionModel.js +33 -85
  113. package/model-provider/openai/OpenAICompletionModel.test.cjs +53 -0
  114. package/model-provider/openai/OpenAICompletionModel.test.d.ts +1 -0
  115. package/model-provider/openai/OpenAICompletionModel.test.js +51 -0
  116. package/model-provider/openai/OpenAICostCalculator.cjs +1 -1
  117. package/model-provider/openai/OpenAICostCalculator.js +1 -1
  118. package/model-provider/openai/OpenAIFacade.cjs +2 -2
  119. package/model-provider/openai/OpenAIFacade.d.ts +3 -3
  120. package/model-provider/openai/OpenAIFacade.js +2 -2
  121. package/model-provider/openai/OpenAITranscriptionModel.d.ts +6 -6
  122. package/model-provider/openai/TikTokenTokenizer.d.ts +1 -1
  123. package/model-provider/openai/{chat/countOpenAIChatMessageTokens.cjs → countOpenAIChatMessageTokens.cjs} +2 -2
  124. package/model-provider/openai/{chat/countOpenAIChatMessageTokens.js → countOpenAIChatMessageTokens.js} +2 -2
  125. package/model-provider/openai/index.cjs +6 -6
  126. package/model-provider/openai/index.d.ts +5 -6
  127. package/model-provider/openai/index.js +5 -5
  128. package/model-provider/openai-compatible/OpenAICompatibleChatModel.cjs +4 -4
  129. package/model-provider/openai-compatible/OpenAICompatibleChatModel.d.ts +6 -6
  130. package/model-provider/openai-compatible/OpenAICompatibleChatModel.js +4 -4
  131. package/package.json +5 -5
  132. package/test/JsonTestServer.cjs +33 -0
  133. package/test/JsonTestServer.d.ts +7 -0
  134. package/test/JsonTestServer.js +29 -0
  135. package/test/StreamingTestServer.cjs +55 -0
  136. package/test/StreamingTestServer.d.ts +7 -0
  137. package/test/StreamingTestServer.js +51 -0
  138. package/test/arrayFromAsync.cjs +13 -0
  139. package/test/arrayFromAsync.d.ts +1 -0
  140. package/test/arrayFromAsync.js +9 -0
  141. package/util/streaming/createEventSourceResponseHandler.cjs +9 -0
  142. package/util/streaming/createEventSourceResponseHandler.d.ts +4 -0
  143. package/util/streaming/createEventSourceResponseHandler.js +5 -0
  144. package/util/streaming/createJsonStreamResponseHandler.cjs +9 -0
  145. package/util/streaming/createJsonStreamResponseHandler.d.ts +4 -0
  146. package/util/streaming/createJsonStreamResponseHandler.js +5 -0
  147. package/util/streaming/parseEventSourceStreamAsAsyncIterable.cjs +52 -0
  148. package/util/streaming/parseEventSourceStreamAsAsyncIterable.d.ts +6 -0
  149. package/util/streaming/parseEventSourceStreamAsAsyncIterable.js +48 -0
  150. package/util/streaming/parseJsonStreamAsAsyncIterable.cjs +21 -0
  151. package/util/streaming/parseJsonStreamAsAsyncIterable.d.ts +6 -0
  152. package/util/streaming/parseJsonStreamAsAsyncIterable.js +17 -0
  153. package/model-function/generate-text/prompt-template/Content.d.ts +0 -25
  154. package/model-provider/openai/chat/OpenAIChatModel.test.cjs +0 -61
  155. package/model-provider/openai/chat/OpenAIChatModel.test.js +0 -59
  156. package/model-provider/openai/chat/OpenAIChatPromptTemplate.cjs +0 -70
  157. package/model-provider/openai/chat/OpenAIChatPromptTemplate.js +0 -63
  158. package/model-provider/openai/chat/OpenAIChatStreamIterable.cjs +0 -156
  159. package/model-provider/openai/chat/OpenAIChatStreamIterable.d.ts +0 -19
  160. package/model-provider/openai/chat/OpenAIChatStreamIterable.js +0 -152
  161. /package/model-provider/{openai/chat/OpenAIChatModel.test.d.ts → anthropic/AnthropicTextGenerationModel.test.d.ts} +0 -0
  162. /package/model-provider/openai/{chat/OpenAIChatMessage.cjs → OpenAIChatMessage.cjs} +0 -0
  163. /package/model-provider/openai/{chat/OpenAIChatMessage.js → OpenAIChatMessage.js} +0 -0
  164. /package/model-provider/openai/{chat/countOpenAIChatMessageTokens.d.ts → countOpenAIChatMessageTokens.d.ts} +0 -0
@@ -2,15 +2,14 @@ import { z } from "zod";
2
2
  import { ApiCallError } from "../../core/api/ApiCallError.js";
3
3
  import { callWithRetryAndThrottle } from "../../core/api/callWithRetryAndThrottle.js";
4
4
  import { postJsonToApi } from "../../core/api/postToApi.js";
5
- import { ZodSchema } from "../../core/schema/ZodSchema.js";
5
+ import { ZodSchema, zodSchema } from "../../core/schema/ZodSchema.js";
6
6
  import { safeParseJSON } from "../../core/schema/parseJSON.js";
7
7
  import { AbstractModel } from "../../model-function/AbstractModel.js";
8
8
  import { PromptTemplateTextStreamingModel } from "../../model-function/generate-text/PromptTemplateTextStreamingModel.js";
9
9
  import { textGenerationModelProperties, } from "../../model-function/generate-text/TextGenerationModel.js";
10
10
  import { TextGenerationToolCallModel, } from "../../tool/generate-tool-call/TextGenerationToolCallModel.js";
11
11
  import { TextGenerationToolCallsOrGenerateTextModel, } from "../../tool/generate-tool-calls-or-text/TextGenerationToolCallsOrGenerateTextModel.js";
12
- import { AsyncQueue } from "../../util/AsyncQueue.js";
13
- import { parseJsonStream } from "../../util/streaming/parseJsonStream.js";
12
+ import { createJsonStreamResponseHandler } from "../../util/streaming/createJsonStreamResponseHandler.js";
14
13
  import { OllamaApiConfiguration } from "./OllamaApiConfiguration.js";
15
14
  import { failedOllamaCallResponseHandler } from "./OllamaError.js";
16
15
  export class OllamaCompletionModel extends AbstractModel {
@@ -132,6 +131,10 @@ export class OllamaCompletionModel extends AbstractModel {
132
131
  responseFormat: OllamaCompletionResponseFormat.deltaIterable,
133
132
  });
134
133
  }
134
+ extractTextDelta(delta) {
135
+ const chunk = delta;
136
+ return chunk.done === true ? undefined : chunk.response;
137
+ }
135
138
  asToolCallGenerationModel(promptTemplate) {
136
139
  return new TextGenerationToolCallModel({
137
140
  model: this,
@@ -147,11 +150,25 @@ export class OllamaCompletionModel extends AbstractModel {
147
150
  withTextPrompt() {
148
151
  return this.withPromptTemplate({
149
152
  format(prompt) {
150
- return { prompt: prompt };
153
+ return { prompt };
151
154
  },
152
155
  stopSequences: [],
153
156
  });
154
157
  }
158
+ /**
159
+ * Maps the prompt for a text version of the Ollama completion prompt template (without image support).
160
+ */
161
+ withTextPromptTemplate(promptTemplate) {
162
+ return new PromptTemplateTextStreamingModel({
163
+ model: this.withTextPrompt().withSettings({
164
+ stopSequences: [
165
+ ...(this.settings.stopSequences ?? []),
166
+ ...promptTemplate.stopSequences,
167
+ ],
168
+ }),
169
+ promptTemplate,
170
+ });
171
+ }
155
172
  withPromptTemplate(promptTemplate) {
156
173
  return new PromptTemplateTextStreamingModel({
157
174
  model: this.withSettings({
@@ -180,7 +197,7 @@ const ollamaCompletionResponseSchema = z.object({
180
197
  eval_duration: z.number(),
181
198
  context: z.array(z.number()).optional(),
182
199
  });
183
- const ollamaCompletionStreamSchema = new ZodSchema(z.discriminatedUnion("done", [
200
+ const ollamaCompletionStreamChunkSchema = zodSchema(z.discriminatedUnion("done", [
184
201
  z.object({
185
202
  done: z.literal(false),
186
203
  model: z.string(),
@@ -202,44 +219,6 @@ const ollamaCompletionStreamSchema = new ZodSchema(z.discriminatedUnion("done",
202
219
  context: z.array(z.number()).optional(),
203
220
  }),
204
221
  ]));
205
- async function createOllamaFullDeltaIterableQueue(stream) {
206
- const queue = new AsyncQueue();
207
- let accumulatedText = "";
208
- // process the stream asynchonously (no 'await' on purpose):
209
- parseJsonStream({
210
- stream,
211
- schema: ollamaCompletionStreamSchema,
212
- process(event) {
213
- if (event.done === true) {
214
- queue.push({
215
- type: "delta",
216
- fullDelta: {
217
- content: accumulatedText,
218
- isComplete: true,
219
- delta: "",
220
- },
221
- valueDelta: "",
222
- });
223
- }
224
- else {
225
- accumulatedText += event.response;
226
- queue.push({
227
- type: "delta",
228
- fullDelta: {
229
- content: accumulatedText,
230
- isComplete: false,
231
- delta: event.response,
232
- },
233
- valueDelta: event.response,
234
- });
235
- }
236
- },
237
- onDone() {
238
- queue.close();
239
- },
240
- });
241
- return queue;
242
- }
243
222
  export const OllamaCompletionResponseFormat = {
244
223
  /**
245
224
  * Returns the response as a JSON object.
@@ -289,6 +268,6 @@ export const OllamaCompletionResponseFormat = {
289
268
  */
290
269
  deltaIterable: {
291
270
  stream: true,
292
- handler: async ({ response }) => createOllamaFullDeltaIterableQueue(response.body),
271
+ handler: createJsonStreamResponseHandler(ollamaCompletionStreamChunkSchema),
293
272
  },
294
273
  };
@@ -1,25 +1,48 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
26
  const assert_1 = require("assert");
4
- const msw_1 = require("msw");
5
- const node_1 = require("msw/node");
27
+ const zod_1 = require("zod");
6
28
  const ApiCallError_js_1 = require("../../core/api/ApiCallError.cjs");
7
29
  const retryNever_js_1 = require("../../core/api/retryNever.cjs");
30
+ const ZodSchema_js_1 = require("../../core/schema/ZodSchema.cjs");
31
+ const jsonStructurePrompt_js_1 = require("../../model-function/generate-structure/jsonStructurePrompt.cjs");
32
+ const streamStructure_js_1 = require("../../model-function/generate-structure/streamStructure.cjs");
8
33
  const generateText_js_1 = require("../../model-function/generate-text/generateText.cjs");
34
+ const TextPrompt = __importStar(require("../../model-function/generate-text/prompt-template/TextPromptTemplate.cjs"));
35
+ const streamText_js_1 = require("../../model-function/generate-text/streamText.cjs");
36
+ const JsonTestServer_js_1 = require("../../test/JsonTestServer.cjs");
37
+ const StreamingTestServer_js_1 = require("../../test/StreamingTestServer.cjs");
38
+ const arrayFromAsync_js_1 = require("../../test/arrayFromAsync.cjs");
9
39
  const OllamaApiConfiguration_js_1 = require("./OllamaApiConfiguration.cjs");
10
40
  const OllamaCompletionModel_js_1 = require("./OllamaCompletionModel.cjs");
11
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
- let responseBodyJson = {};
13
- const server = (0, node_1.setupServer)(msw_1.http.post("http://127.0.0.1:11434/api/generate", () => msw_1.HttpResponse.json(responseBodyJson)));
14
- beforeAll(() => server.listen());
15
- beforeEach(() => {
16
- responseBodyJson = {};
17
- });
18
- afterEach(() => server.resetHandlers());
19
- afterAll(() => server.close());
20
41
  describe("generateText", () => {
42
+ const server = new JsonTestServer_js_1.JsonTestServer("http://127.0.0.1:11434/api/generate");
43
+ server.setupTestEnvironment();
21
44
  it("should return the generated text", async () => {
22
- responseBodyJson = {
45
+ server.responseBodyJson = {
23
46
  model: "test-model",
24
47
  created_at: "2023-08-04T19:22:45.499127Z",
25
48
  response: "test response",
@@ -40,7 +63,7 @@ describe("generateText", () => {
40
63
  expect(result).toEqual("test response");
41
64
  });
42
65
  it("should throw retryable ApiCallError when Ollama is overloaded", async () => {
43
- responseBodyJson = {
66
+ server.responseBodyJson = {
44
67
  model: "",
45
68
  created_at: "0001-01-01T00:00:00Z",
46
69
  response: "",
@@ -61,3 +84,68 @@ describe("generateText", () => {
61
84
  }
62
85
  });
63
86
  });
87
+ describe("streamText", () => {
88
+ const server = new StreamingTestServer_js_1.StreamingTestServer("http://127.0.0.1:11434/api/generate");
89
+ server.setupTestEnvironment();
90
+ it("should return a text stream", async () => {
91
+ server.responseChunks = [
92
+ `{"model":"mistral:text","created_at":"2023-12-24T16:11:17.715003Z","response":"Hello","done":false}\n`,
93
+ `{"model":"mistral:text","created_at":"2023-12-24T16:11:17.715003Z","response":", ","done":false}\n`,
94
+ `{"model":"mistral:text","created_at":"2023-12-24T16:11:17.715003Z","response":"world!","done":false}\n`,
95
+ `{"model":"mistral:text","created_at":"2023-12-24T16:11:19.697067Z","response":"",` +
96
+ `"done":true,"context":[123,456,789],"total_duration":2165354041,"load_duration":1293958,` +
97
+ `"prompt_eval_count":5,"prompt_eval_duration":193273000,"eval_count":136,"eval_duration":1966852000}\n`,
98
+ ];
99
+ const stream = await (0, streamText_js_1.streamText)(new OllamaCompletionModel_js_1.OllamaCompletionModel({ model: "mistral:text" }).withTextPrompt(), "hello");
100
+ // note: space moved to last chunk bc of trimming
101
+ expect(await (0, arrayFromAsync_js_1.arrayFromAsync)(stream)).toStrictEqual([
102
+ "Hello",
103
+ ",",
104
+ " world!",
105
+ ]);
106
+ });
107
+ });
108
+ describe("streamStructure", () => {
109
+ const server = new StreamingTestServer_js_1.StreamingTestServer("http://127.0.0.1:11434/api/generate");
110
+ server.setupTestEnvironment();
111
+ it("should return a text stream", async () => {
112
+ server.responseChunks = [
113
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.253175Z","response":"{","done":false}\n`,
114
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.273505Z","response":"\\n","done":false}\n`,
115
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.293192Z","response":" ","done":false}\n`,
116
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.312446Z","response":" \\"","done":false}\n`,
117
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.332021Z","response":"name","done":false}\n`,
118
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.351128Z","response":"\\":","done":false}\n`,
119
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.372082Z","response":" \\"","done":false}\n`,
120
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.391903Z","response":"M","done":false}\n`,
121
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.411056Z","response":"ike","done":false}\n`,
122
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.430789Z","response":"\\"","done":false}\n`,
123
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.450216Z","response":"\\n","done":false}\n`,
124
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.47009Z","response":"}","done":false}\n`,
125
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.48885Z","response":"","done":true,` +
126
+ `"total_duration":521893000,"load_duration":957666,"prompt_eval_count":74,"prompt_eval_duration":302508000,` +
127
+ `"eval_count":12,"eval_duration":215282000}\n`,
128
+ ];
129
+ const stream = await (0, streamStructure_js_1.streamStructure)(new OllamaCompletionModel_js_1.OllamaCompletionModel({
130
+ model: "mistral:text",
131
+ format: "json",
132
+ raw: true,
133
+ })
134
+ .withTextPromptTemplate(TextPrompt.instruction())
135
+ .asStructureGenerationModel((0, jsonStructurePrompt_js_1.jsonStructurePrompt)((instruction, schema) => ({
136
+ system: "JSON schema: \n" +
137
+ JSON.stringify(schema.getJsonSchema()) +
138
+ "\n\n" +
139
+ "Respond only using JSON that matches the above schema.",
140
+ instruction,
141
+ }))), (0, ZodSchema_js_1.zodSchema)(zod_1.z.object({ name: zod_1.z.string() })), "generate a name");
142
+ // note: space moved to last chunk bc of trimming
143
+ expect(await (0, arrayFromAsync_js_1.arrayFromAsync)(stream)).toStrictEqual([
144
+ { isComplete: false, value: {} },
145
+ { isComplete: false, value: { name: "" } },
146
+ { isComplete: false, value: { name: "M" } },
147
+ { isComplete: false, value: { name: "Mike" } },
148
+ { isComplete: true, value: { name: "Mike" } },
149
+ ]);
150
+ });
151
+ });
@@ -1,23 +1,23 @@
1
1
  import { fail } from "assert";
2
- import { HttpResponse, http } from "msw";
3
- import { setupServer } from "msw/node";
2
+ import { z } from "zod";
4
3
  import { ApiCallError } from "../../core/api/ApiCallError.js";
5
4
  import { retryNever } from "../../core/api/retryNever.js";
5
+ import { zodSchema } from "../../core/schema/ZodSchema.js";
6
+ import { jsonStructurePrompt } from "../../model-function/generate-structure/jsonStructurePrompt.js";
7
+ import { streamStructure } from "../../model-function/generate-structure/streamStructure.js";
6
8
  import { generateText } from "../../model-function/generate-text/generateText.js";
9
+ import * as TextPrompt from "../../model-function/generate-text/prompt-template/TextPromptTemplate.js";
10
+ import { streamText } from "../../model-function/generate-text/streamText.js";
11
+ import { JsonTestServer } from "../../test/JsonTestServer.js";
12
+ import { StreamingTestServer } from "../../test/StreamingTestServer.js";
13
+ import { arrayFromAsync } from "../../test/arrayFromAsync.js";
7
14
  import { OllamaApiConfiguration } from "./OllamaApiConfiguration.js";
8
15
  import { OllamaCompletionModel } from "./OllamaCompletionModel.js";
9
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
- let responseBodyJson = {};
11
- const server = setupServer(http.post("http://127.0.0.1:11434/api/generate", () => HttpResponse.json(responseBodyJson)));
12
- beforeAll(() => server.listen());
13
- beforeEach(() => {
14
- responseBodyJson = {};
15
- });
16
- afterEach(() => server.resetHandlers());
17
- afterAll(() => server.close());
18
16
  describe("generateText", () => {
17
+ const server = new JsonTestServer("http://127.0.0.1:11434/api/generate");
18
+ server.setupTestEnvironment();
19
19
  it("should return the generated text", async () => {
20
- responseBodyJson = {
20
+ server.responseBodyJson = {
21
21
  model: "test-model",
22
22
  created_at: "2023-08-04T19:22:45.499127Z",
23
23
  response: "test response",
@@ -38,7 +38,7 @@ describe("generateText", () => {
38
38
  expect(result).toEqual("test response");
39
39
  });
40
40
  it("should throw retryable ApiCallError when Ollama is overloaded", async () => {
41
- responseBodyJson = {
41
+ server.responseBodyJson = {
42
42
  model: "",
43
43
  created_at: "0001-01-01T00:00:00Z",
44
44
  response: "",
@@ -59,3 +59,68 @@ describe("generateText", () => {
59
59
  }
60
60
  });
61
61
  });
62
+ describe("streamText", () => {
63
+ const server = new StreamingTestServer("http://127.0.0.1:11434/api/generate");
64
+ server.setupTestEnvironment();
65
+ it("should return a text stream", async () => {
66
+ server.responseChunks = [
67
+ `{"model":"mistral:text","created_at":"2023-12-24T16:11:17.715003Z","response":"Hello","done":false}\n`,
68
+ `{"model":"mistral:text","created_at":"2023-12-24T16:11:17.715003Z","response":", ","done":false}\n`,
69
+ `{"model":"mistral:text","created_at":"2023-12-24T16:11:17.715003Z","response":"world!","done":false}\n`,
70
+ `{"model":"mistral:text","created_at":"2023-12-24T16:11:19.697067Z","response":"",` +
71
+ `"done":true,"context":[123,456,789],"total_duration":2165354041,"load_duration":1293958,` +
72
+ `"prompt_eval_count":5,"prompt_eval_duration":193273000,"eval_count":136,"eval_duration":1966852000}\n`,
73
+ ];
74
+ const stream = await streamText(new OllamaCompletionModel({ model: "mistral:text" }).withTextPrompt(), "hello");
75
+ // note: space moved to last chunk bc of trimming
76
+ expect(await arrayFromAsync(stream)).toStrictEqual([
77
+ "Hello",
78
+ ",",
79
+ " world!",
80
+ ]);
81
+ });
82
+ });
83
+ describe("streamStructure", () => {
84
+ const server = new StreamingTestServer("http://127.0.0.1:11434/api/generate");
85
+ server.setupTestEnvironment();
86
+ it("should return a text stream", async () => {
87
+ server.responseChunks = [
88
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.253175Z","response":"{","done":false}\n`,
89
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.273505Z","response":"\\n","done":false}\n`,
90
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.293192Z","response":" ","done":false}\n`,
91
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.312446Z","response":" \\"","done":false}\n`,
92
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.332021Z","response":"name","done":false}\n`,
93
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.351128Z","response":"\\":","done":false}\n`,
94
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.372082Z","response":" \\"","done":false}\n`,
95
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.391903Z","response":"M","done":false}\n`,
96
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.411056Z","response":"ike","done":false}\n`,
97
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.430789Z","response":"\\"","done":false}\n`,
98
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.450216Z","response":"\\n","done":false}\n`,
99
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.47009Z","response":"}","done":false}\n`,
100
+ `{"model":"mistral:text","created_at":"2023-12-25T11:48:02.48885Z","response":"","done":true,` +
101
+ `"total_duration":521893000,"load_duration":957666,"prompt_eval_count":74,"prompt_eval_duration":302508000,` +
102
+ `"eval_count":12,"eval_duration":215282000}\n`,
103
+ ];
104
+ const stream = await streamStructure(new OllamaCompletionModel({
105
+ model: "mistral:text",
106
+ format: "json",
107
+ raw: true,
108
+ })
109
+ .withTextPromptTemplate(TextPrompt.instruction())
110
+ .asStructureGenerationModel(jsonStructurePrompt((instruction, schema) => ({
111
+ system: "JSON schema: \n" +
112
+ JSON.stringify(schema.getJsonSchema()) +
113
+ "\n\n" +
114
+ "Respond only using JSON that matches the above schema.",
115
+ instruction,
116
+ }))), zodSchema(z.object({ name: z.string() })), "generate a name");
117
+ // note: space moved to last chunk bc of trimming
118
+ expect(await arrayFromAsync(stream)).toStrictEqual([
119
+ { isComplete: false, value: {} },
120
+ { isComplete: false, value: { name: "" } },
121
+ { isComplete: false, value: { name: "M" } },
122
+ { isComplete: false, value: { name: "Mike" } },
123
+ { isComplete: true, value: { name: "Mike" } },
124
+ ]);
125
+ });
126
+ });
@@ -2,14 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OpenAIChatResponseFormat = exports.AbstractOpenAIChatModel = void 0;
4
4
  const zod_1 = require("zod");
5
- const callWithRetryAndThrottle_js_1 = require("../../../core/api/callWithRetryAndThrottle.cjs");
6
- const postToApi_js_1 = require("../../../core/api/postToApi.cjs");
7
- const parseJSON_js_1 = require("../../../core/schema/parseJSON.cjs");
8
- const AbstractModel_js_1 = require("../../../model-function/AbstractModel.cjs");
9
- const parsePartialJson_js_1 = require("../../../model-function/generate-structure/parsePartialJson.cjs");
10
- const OpenAIApiConfiguration_js_1 = require("../OpenAIApiConfiguration.cjs");
11
- const OpenAIError_js_1 = require("../OpenAIError.cjs");
12
- const OpenAIChatStreamIterable_js_1 = require("./OpenAIChatStreamIterable.cjs");
5
+ const callWithRetryAndThrottle_js_1 = require("../../core/api/callWithRetryAndThrottle.cjs");
6
+ const postToApi_js_1 = require("../../core/api/postToApi.cjs");
7
+ const ZodSchema_js_1 = require("../../core/schema/ZodSchema.cjs");
8
+ const parseJSON_js_1 = require("../../core/schema/parseJSON.cjs");
9
+ const AbstractModel_js_1 = require("../../model-function/AbstractModel.cjs");
10
+ const createEventSourceResponseHandler_js_1 = require("../../util/streaming/createEventSourceResponseHandler.cjs");
11
+ const OpenAIApiConfiguration_js_1 = require("./OpenAIApiConfiguration.cjs");
12
+ const OpenAIError_js_1 = require("./OpenAIError.cjs");
13
13
  /**
14
14
  * Abstract text generation model that calls an API that is compatible with the OpenAI chat API.
15
15
  *
@@ -105,9 +105,21 @@ class AbstractOpenAIChatModel extends AbstractModel_js_1.AbstractModel {
105
105
  doStreamText(prompt, options) {
106
106
  return this.callAPI(prompt, {
107
107
  ...options,
108
- responseFormat: exports.OpenAIChatResponseFormat.textDeltaIterable,
108
+ responseFormat: exports.OpenAIChatResponseFormat.deltaIterable,
109
109
  });
110
110
  }
111
+ extractTextDelta(delta) {
112
+ const chunk = delta;
113
+ if (chunk.object !== "chat.completion.chunk") {
114
+ return undefined;
115
+ }
116
+ const chatChunk = chunk;
117
+ const firstChoice = chatChunk.choices[0];
118
+ if (firstChoice.index > 0) {
119
+ return undefined;
120
+ }
121
+ return firstChoice.delta.content ?? undefined;
122
+ }
111
123
  async doGenerateToolCall(tool, prompt, options) {
112
124
  const response = await this.callAPI(prompt, {
113
125
  ...options,
@@ -220,6 +232,54 @@ const openAIChatResponseSchema = zod_1.z.object({
220
232
  total_tokens: zod_1.z.number(),
221
233
  }),
222
234
  });
235
+ const chatCompletionChunkSchema = zod_1.z.object({
236
+ object: zod_1.z.literal("chat.completion.chunk"),
237
+ id: zod_1.z.string(),
238
+ choices: zod_1.z.array(zod_1.z.object({
239
+ delta: zod_1.z.object({
240
+ role: zod_1.z.enum(["assistant", "user"]).optional(),
241
+ content: zod_1.z.string().nullable().optional(),
242
+ function_call: zod_1.z
243
+ .object({
244
+ name: zod_1.z.string().optional(),
245
+ arguments: zod_1.z.string().optional(),
246
+ })
247
+ .optional(),
248
+ tool_calls: zod_1.z
249
+ .array(zod_1.z.object({
250
+ id: zod_1.z.string(),
251
+ type: zod_1.z.literal("function"),
252
+ function: zod_1.z.object({
253
+ name: zod_1.z.string(),
254
+ arguments: zod_1.z.string(),
255
+ }),
256
+ }))
257
+ .optional(),
258
+ }),
259
+ finish_reason: zod_1.z
260
+ .enum([
261
+ "stop",
262
+ "length",
263
+ "tool_calls",
264
+ "content_filter",
265
+ "function_call",
266
+ ])
267
+ .nullable()
268
+ .optional(),
269
+ index: zod_1.z.number(),
270
+ })),
271
+ created: zod_1.z.number(),
272
+ model: zod_1.z.string(),
273
+ system_fingerprint: zod_1.z.string().optional().nullable(),
274
+ });
275
+ const openaiChatChunkSchema = (0, ZodSchema_js_1.zodSchema)(zod_1.z.union([
276
+ chatCompletionChunkSchema,
277
+ zod_1.z.object({
278
+ object: zod_1.z.string().refine((obj) => obj !== "chat.completion.chunk", {
279
+ message: "Object must be 'chat.completion.chunk'",
280
+ }),
281
+ }),
282
+ ]));
223
283
  exports.OpenAIChatResponseFormat = {
224
284
  /**
225
285
  * Returns the response as a JSON object.
@@ -231,12 +291,8 @@ exports.OpenAIChatResponseFormat = {
231
291
  /**
232
292
  * Returns an async iterable over the text deltas (only the tex different of the first choice).
233
293
  */
234
- textDeltaIterable: {
235
- stream: true,
236
- handler: async ({ response }) => (0, OpenAIChatStreamIterable_js_1.createOpenAIChatDeltaIterableQueue)(response.body, (delta) => delta[0]?.delta?.content ?? ""),
237
- },
238
- structureDeltaIterable: {
294
+ deltaIterable: {
239
295
  stream: true,
240
- handler: async ({ response }) => (0, OpenAIChatStreamIterable_js_1.createOpenAIChatDeltaIterableQueue)(response.body, (delta) => (0, parsePartialJson_js_1.parsePartialJson)(delta[0]?.function_call?.arguments)),
296
+ handler: (0, createEventSourceResponseHandler_js_1.createEventSourceResponseHandler)(openaiChatChunkSchema),
241
297
  },
242
298
  };