@agentscope-ai/agentscope 0.0.2

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 (136) hide show
  1. package/dist/agent/index.d.mts +234 -0
  2. package/dist/agent/index.d.ts +234 -0
  3. package/dist/agent/index.js +1412 -0
  4. package/dist/agent/index.js.map +1 -0
  5. package/dist/agent/index.mjs +1375 -0
  6. package/dist/agent/index.mjs.map +1 -0
  7. package/dist/base-BOx3UzOl.d.mts +41 -0
  8. package/dist/base-BoIps2RL.d.ts +41 -0
  9. package/dist/base-C7jwyH4Z.d.mts +52 -0
  10. package/dist/base-Cwi4bjze.d.ts +127 -0
  11. package/dist/base-DYlBMCy_.d.mts +127 -0
  12. package/dist/base-NX-knWOv.d.ts +52 -0
  13. package/dist/block-VsnHrllL.d.mts +48 -0
  14. package/dist/block-VsnHrllL.d.ts +48 -0
  15. package/dist/event/index.d.mts +181 -0
  16. package/dist/event/index.d.ts +181 -0
  17. package/dist/event/index.js +58 -0
  18. package/dist/event/index.js.map +1 -0
  19. package/dist/event/index.mjs +33 -0
  20. package/dist/event/index.mjs.map +1 -0
  21. package/dist/formatter/index.d.mts +187 -0
  22. package/dist/formatter/index.d.ts +187 -0
  23. package/dist/formatter/index.js +647 -0
  24. package/dist/formatter/index.js.map +1 -0
  25. package/dist/formatter/index.mjs +616 -0
  26. package/dist/formatter/index.mjs.map +1 -0
  27. package/dist/index-BTJDlKvQ.d.mts +195 -0
  28. package/dist/index-BcatlwXQ.d.ts +195 -0
  29. package/dist/index-CAxQAkiP.d.mts +21 -0
  30. package/dist/index-CAxQAkiP.d.ts +21 -0
  31. package/dist/mcp/index.d.mts +9 -0
  32. package/dist/mcp/index.d.ts +9 -0
  33. package/dist/mcp/index.js +432 -0
  34. package/dist/mcp/index.js.map +1 -0
  35. package/dist/mcp/index.mjs +408 -0
  36. package/dist/mcp/index.mjs.map +1 -0
  37. package/dist/message/index.d.mts +10 -0
  38. package/dist/message/index.d.ts +10 -0
  39. package/dist/message/index.js +67 -0
  40. package/dist/message/index.js.map +1 -0
  41. package/dist/message/index.mjs +37 -0
  42. package/dist/message/index.mjs.map +1 -0
  43. package/dist/message-CkN21KaY.d.mts +99 -0
  44. package/dist/message-CzLeTlua.d.ts +99 -0
  45. package/dist/model/index.d.mts +377 -0
  46. package/dist/model/index.d.ts +377 -0
  47. package/dist/model/index.js +1880 -0
  48. package/dist/model/index.js.map +1 -0
  49. package/dist/model/index.mjs +1849 -0
  50. package/dist/model/index.mjs.map +1 -0
  51. package/dist/storage/index.d.mts +68 -0
  52. package/dist/storage/index.d.ts +68 -0
  53. package/dist/storage/index.js +250 -0
  54. package/dist/storage/index.js.map +1 -0
  55. package/dist/storage/index.mjs +212 -0
  56. package/dist/storage/index.mjs.map +1 -0
  57. package/dist/tool/index.d.mts +311 -0
  58. package/dist/tool/index.d.ts +311 -0
  59. package/dist/tool/index.js +1494 -0
  60. package/dist/tool/index.js.map +1 -0
  61. package/dist/tool/index.mjs +1447 -0
  62. package/dist/tool/index.mjs.map +1 -0
  63. package/dist/toolkit-CEpulFi0.d.ts +99 -0
  64. package/dist/toolkit-CGEZSZPa.d.mts +99 -0
  65. package/jest.config.js +11 -0
  66. package/package.json +92 -0
  67. package/src/_utils/common.ts +104 -0
  68. package/src/_utils/index.ts +1 -0
  69. package/src/agent/agent-base.ts +0 -0
  70. package/src/agent/agent.test.ts +1028 -0
  71. package/src/agent/agent.ts +1032 -0
  72. package/src/agent/index.ts +2 -0
  73. package/src/agent/interfaces.ts +23 -0
  74. package/src/agent/test-compression.ts +72 -0
  75. package/src/event/index.ts +250 -0
  76. package/src/formatter/base.ts +133 -0
  77. package/src/formatter/dashscope-chat-formatter.test.ts +372 -0
  78. package/src/formatter/dashscope-chat-formatter.ts +163 -0
  79. package/src/formatter/deepseek-chat-formatter.ts +130 -0
  80. package/src/formatter/index.ts +5 -0
  81. package/src/formatter/ollama-chat-formatter.ts +67 -0
  82. package/src/formatter/openai-chat-formatter.test.ts +263 -0
  83. package/src/formatter/openai-chat-formatter.ts +301 -0
  84. package/src/formatter/openai.md +767 -0
  85. package/src/mcp/base.ts +114 -0
  86. package/src/mcp/http.test.ts +303 -0
  87. package/src/mcp/http.ts +224 -0
  88. package/src/mcp/index.ts +2 -0
  89. package/src/mcp/stdio.test.ts +91 -0
  90. package/src/mcp/stdio.ts +119 -0
  91. package/src/message/block.ts +60 -0
  92. package/src/message/enums.ts +4 -0
  93. package/src/message/index.ts +12 -0
  94. package/src/message/message.test.ts +80 -0
  95. package/src/message/message.ts +131 -0
  96. package/src/model/base.ts +226 -0
  97. package/src/model/dashscope-model.test.ts +335 -0
  98. package/src/model/dashscope-model.ts +441 -0
  99. package/src/model/deepseek-model.test.ts +279 -0
  100. package/src/model/deepseek-model.ts +401 -0
  101. package/src/model/index.ts +7 -0
  102. package/src/model/ollama-model.test.ts +307 -0
  103. package/src/model/ollama-model.ts +356 -0
  104. package/src/model/openai-model.ts +327 -0
  105. package/src/model/response.ts +22 -0
  106. package/src/model/usage.ts +12 -0
  107. package/src/storage/base.ts +52 -0
  108. package/src/storage/file-system.test.ts +587 -0
  109. package/src/storage/file-system.ts +269 -0
  110. package/src/storage/index.ts +2 -0
  111. package/src/tool/base.ts +23 -0
  112. package/src/tool/bash.test.ts +174 -0
  113. package/src/tool/bash.ts +152 -0
  114. package/src/tool/edit.test.ts +83 -0
  115. package/src/tool/edit.ts +95 -0
  116. package/src/tool/glob.test.ts +63 -0
  117. package/src/tool/glob.ts +166 -0
  118. package/src/tool/grep.test.ts +74 -0
  119. package/src/tool/grep.ts +256 -0
  120. package/src/tool/index.ts +10 -0
  121. package/src/tool/read.test.ts +77 -0
  122. package/src/tool/read.ts +117 -0
  123. package/src/tool/response.ts +82 -0
  124. package/src/tool/task.test.ts +299 -0
  125. package/src/tool/task.ts +399 -0
  126. package/src/tool/toolkit.test.ts +636 -0
  127. package/src/tool/toolkit.ts +601 -0
  128. package/src/tool/write.test.ts +52 -0
  129. package/src/tool/write.ts +57 -0
  130. package/src/type/index.ts +52 -0
  131. package/tsconfig.build.json +4 -0
  132. package/tsconfig.cjs.json +11 -0
  133. package/tsconfig.esm.json +10 -0
  134. package/tsconfig.json +14 -0
  135. package/tsup.config.ts +20 -0
  136. package/typedoc.json +52 -0
@@ -0,0 +1,1880 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/model/index.ts
21
+ var model_exports = {};
22
+ __export(model_exports, {
23
+ ChatModelBase: () => ChatModelBase,
24
+ DashScopeChatModel: () => DashScopeChatModel,
25
+ DeepSeekChatModel: () => DeepSeekChatModel,
26
+ OllamaChatModel: () => OllamaChatModel,
27
+ OpenAIChatModel: () => OpenAIChatModel
28
+ });
29
+ module.exports = __toCommonJS(model_exports);
30
+
31
+ // src/message/message.ts
32
+ function createMsg({
33
+ name,
34
+ content,
35
+ role,
36
+ metadata = {},
37
+ id = crypto.randomUUID(),
38
+ timestamp = (/* @__PURE__ */ new Date()).toISOString(),
39
+ usage
40
+ }) {
41
+ return { id, name, role, content, metadata, timestamp, usage };
42
+ }
43
+ function getTextContent(msg, separator = "\n") {
44
+ const textBlocks = msg.content.filter((block) => block.type === "text");
45
+ if (textBlocks.length === 0) {
46
+ return null;
47
+ }
48
+ return textBlocks.map((block) => block.text).join(separator);
49
+ }
50
+ function getContentBlocks(msg, blockType) {
51
+ if (!blockType) return msg.content;
52
+ return msg.content.filter((block) => block.type === blockType);
53
+ }
54
+
55
+ // src/model/base.ts
56
+ var ChatModelBase = class {
57
+ modelName;
58
+ stream;
59
+ maxRetries;
60
+ fallbackModelName;
61
+ formatter;
62
+ /**
63
+ * Initializes a new instance of the ChatModelBase class.
64
+ *
65
+ * @param options - The chat model options, including model name, streaming option, max retries, fallback
66
+ * model name, and formatter.
67
+ *
68
+ * @param options.modelName
69
+ * @param options.stream
70
+ * @param options.maxRetries
71
+ * @param options.fallbackModelName
72
+ * @param options.formatter
73
+ */
74
+ constructor({
75
+ modelName,
76
+ stream,
77
+ maxRetries,
78
+ fallbackModelName,
79
+ formatter
80
+ }) {
81
+ this.modelName = modelName;
82
+ this.stream = stream ?? true;
83
+ this.maxRetries = maxRetries ?? 0;
84
+ this.fallbackModelName = fallbackModelName;
85
+ this.formatter = formatter;
86
+ }
87
+ /**
88
+ * Calls the chat model with the given messages.
89
+ * This is the main method to interact with the model.
90
+ *
91
+ * @param options - The chat model call options.
92
+ * @returns A promise that resolves to the model's response.
93
+ */
94
+ async call(options) {
95
+ let formattedMessages;
96
+ if (this.formatter) {
97
+ formattedMessages = await this.formatter.format({ msgs: options.messages });
98
+ } else {
99
+ formattedMessages = options.messages;
100
+ }
101
+ const requestOptions = {
102
+ ...options,
103
+ messages: formattedMessages
104
+ };
105
+ let lastError;
106
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
107
+ try {
108
+ return await this._callAPI(this.modelName, requestOptions);
109
+ } catch (error) {
110
+ lastError = error;
111
+ if (attempt === this.maxRetries) {
112
+ throw error;
113
+ } else {
114
+ console.log(
115
+ `Attempt ${attempt + 1} failed for model ${this.modelName}. Retrying...`
116
+ );
117
+ }
118
+ }
119
+ }
120
+ if (this.fallbackModelName) {
121
+ console.log(
122
+ `Using fallback model ${this.fallbackModelName} after ${this.maxRetries} failed attempts.`
123
+ );
124
+ return await this._callAPI(this.fallbackModelName, requestOptions);
125
+ }
126
+ throw lastError;
127
+ }
128
+ /**
129
+ * A heuristic method to count the number of the tokens
130
+ * Note the multimodal content is ignored in the token counting
131
+ * @param options
132
+ * @param options.messages
133
+ * @param options.tools
134
+ * @returns The estimated number of tokens in the input messages and tools.
135
+ */
136
+ async countTokens(options) {
137
+ let accText = "";
138
+ for (const msg of options.messages) {
139
+ accText += getTextContent(msg) || "";
140
+ }
141
+ if (options.tools) {
142
+ accText += JSON.stringify(options.tools);
143
+ }
144
+ const chineseMatches = accText.match(/[\u4e00-\u9fff\u3400-\u4dbf\u{20000}-\u{2a6df}]/gu)?.length ?? 0;
145
+ const englishMatches = accText.match(/[a-zA-Z]+/g)?.length ?? 0;
146
+ return chineseMatches * 2 + englishMatches * 1.5;
147
+ }
148
+ /**
149
+ * A default implementation of the structured call method. For those supporting structured output, the model should
150
+ * override this method.
151
+ * @param options
152
+ * @returns The structured response from the model, which should conform to the provided Zod schema.
153
+ */
154
+ async callStructured(options) {
155
+ const toolSchema = {
156
+ type: "function",
157
+ function: {
158
+ name: "GenerateStructuredResponse",
159
+ description: "Generate required structured response by this toll.",
160
+ parameters: options.schema.toJSONSchema({
161
+ target: "openapi-3.0"
162
+ })
163
+ }
164
+ };
165
+ const res = await this.call({
166
+ messages: options.messages,
167
+ tools: [toolSchema],
168
+ toolChoice: "GenerateStructuredResponse"
169
+ });
170
+ let completedResponse;
171
+ if (this.stream) {
172
+ while (true) {
173
+ const { value, done } = await res.next();
174
+ if (done) {
175
+ completedResponse = value;
176
+ break;
177
+ }
178
+ }
179
+ } else {
180
+ completedResponse = res;
181
+ }
182
+ for (const block of completedResponse.content) {
183
+ if (block.type === "tool_call" && block.name === "GenerateStructuredResponse") {
184
+ const structuredContent = JSON.parse(block.input);
185
+ return {
186
+ ...completedResponse,
187
+ content: structuredContent,
188
+ type: "structured"
189
+ };
190
+ }
191
+ }
192
+ throw new Error(`Failed to generate the structured response`);
193
+ }
194
+ };
195
+
196
+ // src/_utils/common.ts
197
+ var import_jsonrepair = require("jsonrepair");
198
+ async function* _parseStreamedResponse(response) {
199
+ const reader = response.body?.getReader();
200
+ if (!reader) {
201
+ throw new Error("Failed to get reader from response body for streaming.");
202
+ }
203
+ const decoder = new TextDecoder();
204
+ let buffer = "";
205
+ try {
206
+ while (true) {
207
+ const { done, value } = await reader.read();
208
+ if (done) break;
209
+ buffer += decoder.decode(value, { stream: true });
210
+ const lines = buffer.split("\n");
211
+ buffer = lines.pop() || "";
212
+ for (const line of lines) {
213
+ const trimmedLine = line.trim();
214
+ if (!trimmedLine || trimmedLine.startsWith(":")) {
215
+ continue;
216
+ }
217
+ if (trimmedLine.startsWith("data:")) {
218
+ const jsonStr = trimmedLine.slice(5).trim();
219
+ if (jsonStr === "[DONE]") {
220
+ break;
221
+ }
222
+ try {
223
+ const json = JSON.parse(jsonStr);
224
+ yield json;
225
+ } catch (e) {
226
+ console.error("Failed to parse JSON:", e);
227
+ throw new Error(`Failed to parse JSON from stream: ${jsonStr}`);
228
+ }
229
+ }
230
+ }
231
+ }
232
+ } finally {
233
+ reader.releaseLock();
234
+ }
235
+ }
236
+
237
+ // src/formatter/base.ts
238
+ var FormatterBase = class {
239
+ /**
240
+ * Convert the tool output to string format for the LLM APIs that only accept text input. If
241
+ * `promoteMultimodalToolResult` is true, the multimodal content will be promoted to be a user message with
242
+ * "<system-info></system-info>" tags. Otherwise, the multimodal content will be saved to a storage and a URL link
243
+ * will be provided in the text output.
244
+ *
245
+ * @param output - The tool output, which can be a string or an array of content blocks.
246
+ * @param promoteMultimodalToolResult - Whether to promote the multimodal content to the prompt messages.
247
+ * @returns An object containing the text output and an optional promoted message.
248
+ */
249
+ convertToolOutputToString(output, promoteMultimodalToolResult) {
250
+ if (typeof output === "string") return { text: output, promotedMsg: null };
251
+ let textualOutput = [];
252
+ const promotedData = [];
253
+ for (const block of output) {
254
+ switch (block.type) {
255
+ case "text":
256
+ textualOutput.push(block.text);
257
+ break;
258
+ default:
259
+ const type = block.source.mediaType.split("/")[0];
260
+ if (type !== "image" && type !== "audio" && type !== "video") {
261
+ console.log(
262
+ `Unsupported media type '${block.source.mediaType}' in tool output. Only image, audio and video are supported.`
263
+ );
264
+ break;
265
+ }
266
+ if (block.source.type === "url") {
267
+ textualOutput.push(
268
+ `<system-info>One returned ${type} can be found at: ${block.source.url}</system-info>`
269
+ );
270
+ } else {
271
+ const shouldPromote = promoteMultimodalToolResult === true || typeof promoteMultimodalToolResult === "object" && promoteMultimodalToolResult[type];
272
+ if (shouldPromote) {
273
+ const dataID = Math.random().toString(36).substring(2, 10);
274
+ textualOutput.push(
275
+ `<system-info>One returned ${type} is embedded with ID '${dataID}' and will be attached within '<system-info></system-info>' tags later.</system-info>`
276
+ );
277
+ promotedData.push({ id: dataID, block });
278
+ } else {
279
+ textualOutput.push(`The returned ${block.type} is stored locally.`);
280
+ }
281
+ }
282
+ }
283
+ }
284
+ const promotedBlocks = [];
285
+ promotedData.forEach(({ id, block }) => {
286
+ const type = block.source.mediaType.split("/")[0];
287
+ promotedBlocks.push({
288
+ id: crypto.randomUUID(),
289
+ type: "text",
290
+ text: `<${type}_data id='${id}'>`
291
+ });
292
+ promotedBlocks.push(block);
293
+ promotedBlocks.push({
294
+ id: crypto.randomUUID(),
295
+ type: "text",
296
+ text: `</${type}_data>
297
+ `
298
+ });
299
+ });
300
+ if (promotedBlocks.length > 0) {
301
+ const prefix = "<system-info>The multimodal contents returned from the tool call are as follows:\n";
302
+ if (promotedBlocks[0].type === "text") {
303
+ promotedBlocks[0].text = `${prefix}${promotedBlocks[0].text}`;
304
+ } else {
305
+ promotedBlocks.unshift({
306
+ id: crypto.randomUUID(),
307
+ type: "text",
308
+ text: `${prefix}`
309
+ });
310
+ }
311
+ const lastBlock = promotedBlocks[promotedBlocks.length - 1];
312
+ if (lastBlock.type === "text") {
313
+ promotedBlocks[promotedBlocks.length - 1] = {
314
+ id: crypto.randomUUID(),
315
+ type: "text",
316
+ text: `${lastBlock.text}</system-info>`
317
+ };
318
+ } else {
319
+ promotedBlocks.push({
320
+ id: crypto.randomUUID(),
321
+ type: "text",
322
+ text: `</system-info>`
323
+ });
324
+ }
325
+ }
326
+ return {
327
+ text: textualOutput.join("\n"),
328
+ promotedMsg: createMsg({ name: "user", content: promotedBlocks, role: "user" })
329
+ };
330
+ }
331
+ };
332
+
333
+ // src/formatter/dashscope-chat-formatter.ts
334
+ var DashScopeChatFormatter = class extends FormatterBase {
335
+ promoteMultimodalToolResult;
336
+ /**
337
+ * Initialize a DashScopeChatFormatter instance.
338
+ *
339
+ * @param promoteMultimodalToolResult - Since DashScope API doesn't support multimodal tool outputs, this option
340
+ * indicates whether to promote the multimodal tool results to the prompt messages, so that LLMs can see them.
341
+ * Note you should ensure your model supports the corresponding modalities.
342
+ * @param promoteMultimodalToolResult.promoteMultimodalToolResult
343
+ */
344
+ constructor({ promoteMultimodalToolResult = false } = {}) {
345
+ super();
346
+ this.promoteMultimodalToolResult = promoteMultimodalToolResult;
347
+ }
348
+ /**
349
+ * Format the input message objects into the required format by DashScope API.
350
+ *
351
+ * @param msgs - An array of Msg instances to be formatted.
352
+ * @param msgs.msgs
353
+ * @returns A promise that resolves to an array of formatted message objects.
354
+ */
355
+ async format({ msgs }) {
356
+ const formattedMsgs = [];
357
+ let index = 0;
358
+ while (index < msgs.length) {
359
+ const msg = msgs[index];
360
+ const formattedMsg = {
361
+ role: msg.role,
362
+ content: []
363
+ };
364
+ const cachedMsgs = [];
365
+ for (const block of getContentBlocks(msg)) {
366
+ switch (block.type) {
367
+ case "text":
368
+ formattedMsg.content.push(this._formatTextBlock(block));
369
+ break;
370
+ case "thinking":
371
+ break;
372
+ case "tool_call":
373
+ if (!formattedMsg.tool_calls) {
374
+ formattedMsg.tool_calls = [];
375
+ }
376
+ formattedMsg.tool_calls.push({
377
+ id: block.id,
378
+ type: "function",
379
+ function: {
380
+ name: block.name,
381
+ arguments: block.input
382
+ }
383
+ });
384
+ break;
385
+ case "tool_result":
386
+ const formattedToolResult = this.convertToolOutputToString(
387
+ block.output,
388
+ this.promoteMultimodalToolResult
389
+ );
390
+ cachedMsgs.push({
391
+ role: "tool",
392
+ tool_call_id: block.id,
393
+ name: block.name,
394
+ content: formattedToolResult.text
395
+ });
396
+ if (formattedToolResult.promotedMsg) {
397
+ msgs.splice(index + 1, 0, formattedToolResult.promotedMsg);
398
+ }
399
+ break;
400
+ case "data":
401
+ formattedMsg.content.push(...this._formatMultimodalBlock(block));
402
+ break;
403
+ }
404
+ }
405
+ if (formattedMsg.content.length > 0 || formattedMsg.tool_calls) {
406
+ formattedMsgs.push(formattedMsg);
407
+ }
408
+ if (cachedMsgs.length > 0) {
409
+ formattedMsgs.push(...cachedMsgs);
410
+ }
411
+ index++;
412
+ }
413
+ return formattedMsgs;
414
+ }
415
+ /**
416
+ * Format a text content block into the required format.
417
+ *
418
+ * @param block - The text content block to format.
419
+ * @returns An object representing the formatted text content.
420
+ */
421
+ _formatTextBlock(block) {
422
+ return { text: block.text };
423
+ }
424
+ /**
425
+ * Format a multimodal data block into the required format.
426
+ * In DashScope API, the local file paths should be prefixed with "file://". URLs are kept unchanged.
427
+ *
428
+ * @param block - The multimodal content block to format.
429
+ * @returns An object representing the formatted multimodal content.
430
+ */
431
+ _formatMultimodalBlock(block) {
432
+ const type = block.source.mediaType.split("/")[0];
433
+ if (!["image", "audio", "video"].includes(type)) {
434
+ console.log(
435
+ `Skip unsupported media type ${block.source.mediaType} in DashScopeChatFormatter. Only image, audio and video are supported.`
436
+ );
437
+ return [];
438
+ }
439
+ if (block.source.type === "url") {
440
+ return [{ [type]: block.source.url }];
441
+ }
442
+ return [
443
+ {
444
+ [type]: `data:${block.source.mediaType};base64,${block.source.data}`
445
+ }
446
+ ];
447
+ }
448
+ };
449
+
450
+ // src/formatter/deepseek-chat-formatter.ts
451
+ var DeepSeekChatFormatter = class extends FormatterBase {
452
+ promoteMultimodalToolResult;
453
+ /**
454
+ * Initializes a new instance of the DeepSeekChatFormatter class.
455
+ * @param root0
456
+ * @param root0.promoteMultimodalToolResult
457
+ */
458
+ constructor({ promoteMultimodalToolResult = false } = {}) {
459
+ super();
460
+ this.promoteMultimodalToolResult = promoteMultimodalToolResult;
461
+ }
462
+ /**
463
+ * Format the input messages into the structure expected by DeepSeek Chat Completions API.
464
+ * @param root0
465
+ * @param root0.msgs
466
+ * @returns An array of formatted message objects ready to be sent to the DeepSeek API.
467
+ */
468
+ async format({ msgs }) {
469
+ const formattedMsgs = [];
470
+ let index = 0;
471
+ while (index < msgs.length) {
472
+ const msg = msgs[index];
473
+ const formattedMsg = {
474
+ role: msg.role,
475
+ name: msg.name,
476
+ content: null
477
+ };
478
+ const content = [];
479
+ const cachedMsgs = [];
480
+ for (const block of getContentBlocks(msg)) {
481
+ switch (block.type) {
482
+ case "text":
483
+ content.push({
484
+ type: "text",
485
+ text: block.text
486
+ });
487
+ break;
488
+ case "thinking":
489
+ break;
490
+ case "tool_call":
491
+ if (!formattedMsg.tool_calls) {
492
+ formattedMsg.tool_calls = [];
493
+ }
494
+ formattedMsg.tool_calls.push({
495
+ id: block.id,
496
+ type: "function",
497
+ function: {
498
+ name: block.name,
499
+ arguments: block.input
500
+ }
501
+ });
502
+ break;
503
+ case "tool_result":
504
+ const formattedToolResult = this.convertToolOutputToString(
505
+ block.output,
506
+ this.promoteMultimodalToolResult
507
+ );
508
+ cachedMsgs.push({
509
+ role: "tool",
510
+ tool_call_id: block.id,
511
+ name: block.name,
512
+ content: formattedToolResult.text
513
+ });
514
+ if (formattedToolResult.promotedMsg?.content.length) {
515
+ msgs.splice(index + 1, 0, formattedToolResult.promotedMsg);
516
+ }
517
+ break;
518
+ case "data":
519
+ console.warn(
520
+ `DeepSeek models don't support multimodal data for now (2026-03), skip the data block in message content.`
521
+ );
522
+ break;
523
+ }
524
+ }
525
+ if (content.length > 0) {
526
+ formattedMsg.content = content;
527
+ }
528
+ if (formattedMsg.content || formattedMsg.tool_calls) {
529
+ formattedMsgs.push(formattedMsg);
530
+ }
531
+ if (cachedMsgs.length > 0) {
532
+ formattedMsgs.push(...cachedMsgs);
533
+ }
534
+ index++;
535
+ }
536
+ return formattedMsgs;
537
+ }
538
+ };
539
+
540
+ // src/formatter/ollama-chat-formatter.ts
541
+ var OllamaChatFormatter = class extends FormatterBase {
542
+ // eslint-disable-next-line jsdoc/require-returns
543
+ /**
544
+ * Format messages for Ollama API
545
+ * @param root0
546
+ * @param root0.msgs
547
+ */
548
+ async format({ msgs }) {
549
+ const formattedMsgs = [];
550
+ for (const msg of msgs) {
551
+ const formattedMsg = {
552
+ role: msg.role,
553
+ content: ""
554
+ };
555
+ const textContent = getTextContent(msg);
556
+ if (textContent) {
557
+ formattedMsg.content = textContent;
558
+ }
559
+ const toolCalls = getContentBlocks(msg, "tool_call");
560
+ if (toolCalls.length > 0) {
561
+ formattedMsg.tool_calls = toolCalls.map((toolCall) => ({
562
+ function: {
563
+ name: toolCall.name,
564
+ arguments: JSON.parse(toolCall.input)
565
+ }
566
+ }));
567
+ }
568
+ const toolResults = getContentBlocks(msg, "tool_result");
569
+ for (const toolResult of toolResults) {
570
+ const resultText = this.convertToolOutputToString(toolResult.output, false);
571
+ formattedMsgs.push({
572
+ role: "tool",
573
+ content: resultText.text
574
+ });
575
+ }
576
+ if (formattedMsg.content || formattedMsg.tool_calls) {
577
+ formattedMsgs.push(formattedMsg);
578
+ }
579
+ }
580
+ return formattedMsgs;
581
+ }
582
+ };
583
+
584
+ // src/formatter/openai-chat-formatter.ts
585
+ var import_fs = require("fs");
586
+ var import_promises = require("fs/promises");
587
+ var import_path = require("path");
588
+ var import_url = require("url");
589
+ var OpenAIChatFormatter = class extends FormatterBase {
590
+ promoteMultimodalToolResult;
591
+ /**
592
+ * Initializes a new instance of the OpenAIChatFormatter class.
593
+ * @param root0
594
+ * @param root0.promoteMultimodalToolResult
595
+ */
596
+ constructor({ promoteMultimodalToolResult = false } = {}) {
597
+ super();
598
+ this.promoteMultimodalToolResult = promoteMultimodalToolResult;
599
+ }
600
+ /**
601
+ * Format the input messages into OpenAI Chat Completions message format.
602
+ * @param root0
603
+ * @param root0.msgs
604
+ * @returns An array of formatted messages compatible with OpenAI Chat Completions API.
605
+ */
606
+ async format({ msgs }) {
607
+ const formattedMsgs = [];
608
+ let index = 0;
609
+ while (index < msgs.length) {
610
+ const msg = msgs[index];
611
+ const formattedMsg = {
612
+ role: msg.role,
613
+ name: msg.name,
614
+ content: null
615
+ };
616
+ const content = [];
617
+ const cachedMsgs = [];
618
+ for (const block of getContentBlocks(msg)) {
619
+ switch (block.type) {
620
+ case "text":
621
+ content.push(this._formatTextBlock(block));
622
+ break;
623
+ case "thinking":
624
+ break;
625
+ case "tool_call":
626
+ if (!formattedMsg.tool_calls) {
627
+ formattedMsg.tool_calls = [];
628
+ }
629
+ formattedMsg.tool_calls.push({
630
+ id: block.id,
631
+ type: "function",
632
+ function: {
633
+ name: block.name,
634
+ arguments: block.input
635
+ }
636
+ });
637
+ break;
638
+ case "tool_result":
639
+ const formattedToolResult = this.convertToolOutputToString(
640
+ block.output,
641
+ this.promoteMultimodalToolResult
642
+ );
643
+ cachedMsgs.push({
644
+ role: "tool",
645
+ tool_call_id: block.id,
646
+ name: block.name,
647
+ content: formattedToolResult.text
648
+ });
649
+ if (formattedToolResult.promotedMsg?.content.length) {
650
+ msgs.splice(index + 1, 0, formattedToolResult.promotedMsg);
651
+ }
652
+ break;
653
+ case "data":
654
+ content.push(
655
+ ...await this._formatMultimodalBlock({ block, role: msg.role })
656
+ );
657
+ break;
658
+ }
659
+ }
660
+ if (content.length > 0) {
661
+ formattedMsg.content = content;
662
+ }
663
+ if (formattedMsg.content || formattedMsg.tool_calls) {
664
+ formattedMsgs.push(formattedMsg);
665
+ }
666
+ if (cachedMsgs.length > 0) {
667
+ formattedMsgs.push(...cachedMsgs);
668
+ }
669
+ index++;
670
+ }
671
+ return formattedMsgs;
672
+ }
673
+ /**
674
+ * Format a text block into OpenAI Chat Completions message content format.
675
+ * @param block
676
+ * @returns An object representing the formatted text block.
677
+ */
678
+ _formatTextBlock(block) {
679
+ return {
680
+ type: "text",
681
+ text: block.text
682
+ };
683
+ }
684
+ /**
685
+ * Format a multimodal data block into OpenAI Chat Completions message content format.
686
+ * @param root0
687
+ * @param root0.block
688
+ * @param root0.role
689
+ * @returns The formatted content blocks
690
+ */
691
+ async _formatMultimodalBlock({
692
+ block,
693
+ role
694
+ }) {
695
+ const type = block.source.mediaType.split("/")[0];
696
+ if (type === "image") {
697
+ return [
698
+ {
699
+ type: "image_url",
700
+ image_url: {
701
+ url: await this._toOpenAIImageURL(block)
702
+ }
703
+ }
704
+ ];
705
+ }
706
+ if (type === "audio") {
707
+ if (role === "assistant") {
708
+ return [];
709
+ }
710
+ return [
711
+ {
712
+ type: "input_audio",
713
+ input_audio: await this._toOpenAIAudioData(block)
714
+ }
715
+ ];
716
+ }
717
+ console.log(
718
+ `Skip unsupported media type ${block.source.mediaType} in OpenAIChatFormatter. Only image and audio are supported.`
719
+ );
720
+ return [];
721
+ }
722
+ /**
723
+ * Convert the data block to an OpenAI compatible image URL.
724
+ * @param block
725
+ * @returns A promise that resolves to a string representing the image URL in a format compatible with OpenAI Chat Completions API.
726
+ */
727
+ async _toOpenAIImageURL(block) {
728
+ if (block.source.type === "base64") {
729
+ return `data:${block.source.mediaType};base64,${block.source.data}`;
730
+ }
731
+ const sourceUrl = block.source.url;
732
+ if (sourceUrl.startsWith("http://") || sourceUrl.startsWith("https://")) {
733
+ return sourceUrl;
734
+ }
735
+ if (sourceUrl.startsWith("data:")) {
736
+ return sourceUrl;
737
+ }
738
+ const localPath = this._toLocalPath(sourceUrl);
739
+ if (!localPath || !(0, import_fs.existsSync)(localPath)) {
740
+ throw new Error(`Image path not found: ${sourceUrl}`);
741
+ }
742
+ const ext = (0, import_path.extname)(localPath).toLowerCase();
743
+ const supportedImageExtensions = [".png", ".jpg", ".jpeg", ".gif", ".webp"];
744
+ if (!supportedImageExtensions.includes(ext)) {
745
+ throw new TypeError(
746
+ `Unsupported image extension: ${ext}. Supported: ${supportedImageExtensions.join(", ")}`
747
+ );
748
+ }
749
+ const file = await (0, import_promises.readFile)(localPath);
750
+ const mime = block.source.mediaType || `image/${ext.slice(1)}`;
751
+ return `data:${mime};base64,${file.toString("base64")}`;
752
+ }
753
+ /**
754
+ * Converts a data block to OpenAI compatible audio data format.
755
+ *
756
+ * @param block - The data block containing audio information.
757
+ * @returns A promise that resolves to an object with audio data and format.
758
+ */
759
+ async _toOpenAIAudioData(block) {
760
+ const supportedMediaTypes = /* @__PURE__ */ new Map([
761
+ ["audio/wav", "wav"],
762
+ ["audio/mp3", "mp3"],
763
+ ["audio/mpeg", "mp3"]
764
+ ]);
765
+ if (block.source.type === "base64") {
766
+ const format2 = supportedMediaTypes.get(block.source.mediaType);
767
+ if (!format2) {
768
+ throw new TypeError(
769
+ `Unsupported audio media type: ${block.source.mediaType}, only audio/wav and audio/mp3 are supported.`
770
+ );
771
+ }
772
+ return { data: block.source.data, format: format2 };
773
+ }
774
+ const sourceUrl = block.source.url;
775
+ const localPath = this._toLocalPath(sourceUrl);
776
+ let data;
777
+ if (localPath && (0, import_fs.existsSync)(localPath)) {
778
+ const file = await (0, import_promises.readFile)(localPath);
779
+ data = file.toString("base64");
780
+ } else if (sourceUrl.startsWith("http://") || sourceUrl.startsWith("https://")) {
781
+ const response = await fetch(sourceUrl);
782
+ if (!response.ok) {
783
+ throw new Error(
784
+ `Failed to fetch audio from URL: ${sourceUrl} (${response.status})`
785
+ );
786
+ }
787
+ const arr = await response.arrayBuffer();
788
+ data = Buffer.from(arr).toString("base64");
789
+ } else {
790
+ throw new Error(
791
+ `Unsupported audio source: ${sourceUrl}, it should be a local file path, file URL, or an HTTP URL.`
792
+ );
793
+ }
794
+ const ext = (0, import_path.extname)(localPath || sourceUrl).toLowerCase();
795
+ const extToFormat = /* @__PURE__ */ new Map([
796
+ [".wav", "wav"],
797
+ [".mp3", "mp3"]
798
+ ]);
799
+ const format = extToFormat.get(ext);
800
+ if (!format) {
801
+ throw new TypeError(`Unsupported audio extension: ${ext}, wav and mp3 are supported.`);
802
+ }
803
+ return { data, format };
804
+ }
805
+ /**
806
+ * Converts a URL or path to a local file path.
807
+ *
808
+ * @param urlOrPath - The URL or path to convert.
809
+ * @returns The local file path, or null if not a local path.
810
+ */
811
+ _toLocalPath(urlOrPath) {
812
+ if (urlOrPath.startsWith("file://")) {
813
+ return (0, import_url.fileURLToPath)(urlOrPath);
814
+ }
815
+ if (!urlOrPath.includes("://")) {
816
+ return urlOrPath;
817
+ }
818
+ return null;
819
+ }
820
+ };
821
+
822
+ // src/model/dashscope-model.ts
823
+ var DashScopeChatModel = class extends ChatModelBase {
824
+ apiURL;
825
+ apiKey;
826
+ presetGenParams;
827
+ presetHeaders;
828
+ thinkingConfig;
829
+ /**
830
+ * Initializes a new instance of the DashScopeChatModel class.
831
+ *
832
+ * @param options - The DashScope chat model options.
833
+ * @param options.modelName - The name of the model to use.
834
+ * @param options.apiKey - The API key for authentication.
835
+ * @param options.stream - Whether to use streaming responses. Default is true.
836
+ * @param options.thinkingConfig - The thinking configuration for DashScope models, including whether to enable thinking and the thinking budget.
837
+ * @param options.maxRetries - The maximum number of retries for failed requests. Default is 3.
838
+ * @param options.fallbackModelName - The fallback model name to use if the primary model fails.
839
+ * @param options.presetGenParams - Preset generation parameters to include in each request.
840
+ * @param options.presetHeaders - Preset headers that will be included in each request.
841
+ * @param options.multimodal - Whether the model is multimodal or not, this will decide the default API endpoint. If not provided, it will be inferred from the model name.
842
+ * @param options.formatter - An optional custom formatter. If not provided, a default DashScopeChatFormatter will be used.
843
+ */
844
+ constructor({
845
+ modelName,
846
+ apiKey,
847
+ stream = true,
848
+ thinkingConfig,
849
+ maxRetries = 0,
850
+ fallbackModelName,
851
+ presetGenParams,
852
+ presetHeaders,
853
+ multimodal,
854
+ formatter
855
+ }) {
856
+ const defaultFormatter = formatter || new DashScopeChatFormatter();
857
+ super({
858
+ modelName,
859
+ stream,
860
+ maxRetries,
861
+ fallbackModelName,
862
+ formatter: defaultFormatter
863
+ });
864
+ this.apiKey = apiKey;
865
+ this.thinkingConfig = thinkingConfig;
866
+ this.presetGenParams = presetGenParams;
867
+ this.presetHeaders = presetHeaders;
868
+ if (multimodal === void 0) {
869
+ multimodal = modelName.includes("vl") || modelName.includes("qwen3.5-plus") || modelName.includes("qvq");
870
+ }
871
+ this.apiURL = multimodal ? "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation" : "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation";
872
+ }
873
+ /**
874
+ * Calls the DashScope API with the given parameters.
875
+ *
876
+ * @param modelName - The name of the model to use.
877
+ * @param options - The chat model options.
878
+ * @returns A promise that resolves to either a ChatResponse or an AsyncGenerator of ChatResponses.
879
+ */
880
+ async _callAPI(modelName, options) {
881
+ const data = {
882
+ model: modelName,
883
+ input: {
884
+ messages: options.messages
885
+ },
886
+ parameters: {
887
+ result_format: "message",
888
+ tools: this._formatToolSchemas(options.tools),
889
+ toolChoice: this._formatToolChoice(options.toolChoice),
890
+ enable_thinking: this.thinkingConfig?.enableThinking ?? false,
891
+ ...this.thinkingConfig?.thinkingBudget !== void 0 && {
892
+ thinking_budget: this.thinkingConfig.thinkingBudget
893
+ },
894
+ ...this.presetGenParams ?? {},
895
+ incremental_output: true
896
+ }
897
+ };
898
+ const headers = {
899
+ Authorization: `Bearer ${this.apiKey}`,
900
+ "Content-Type": "application/json",
901
+ ...this.presetHeaders
902
+ };
903
+ if (this.stream) {
904
+ headers["X-DashScope-SSE"] = "enable";
905
+ }
906
+ const startTime = Date.now();
907
+ const response = await fetch(this.apiURL, {
908
+ method: "POST",
909
+ headers,
910
+ body: JSON.stringify(data)
911
+ });
912
+ if (!response.ok) {
913
+ throw new Error(
914
+ `DashScope API request failed with status ${response.status}: ${await response.text()}`
915
+ );
916
+ }
917
+ if (this.stream) {
918
+ return this._parseDashScopeStreamedResponse(response, startTime);
919
+ }
920
+ const blocks = [];
921
+ const res = await response.json();
922
+ const choice = res.output.choices[0];
923
+ if (choice.message.reasoning_content) {
924
+ blocks.push({
925
+ type: "thinking",
926
+ thinking: choice.message.reasoning_content,
927
+ id: crypto.randomUUID()
928
+ });
929
+ }
930
+ if (choice.message.content) {
931
+ blocks.push({ type: "text", text: choice.message.content, id: crypto.randomUUID() });
932
+ }
933
+ if (choice.message.tool_calls && Array.isArray(choice.message.tool_calls)) {
934
+ choice.message.tool_calls.forEach((toolCall) => {
935
+ if ("id" in toolCall && "function" in toolCall && typeof toolCall.function === "object" && toolCall.function && "name" in toolCall.function && "arguments" in toolCall.function) {
936
+ const inputString = String(toolCall.function.arguments);
937
+ blocks.push({
938
+ type: "tool_call",
939
+ id: String(toolCall.id),
940
+ name: String(toolCall.function.name),
941
+ input: inputString
942
+ });
943
+ }
944
+ });
945
+ }
946
+ const usage = res.usage ? {
947
+ type: "chat_usage",
948
+ inputTokens: res.usage.input_tokens || 0,
949
+ outputTokens: res.usage.output_tokens || 0,
950
+ time: (Date.now() - startTime) / 1e3
951
+ } : void 0;
952
+ return {
953
+ type: "chat",
954
+ id: crypto.randomUUID(),
955
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
956
+ content: blocks,
957
+ usage
958
+ };
959
+ }
960
+ /**
961
+ * The method to format the tool choice parameter.
962
+ *
963
+ * @param toolChoice - The tool choice option.
964
+ * @returns The formatted tool choice.
965
+ */
966
+ _formatToolChoice(toolChoice) {
967
+ if (toolChoice) {
968
+ if (toolChoice === "auto") return "auto";
969
+ if (toolChoice === "none") return "none";
970
+ return {
971
+ type: "function",
972
+ function: {
973
+ name: toolChoice
974
+ }
975
+ };
976
+ }
977
+ return "auto";
978
+ }
979
+ /**
980
+ * Parses a streamed response from DashScope API specifically for chat responses.
981
+ * An async generator that yields delta ChatResponse objects as they are received.
982
+ *
983
+ * @param response - The fetch response object.
984
+ * @param startTime - The start time of the request for usage calculation.
985
+ * @returns An async generator yielding delta ChatResponse objects, and returns the complete ChatResponse.
986
+ */
987
+ async *_parseDashScopeStreamedResponse(response, startTime) {
988
+ const asyncGenerator = _parseStreamedResponse(response);
989
+ let accText = "";
990
+ let accThinking = "";
991
+ const accToolInputs = /* @__PURE__ */ new Map();
992
+ const toolCallMeta = /* @__PURE__ */ new Map();
993
+ let lastUsage = void 0;
994
+ for await (const jsonObj of asyncGenerator) {
995
+ if (jsonObj.output && jsonObj.output.choices) {
996
+ const choice = jsonObj.output.choices[0];
997
+ let deltaText = "";
998
+ let deltaThinking = "";
999
+ const deltaToolCalls = /* @__PURE__ */ new Map();
1000
+ const content = choice.message?.content;
1001
+ if (content) {
1002
+ if (typeof content === "string") {
1003
+ deltaText = content;
1004
+ } else if (Array.isArray(content)) {
1005
+ for (const block of content) {
1006
+ if (block.text) {
1007
+ deltaText += block.text;
1008
+ }
1009
+ }
1010
+ }
1011
+ accText += deltaText;
1012
+ }
1013
+ if (choice.message?.reasoning_content) {
1014
+ deltaThinking = choice.message.reasoning_content;
1015
+ accThinking += deltaThinking;
1016
+ }
1017
+ if (choice.message?.tool_calls) {
1018
+ choice.message.tool_calls.forEach((toolCall) => {
1019
+ const index = toolCall.index.toString();
1020
+ if (!toolCallMeta.has(index)) {
1021
+ toolCallMeta.set(index, { id: "", name: "" });
1022
+ }
1023
+ if (!accToolInputs.has(index)) {
1024
+ accToolInputs.set(index, "");
1025
+ }
1026
+ if (toolCall.id) {
1027
+ toolCallMeta.get(index).id = toolCall.id;
1028
+ }
1029
+ if (toolCall.function?.name) {
1030
+ toolCallMeta.get(index).name = toolCall.function.name;
1031
+ }
1032
+ if (toolCall.function?.arguments) {
1033
+ const deltaArgs = toolCall.function.arguments;
1034
+ accToolInputs.set(index, accToolInputs.get(index) + deltaArgs);
1035
+ const meta = toolCallMeta.get(index);
1036
+ deltaToolCalls.set(index, {
1037
+ type: "tool_call",
1038
+ id: meta.id,
1039
+ name: meta.name,
1040
+ input: deltaArgs
1041
+ });
1042
+ }
1043
+ });
1044
+ }
1045
+ const deltaBlocks = this._dataToBlocks(deltaText, deltaThinking, deltaToolCalls);
1046
+ lastUsage = jsonObj.usage ? {
1047
+ type: "chat_usage",
1048
+ inputTokens: jsonObj.usage.input_tokens || 0,
1049
+ outputTokens: jsonObj.usage.output_tokens || 0,
1050
+ time: (Date.now() - startTime) / 1e3
1051
+ } : void 0;
1052
+ yield {
1053
+ type: "chat",
1054
+ id: crypto.randomUUID(),
1055
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1056
+ content: deltaBlocks,
1057
+ usage: lastUsage
1058
+ };
1059
+ }
1060
+ }
1061
+ const finalToolCalls = /* @__PURE__ */ new Map();
1062
+ toolCallMeta.forEach((meta, index) => {
1063
+ finalToolCalls.set(index, {
1064
+ type: "tool_call",
1065
+ id: meta.id,
1066
+ name: meta.name,
1067
+ input: accToolInputs.get(index) || "{}"
1068
+ });
1069
+ });
1070
+ const blocks = this._dataToBlocks(accText, accThinking, finalToolCalls);
1071
+ return {
1072
+ type: "chat",
1073
+ id: crypto.randomUUID(),
1074
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1075
+ content: blocks,
1076
+ usage: lastUsage
1077
+ };
1078
+ }
1079
+ /**
1080
+ * Convert data into blocks
1081
+ *
1082
+ * @param text - The text response from the llm API
1083
+ * @param thinking - The thinking response
1084
+ * @param toolCalls - The tool calls
1085
+ * @returns An array of blocks
1086
+ */
1087
+ _dataToBlocks(text, thinking, toolCalls) {
1088
+ const blocks = [];
1089
+ if (thinking) {
1090
+ blocks.push({ type: "thinking", thinking, id: crypto.randomUUID() });
1091
+ }
1092
+ if (text) {
1093
+ blocks.push({ type: "text", text, id: crypto.randomUUID() });
1094
+ }
1095
+ if (toolCalls.size > 0) {
1096
+ toolCalls.forEach((value) => {
1097
+ blocks.push(value);
1098
+ });
1099
+ }
1100
+ return blocks;
1101
+ }
1102
+ /**
1103
+ * Format the tool schemas to the expected API format.
1104
+ * @param tools
1105
+ * @returns The formatted tool schemas.
1106
+ */
1107
+ _formatToolSchemas(tools) {
1108
+ return tools || [];
1109
+ }
1110
+ };
1111
+
1112
+ // src/model/deepseek-model.ts
1113
+ var DeepSeekChatModel = class extends ChatModelBase {
1114
+ apiURL;
1115
+ apiKey;
1116
+ presetGenParams;
1117
+ presetHeaders;
1118
+ thinkingConfig;
1119
+ /**
1120
+ * Initializes a new instance of the DeepSeekChatModel class.
1121
+ *
1122
+ * @param options - The DeepSeek chat model options.
1123
+ * @param options.modelName - The name of the model to use.
1124
+ * @param options.apiKey - The API key for authentication.
1125
+ * @param options.stream - Whether to use streaming responses. Default is true.
1126
+ * @param options.thinkingConfig - Thinking configuration.
1127
+ * @param options.maxRetries - The maximum number of retries for failed requests. Default is 0.
1128
+ * @param options.fallbackModelName - The fallback model name to use if the primary model fails.
1129
+ * @param options.presetGenParams - Preset generation parameters to include in each request.
1130
+ * @param options.presetHeaders - Preset headers that will be included in each request.
1131
+ * @param options.formatter
1132
+ */
1133
+ constructor({
1134
+ modelName,
1135
+ apiKey,
1136
+ stream = true,
1137
+ thinkingConfig,
1138
+ maxRetries = 0,
1139
+ fallbackModelName,
1140
+ presetGenParams,
1141
+ presetHeaders,
1142
+ formatter
1143
+ }) {
1144
+ const defaultFormatter = formatter || new DeepSeekChatFormatter();
1145
+ super({
1146
+ modelName,
1147
+ stream,
1148
+ maxRetries,
1149
+ fallbackModelName,
1150
+ formatter: defaultFormatter
1151
+ });
1152
+ this.apiKey = apiKey;
1153
+ this.thinkingConfig = thinkingConfig || { enableThinking: false };
1154
+ this.presetGenParams = presetGenParams;
1155
+ this.presetHeaders = presetHeaders;
1156
+ this.apiURL = "https://api.deepseek.com/chat/completions";
1157
+ }
1158
+ /**
1159
+ * Calls the DeepSeek API with the given parameters.
1160
+ *
1161
+ * @param modelName - The name of the model to use.
1162
+ * @param options - The chat model options.
1163
+ * @returns A promise that resolves to either a ChatResponse or an AsyncGenerator of ChatResponses.
1164
+ */
1165
+ async _callAPI(modelName, options) {
1166
+ const data = {
1167
+ model: modelName,
1168
+ messages: options.messages,
1169
+ tools: this._formatToolSchemas(options.tools),
1170
+ tool_choice: this._formatToolChoice(options.toolChoice),
1171
+ thinking: this.thinkingConfig.enableThinking ? { type: "enabled" } : { type: "disabled" },
1172
+ stream: this.stream,
1173
+ ...this.presetGenParams ?? {}
1174
+ };
1175
+ const headers = {
1176
+ Authorization: `Bearer ${this.apiKey}`,
1177
+ "Content-Type": "application/json",
1178
+ ...this.presetHeaders
1179
+ };
1180
+ const startTime = Date.now();
1181
+ const response = await fetch(this.apiURL, {
1182
+ method: "POST",
1183
+ headers,
1184
+ body: JSON.stringify(data)
1185
+ });
1186
+ if (!response.ok) {
1187
+ throw new Error(
1188
+ `DeepSeek API request failed with status ${response.status}: ${await response.text()}`
1189
+ );
1190
+ }
1191
+ if (this.stream) {
1192
+ return this._parseDeepSeekStreamedResponse(response, startTime);
1193
+ }
1194
+ const blocks = [];
1195
+ const res = await response.json();
1196
+ const choice = res.choices[0];
1197
+ if (choice.message.reasoning_content) {
1198
+ blocks.push({
1199
+ id: crypto.randomUUID(),
1200
+ type: "thinking",
1201
+ thinking: choice.message.reasoning_content
1202
+ });
1203
+ }
1204
+ if (choice.message.content) {
1205
+ blocks.push({ id: crypto.randomUUID(), type: "text", text: choice.message.content });
1206
+ }
1207
+ if (choice.message.tool_calls && Array.isArray(choice.message.tool_calls)) {
1208
+ choice.message.tool_calls.forEach((toolCall) => {
1209
+ if ("id" in toolCall && "function" in toolCall && typeof toolCall.function === "object" && toolCall.function && "name" in toolCall.function && "arguments" in toolCall.function) {
1210
+ const inputString = String(toolCall.function.arguments);
1211
+ blocks.push({
1212
+ type: "tool_call",
1213
+ id: String(toolCall.id),
1214
+ name: String(toolCall.function.name),
1215
+ input: inputString
1216
+ });
1217
+ }
1218
+ });
1219
+ }
1220
+ const usage = res.usage ? {
1221
+ type: "chat_usage",
1222
+ inputTokens: res.usage.prompt_tokens || 0,
1223
+ outputTokens: res.usage.completion_tokens || 0,
1224
+ time: (Date.now() - startTime) / 1e3
1225
+ } : void 0;
1226
+ return {
1227
+ type: "chat",
1228
+ id: crypto.randomUUID(),
1229
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1230
+ content: blocks,
1231
+ usage
1232
+ };
1233
+ }
1234
+ /**
1235
+ * The method to format the tool choice parameter.
1236
+ *
1237
+ * @param toolChoice - The tool choice option.
1238
+ * @returns The formatted tool choice.
1239
+ */
1240
+ _formatToolChoice(toolChoice) {
1241
+ if (toolChoice) {
1242
+ if (toolChoice === "auto") return "auto";
1243
+ if (toolChoice === "none") return "none";
1244
+ if (this.thinkingConfig?.enableThinking) {
1245
+ console.log(
1246
+ `The deepseek reasoning model does not support tool choice options '${toolChoice}'. 'auto' will be used instead.`
1247
+ );
1248
+ return "auto";
1249
+ }
1250
+ if (toolChoice === "required") return "required";
1251
+ return {
1252
+ type: "function",
1253
+ function: {
1254
+ name: toolChoice
1255
+ }
1256
+ };
1257
+ }
1258
+ return "auto";
1259
+ }
1260
+ /**
1261
+ * Parses a streamed response from DeepSeek API specifically for chat responses.
1262
+ * An async generator that yields delta ChatResponse objects as they are received.
1263
+ *
1264
+ * @param response - The fetch response object.
1265
+ * @param startTime - The start time of the request for usage calculation.
1266
+ * @returns An async generator yielding delta ChatResponse objects, and returns the complete ChatResponse.
1267
+ */
1268
+ async *_parseDeepSeekStreamedResponse(response, startTime) {
1269
+ const asyncGenerator = _parseStreamedResponse(response);
1270
+ let accText = "";
1271
+ let accThinking = "";
1272
+ const accToolInputs = /* @__PURE__ */ new Map();
1273
+ const toolCallMeta = /* @__PURE__ */ new Map();
1274
+ let lastUsage = void 0;
1275
+ for await (const jsonObj of asyncGenerator) {
1276
+ if (jsonObj.choices && jsonObj.choices.length > 0) {
1277
+ const choice = jsonObj.choices[0];
1278
+ let deltaText = "";
1279
+ let deltaThinking = "";
1280
+ const deltaToolCalls = /* @__PURE__ */ new Map();
1281
+ if (choice.delta?.content) {
1282
+ deltaText = choice.delta.content;
1283
+ accText += deltaText;
1284
+ }
1285
+ if (choice.delta?.reasoning_content) {
1286
+ deltaThinking = choice.delta.reasoning_content;
1287
+ accThinking += deltaThinking;
1288
+ }
1289
+ if (choice.delta?.tool_calls) {
1290
+ choice.delta.tool_calls.forEach((toolCall) => {
1291
+ const index = toolCall.index.toString();
1292
+ if (!toolCallMeta.has(index)) {
1293
+ toolCallMeta.set(index, { id: "", name: "" });
1294
+ }
1295
+ if (!accToolInputs.has(index)) {
1296
+ accToolInputs.set(index, "");
1297
+ }
1298
+ if (toolCall.id) {
1299
+ toolCallMeta.get(index).id = toolCall.id;
1300
+ }
1301
+ if (toolCall.function?.name) {
1302
+ toolCallMeta.get(index).name = toolCall.function.name;
1303
+ }
1304
+ if (toolCall.function?.arguments) {
1305
+ const deltaArgs = toolCall.function.arguments;
1306
+ accToolInputs.set(index, accToolInputs.get(index) + deltaArgs);
1307
+ const meta = toolCallMeta.get(index);
1308
+ deltaToolCalls.set(index, {
1309
+ type: "tool_call",
1310
+ id: meta.id,
1311
+ name: meta.name,
1312
+ input: deltaArgs
1313
+ });
1314
+ }
1315
+ });
1316
+ }
1317
+ const deltaBlocks = this._accDataToBlocks(deltaText, deltaThinking, deltaToolCalls);
1318
+ lastUsage = jsonObj.usage ? {
1319
+ type: "chat_usage",
1320
+ inputTokens: jsonObj.usage.prompt_tokens || 0,
1321
+ outputTokens: jsonObj.usage.completion_tokens || 0,
1322
+ time: (Date.now() - startTime) / 1e3
1323
+ } : void 0;
1324
+ yield {
1325
+ type: "chat",
1326
+ id: crypto.randomUUID(),
1327
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1328
+ content: deltaBlocks,
1329
+ usage: lastUsage
1330
+ };
1331
+ }
1332
+ }
1333
+ const finalToolCalls = /* @__PURE__ */ new Map();
1334
+ toolCallMeta.forEach((meta, index) => {
1335
+ finalToolCalls.set(index, {
1336
+ type: "tool_call",
1337
+ id: meta.id,
1338
+ name: meta.name,
1339
+ input: accToolInputs.get(index) || "{}"
1340
+ });
1341
+ });
1342
+ const blocks = this._accDataToBlocks(accText, accThinking, finalToolCalls);
1343
+ return {
1344
+ type: "chat",
1345
+ id: crypto.randomUUID(),
1346
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1347
+ content: blocks,
1348
+ usage: lastUsage
1349
+ };
1350
+ }
1351
+ /**
1352
+ * Convert data into blocks
1353
+ *
1354
+ * @param text - The text response from the llm API
1355
+ * @param thinking - The thinking response
1356
+ * @param toolCalls - The tool calls
1357
+ * @returns An array of blocks
1358
+ */
1359
+ _accDataToBlocks(text, thinking, toolCalls) {
1360
+ const blocks = [];
1361
+ if (thinking) {
1362
+ blocks.push({ id: crypto.randomUUID(), type: "thinking", thinking });
1363
+ }
1364
+ if (text) {
1365
+ blocks.push({ id: crypto.randomUUID(), type: "text", text });
1366
+ }
1367
+ if (toolCalls.size > 0) {
1368
+ toolCalls.forEach((value) => {
1369
+ blocks.push(value);
1370
+ });
1371
+ }
1372
+ return blocks;
1373
+ }
1374
+ /**
1375
+ * Format the tool schemas to the expected API format for DeepSeek API.
1376
+ * @param tools
1377
+ * @returns The formatted tool schemas.
1378
+ */
1379
+ _formatToolSchemas(tools) {
1380
+ return tools || [];
1381
+ }
1382
+ };
1383
+
1384
+ // src/model/ollama-model.ts
1385
+ var import_ollama = require("ollama");
1386
+ var OllamaChatModel = class extends ChatModelBase {
1387
+ client;
1388
+ options;
1389
+ keepAlive;
1390
+ thinkingConfig;
1391
+ generateKwargs;
1392
+ /**
1393
+ * Initializes a new instance of the OllamaChatModel class.
1394
+ * @param root0
1395
+ * @param root0.modelName
1396
+ * @param root0.stream
1397
+ * @param root0.options
1398
+ * @param root0.keepAlive
1399
+ * @param root0.thinkingConfig
1400
+ * @param root0.host
1401
+ * @param root0.maxRetries
1402
+ * @param root0.fallbackModelName
1403
+ * @param root0.clientKwargs
1404
+ * @param root0.generateKwargs
1405
+ * @param root0.formatter
1406
+ */
1407
+ constructor({
1408
+ modelName,
1409
+ stream = true,
1410
+ options,
1411
+ keepAlive = "5m",
1412
+ thinkingConfig,
1413
+ host,
1414
+ maxRetries = 0,
1415
+ fallbackModelName,
1416
+ clientKwargs,
1417
+ generateKwargs,
1418
+ formatter
1419
+ }) {
1420
+ const defaultFormatter = formatter || new OllamaChatFormatter();
1421
+ super({
1422
+ modelName,
1423
+ stream,
1424
+ maxRetries,
1425
+ fallbackModelName,
1426
+ formatter: defaultFormatter
1427
+ });
1428
+ this.options = options;
1429
+ this.keepAlive = keepAlive;
1430
+ this.thinkingConfig = thinkingConfig || {
1431
+ enableThinking: false
1432
+ };
1433
+ this.generateKwargs = generateKwargs || {};
1434
+ this.client = new import_ollama.Ollama({
1435
+ host,
1436
+ ...clientKwargs
1437
+ });
1438
+ }
1439
+ /**
1440
+ * Calls the Ollama API with the given parameters.
1441
+ * @param modelName
1442
+ * @param options
1443
+ * @returns A promise that resolves to either a ChatResponse or an AsyncGenerator of ChatResponses.
1444
+ */
1445
+ async _callAPI(modelName, options) {
1446
+ const kwargs = {
1447
+ model: modelName,
1448
+ messages: options.messages,
1449
+ stream: this.stream,
1450
+ options: this.options,
1451
+ keep_alive: this.keepAlive,
1452
+ ...this.generateKwargs
1453
+ };
1454
+ if (this.thinkingConfig.enableThinking) {
1455
+ kwargs.think = this.thinkingConfig.thinkingLevel || true;
1456
+ } else {
1457
+ kwargs.think = false;
1458
+ }
1459
+ if (options.tools) {
1460
+ kwargs.tools = this._formatToolSchemas(options.tools);
1461
+ }
1462
+ if (options.toolChoice) {
1463
+ console.warn("Ollama does not support tool_choice yet, ignored.");
1464
+ }
1465
+ const startTime = Date.now();
1466
+ if (this.stream) {
1467
+ const response2 = await this.client.chat({
1468
+ ...kwargs,
1469
+ stream: true
1470
+ });
1471
+ return this._parseOllamaStreamResponse(response2, startTime);
1472
+ }
1473
+ const response = await this.client.chat({
1474
+ ...kwargs,
1475
+ stream: false
1476
+ });
1477
+ return this._parseOllamaResponse(response, startTime);
1478
+ }
1479
+ /**
1480
+ * Parse Ollama streaming response.
1481
+ * @param stream
1482
+ * @param startTime
1483
+ * @returns An async generator that yields delta ChatResponse objects and returns the complete ChatResponse.
1484
+ */
1485
+ async *_parseOllamaStreamResponse(stream, startTime) {
1486
+ let accText = "";
1487
+ let accThinking = "";
1488
+ const toolCalls = /* @__PURE__ */ new Map();
1489
+ let lastUsage = null;
1490
+ for await (const chunk of stream) {
1491
+ const msg = chunk.message;
1492
+ let deltaText = "";
1493
+ let deltaThinking = "";
1494
+ const deltaToolCalls = /* @__PURE__ */ new Map();
1495
+ if (msg.thinking) {
1496
+ deltaThinking = msg.thinking;
1497
+ accThinking += msg.thinking;
1498
+ }
1499
+ if (msg.content) {
1500
+ deltaText = msg.content;
1501
+ accText += msg.content;
1502
+ }
1503
+ if (msg.tool_calls && Array.isArray(msg.tool_calls)) {
1504
+ for (let idx = 0; idx < msg.tool_calls.length; idx++) {
1505
+ const toolCall = msg.tool_calls[idx];
1506
+ const func = toolCall.function;
1507
+ const toolId = `${idx}_${func.name}`;
1508
+ const toolCallBlock = {
1509
+ type: "tool_call",
1510
+ id: toolId,
1511
+ name: func.name,
1512
+ input: JSON.stringify(func.arguments)
1513
+ };
1514
+ toolCalls.set(toolId, toolCallBlock);
1515
+ deltaToolCalls.set(toolId, toolCallBlock);
1516
+ }
1517
+ }
1518
+ const currentTime = (Date.now() - startTime) / 1e3;
1519
+ lastUsage = {
1520
+ type: "chat_usage",
1521
+ inputTokens: chunk.prompt_eval_count || 0,
1522
+ outputTokens: chunk.eval_count || 0,
1523
+ time: currentTime
1524
+ };
1525
+ const deltaBlocks = this._buildContentBlocks(deltaText, deltaThinking, deltaToolCalls);
1526
+ yield {
1527
+ type: "chat",
1528
+ id: crypto.randomUUID(),
1529
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1530
+ content: deltaBlocks,
1531
+ usage: lastUsage
1532
+ };
1533
+ }
1534
+ const blocks = this._buildContentBlocks(accText, accThinking, toolCalls);
1535
+ return {
1536
+ type: "chat",
1537
+ id: crypto.randomUUID(),
1538
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1539
+ content: blocks,
1540
+ usage: lastUsage
1541
+ };
1542
+ }
1543
+ /**
1544
+ * Parse Ollama non-streaming response.
1545
+ * @param response
1546
+ * @param startTime
1547
+ * @returns A ChatResponse object containing the content blocks and usage.
1548
+ */
1549
+ _parseOllamaResponse(response, startTime) {
1550
+ const blocks = [];
1551
+ if (response.message.thinking) {
1552
+ blocks.push({
1553
+ id: crypto.randomUUID(),
1554
+ type: "thinking",
1555
+ thinking: response.message.thinking
1556
+ });
1557
+ }
1558
+ if (response.message.content) {
1559
+ blocks.push({
1560
+ id: crypto.randomUUID(),
1561
+ type: "text",
1562
+ text: response.message.content
1563
+ });
1564
+ }
1565
+ if (response.message.tool_calls && Array.isArray(response.message.tool_calls)) {
1566
+ for (let idx = 0; idx < response.message.tool_calls.length; idx++) {
1567
+ const toolCall = response.message.tool_calls[idx];
1568
+ blocks.push({
1569
+ type: "tool_call",
1570
+ id: `${idx}_${toolCall.function.name}`,
1571
+ name: toolCall.function.name,
1572
+ input: JSON.stringify(toolCall.function.arguments)
1573
+ });
1574
+ }
1575
+ }
1576
+ const usage = response.prompt_eval_count !== void 0 && response.eval_count !== void 0 ? {
1577
+ type: "chat_usage",
1578
+ inputTokens: response.prompt_eval_count || 0,
1579
+ outputTokens: response.eval_count || 0,
1580
+ time: (Date.now() - startTime) / 1e3
1581
+ } : void 0;
1582
+ return {
1583
+ type: "chat",
1584
+ id: crypto.randomUUID(),
1585
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1586
+ content: blocks,
1587
+ usage
1588
+ };
1589
+ }
1590
+ /**
1591
+ * Build content blocks from accumulated data.
1592
+ * @param text
1593
+ * @param thinking
1594
+ * @param toolCalls
1595
+ * @returns An array of content blocks.
1596
+ */
1597
+ _buildContentBlocks(text, thinking, toolCalls) {
1598
+ const blocks = [];
1599
+ if (thinking) {
1600
+ blocks.push({ id: crypto.randomUUID(), type: "thinking", thinking });
1601
+ }
1602
+ if (text) {
1603
+ blocks.push({ id: crypto.randomUUID(), type: "text", text });
1604
+ }
1605
+ toolCalls.forEach((toolCall) => {
1606
+ blocks.push(toolCall);
1607
+ });
1608
+ return blocks;
1609
+ }
1610
+ /**
1611
+ * Format tool choice parameter (not supported by Ollama).
1612
+ * @param _toolChoice
1613
+ * @returns undefined as Ollama does not support tool choice.
1614
+ */
1615
+ _formatToolChoice(_toolChoice) {
1616
+ return void 0;
1617
+ }
1618
+ /**
1619
+ * Format tool schemas for Ollama API (no special formatting needed).
1620
+ * @param tools
1621
+ * @returns The same array of tool schemas, or an empty array if undefined.
1622
+ */
1623
+ _formatToolSchemas(tools) {
1624
+ return tools || [];
1625
+ }
1626
+ };
1627
+
1628
+ // src/model/openai-model.ts
1629
+ var import_openai = require("openai");
1630
+ var OpenAIChatModel = class extends ChatModelBase {
1631
+ client;
1632
+ presetGenParams;
1633
+ /**
1634
+ * Initializes a new instance of the OpenAIChatModel class.
1635
+ * @param options
1636
+ * @param options.modelName
1637
+ * @param options.apiKey
1638
+ * @param options.stream
1639
+ * @param options.maxRetries
1640
+ * @param options.fallbackModelName
1641
+ * @param options.presetGenParams
1642
+ * @param options.baseURL
1643
+ * @param options.formatter
1644
+ */
1645
+ constructor({
1646
+ modelName,
1647
+ apiKey,
1648
+ stream = true,
1649
+ maxRetries = 3,
1650
+ fallbackModelName,
1651
+ presetGenParams,
1652
+ baseURL,
1653
+ formatter
1654
+ }) {
1655
+ const defaultFormatter = formatter || new OpenAIChatFormatter();
1656
+ super({
1657
+ modelName,
1658
+ stream,
1659
+ maxRetries,
1660
+ fallbackModelName,
1661
+ formatter: defaultFormatter
1662
+ });
1663
+ this.client = new import_openai.OpenAI({
1664
+ apiKey,
1665
+ baseURL
1666
+ });
1667
+ this.presetGenParams = presetGenParams;
1668
+ }
1669
+ /**
1670
+ * Calls the OpenAI API with the given parameters.
1671
+ *
1672
+ * @param modelName - The name of the model to use.
1673
+ * @param options - The chat model options.
1674
+ * @returns A promise that resolves to either a ChatResponse or an AsyncGenerator of ChatResponses.
1675
+ */
1676
+ async _callAPI(modelName, options) {
1677
+ const startTime = Date.now();
1678
+ if (this.stream) {
1679
+ const stream = await this.client.chat.completions.create({
1680
+ model: modelName,
1681
+ messages: options.messages,
1682
+ tools: this._formatToolSchemas(options.tools),
1683
+ tool_choice: this._formatToolChoice(options.toolChoice),
1684
+ stream: true,
1685
+ ...this.presetGenParams ?? {}
1686
+ });
1687
+ return this._parseOpenAIStreamedResponse(stream, startTime);
1688
+ }
1689
+ const response = await this.client.chat.completions.create({
1690
+ model: modelName,
1691
+ messages: options.messages,
1692
+ tools: options.tools,
1693
+ tool_choice: this._formatToolChoice(options.toolChoice),
1694
+ stream: false,
1695
+ ...this.presetGenParams ?? {}
1696
+ });
1697
+ const choice = response.choices[0];
1698
+ const blocks = [];
1699
+ if (choice.message.content) {
1700
+ blocks.push({ id: crypto.randomUUID(), type: "text", text: choice.message.content });
1701
+ }
1702
+ if (choice.message.tool_calls && Array.isArray(choice.message.tool_calls)) {
1703
+ choice.message.tool_calls.forEach((toolCall) => {
1704
+ if (toolCall.type === "function") {
1705
+ blocks.push({
1706
+ type: "tool_call",
1707
+ id: toolCall.id,
1708
+ name: toolCall.function.name,
1709
+ input: toolCall.function.arguments
1710
+ });
1711
+ }
1712
+ });
1713
+ }
1714
+ const usage = response.usage ? {
1715
+ type: "chat_usage",
1716
+ inputTokens: response.usage.prompt_tokens,
1717
+ outputTokens: response.usage.completion_tokens,
1718
+ time: (Date.now() - startTime) / 1e3
1719
+ } : void 0;
1720
+ return {
1721
+ type: "chat",
1722
+ id: response.id,
1723
+ createdAt: new Date(response.created * 1e3).toISOString(),
1724
+ content: blocks,
1725
+ usage
1726
+ };
1727
+ }
1728
+ /**
1729
+ * Formats the tool choice for the API request.
1730
+ *
1731
+ * TODO: supports grouped tool choices.
1732
+ *
1733
+ * @param toolChoice - The tool choice option.
1734
+ * @returns The formatted tool choice.
1735
+ */
1736
+ _formatToolChoice(toolChoice) {
1737
+ if (toolChoice) {
1738
+ if (toolChoice === "none" || toolChoice === "auto" || toolChoice === "required") {
1739
+ return toolChoice;
1740
+ }
1741
+ return {
1742
+ type: "function",
1743
+ function: {
1744
+ name: toolChoice
1745
+ }
1746
+ };
1747
+ }
1748
+ return "auto";
1749
+ }
1750
+ /**
1751
+ * Parses a streamed response from OpenAI API.
1752
+ * An async generator that yields delta ChatResponse objects as they are received.
1753
+ *
1754
+ * @param stream - The OpenAI stream object.
1755
+ * @param startTime - The start time of the request for usage calculation.
1756
+ * @returns An async generator yielding delta ChatResponse objects, and returns the complete ChatResponse.
1757
+ */
1758
+ async *_parseOpenAIStreamedResponse(stream, startTime) {
1759
+ let accText = "";
1760
+ const accToolInputs = /* @__PURE__ */ new Map();
1761
+ const toolCallMeta = /* @__PURE__ */ new Map();
1762
+ let lastUsage = null;
1763
+ let responseId = "";
1764
+ let createdTimestamp = 0;
1765
+ for await (const chunk of stream) {
1766
+ if (!responseId && chunk.id) {
1767
+ responseId = chunk.id;
1768
+ }
1769
+ if (!createdTimestamp && chunk.created) {
1770
+ createdTimestamp = chunk.created;
1771
+ }
1772
+ if (chunk.choices && chunk.choices.length > 0) {
1773
+ const choice = chunk.choices[0];
1774
+ let deltaText = "";
1775
+ const deltaToolCalls = /* @__PURE__ */ new Map();
1776
+ if (choice.delta?.content) {
1777
+ deltaText = choice.delta.content;
1778
+ accText += deltaText;
1779
+ }
1780
+ if (choice.delta?.tool_calls) {
1781
+ choice.delta.tool_calls.forEach((toolCall) => {
1782
+ const index = toolCall.index.toString();
1783
+ if (!toolCallMeta.has(index)) {
1784
+ toolCallMeta.set(index, { id: "", name: "" });
1785
+ }
1786
+ if (!accToolInputs.has(index)) {
1787
+ accToolInputs.set(index, "");
1788
+ }
1789
+ if (toolCall.id) {
1790
+ toolCallMeta.get(index).id = toolCall.id;
1791
+ }
1792
+ if (toolCall.function?.name) {
1793
+ toolCallMeta.get(index).name = toolCall.function.name;
1794
+ }
1795
+ if (toolCall.function?.arguments) {
1796
+ const deltaArgs = toolCall.function.arguments;
1797
+ accToolInputs.set(index, accToolInputs.get(index) + deltaArgs);
1798
+ const meta = toolCallMeta.get(index);
1799
+ deltaToolCalls.set(index, {
1800
+ type: "tool_call",
1801
+ id: meta.id,
1802
+ name: meta.name,
1803
+ input: deltaArgs
1804
+ });
1805
+ }
1806
+ });
1807
+ }
1808
+ const deltaBlocks = this._accDataToBlocks(deltaText, deltaToolCalls);
1809
+ yield {
1810
+ type: "chat",
1811
+ id: responseId || crypto.randomUUID(),
1812
+ createdAt: createdTimestamp ? new Date(createdTimestamp * 1e3).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
1813
+ content: deltaBlocks,
1814
+ usage: lastUsage
1815
+ };
1816
+ }
1817
+ if (chunk.usage) {
1818
+ lastUsage = {
1819
+ type: "chat_usage",
1820
+ inputTokens: chunk.usage.prompt_tokens || 0,
1821
+ outputTokens: chunk.usage.completion_tokens || 0,
1822
+ time: (Date.now() - startTime) / 1e3
1823
+ };
1824
+ }
1825
+ }
1826
+ const finalToolCalls = /* @__PURE__ */ new Map();
1827
+ toolCallMeta.forEach((meta, index) => {
1828
+ finalToolCalls.set(index, {
1829
+ type: "tool_call",
1830
+ id: meta.id,
1831
+ name: meta.name,
1832
+ input: accToolInputs.get(index) || "{}"
1833
+ });
1834
+ });
1835
+ const blocks = this._accDataToBlocks(accText, finalToolCalls);
1836
+ return {
1837
+ type: "chat",
1838
+ id: responseId || crypto.randomUUID(),
1839
+ createdAt: createdTimestamp ? new Date(createdTimestamp * 1e3).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
1840
+ content: blocks,
1841
+ usage: lastUsage
1842
+ };
1843
+ }
1844
+ /**
1845
+ * Convert data into blocks
1846
+ *
1847
+ * @param text - The text response from the llm API
1848
+ * @param toolCalls - The tool calls
1849
+ * @returns An array of blocks
1850
+ */
1851
+ _accDataToBlocks(text, toolCalls) {
1852
+ const blocks = [];
1853
+ if (text) {
1854
+ blocks.push({ id: crypto.randomUUID(), type: "text", text });
1855
+ }
1856
+ if (toolCalls.size > 0) {
1857
+ toolCalls.forEach((value) => {
1858
+ blocks.push(value);
1859
+ });
1860
+ }
1861
+ return blocks;
1862
+ }
1863
+ /**
1864
+ * Format the tool schemas to the expected API format.
1865
+ * @param tools
1866
+ * @returns The formatted tool schemas.
1867
+ */
1868
+ _formatToolSchemas(tools) {
1869
+ return tools || [];
1870
+ }
1871
+ };
1872
+ // Annotate the CommonJS export names for ESM import in node:
1873
+ 0 && (module.exports = {
1874
+ ChatModelBase,
1875
+ DashScopeChatModel,
1876
+ DeepSeekChatModel,
1877
+ OllamaChatModel,
1878
+ OpenAIChatModel
1879
+ });
1880
+ //# sourceMappingURL=index.js.map