@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,616 @@
1
+ // src/message/message.ts
2
+ function createMsg({
3
+ name,
4
+ content,
5
+ role,
6
+ metadata = {},
7
+ id = crypto.randomUUID(),
8
+ timestamp = (/* @__PURE__ */ new Date()).toISOString(),
9
+ usage
10
+ }) {
11
+ return { id, name, role, content, metadata, timestamp, usage };
12
+ }
13
+ function getTextContent(msg, separator = "\n") {
14
+ const textBlocks = msg.content.filter((block) => block.type === "text");
15
+ if (textBlocks.length === 0) {
16
+ return null;
17
+ }
18
+ return textBlocks.map((block) => block.text).join(separator);
19
+ }
20
+ function getContentBlocks(msg, blockType) {
21
+ if (!blockType) return msg.content;
22
+ return msg.content.filter((block) => block.type === blockType);
23
+ }
24
+
25
+ // src/formatter/base.ts
26
+ var FormatterBase = class {
27
+ /**
28
+ * Convert the tool output to string format for the LLM APIs that only accept text input. If
29
+ * `promoteMultimodalToolResult` is true, the multimodal content will be promoted to be a user message with
30
+ * "<system-info></system-info>" tags. Otherwise, the multimodal content will be saved to a storage and a URL link
31
+ * will be provided in the text output.
32
+ *
33
+ * @param output - The tool output, which can be a string or an array of content blocks.
34
+ * @param promoteMultimodalToolResult - Whether to promote the multimodal content to the prompt messages.
35
+ * @returns An object containing the text output and an optional promoted message.
36
+ */
37
+ convertToolOutputToString(output, promoteMultimodalToolResult) {
38
+ if (typeof output === "string") return { text: output, promotedMsg: null };
39
+ let textualOutput = [];
40
+ const promotedData = [];
41
+ for (const block of output) {
42
+ switch (block.type) {
43
+ case "text":
44
+ textualOutput.push(block.text);
45
+ break;
46
+ default:
47
+ const type = block.source.mediaType.split("/")[0];
48
+ if (type !== "image" && type !== "audio" && type !== "video") {
49
+ console.log(
50
+ `Unsupported media type '${block.source.mediaType}' in tool output. Only image, audio and video are supported.`
51
+ );
52
+ break;
53
+ }
54
+ if (block.source.type === "url") {
55
+ textualOutput.push(
56
+ `<system-info>One returned ${type} can be found at: ${block.source.url}</system-info>`
57
+ );
58
+ } else {
59
+ const shouldPromote = promoteMultimodalToolResult === true || typeof promoteMultimodalToolResult === "object" && promoteMultimodalToolResult[type];
60
+ if (shouldPromote) {
61
+ const dataID = Math.random().toString(36).substring(2, 10);
62
+ textualOutput.push(
63
+ `<system-info>One returned ${type} is embedded with ID '${dataID}' and will be attached within '<system-info></system-info>' tags later.</system-info>`
64
+ );
65
+ promotedData.push({ id: dataID, block });
66
+ } else {
67
+ textualOutput.push(`The returned ${block.type} is stored locally.`);
68
+ }
69
+ }
70
+ }
71
+ }
72
+ const promotedBlocks = [];
73
+ promotedData.forEach(({ id, block }) => {
74
+ const type = block.source.mediaType.split("/")[0];
75
+ promotedBlocks.push({
76
+ id: crypto.randomUUID(),
77
+ type: "text",
78
+ text: `<${type}_data id='${id}'>`
79
+ });
80
+ promotedBlocks.push(block);
81
+ promotedBlocks.push({
82
+ id: crypto.randomUUID(),
83
+ type: "text",
84
+ text: `</${type}_data>
85
+ `
86
+ });
87
+ });
88
+ if (promotedBlocks.length > 0) {
89
+ const prefix = "<system-info>The multimodal contents returned from the tool call are as follows:\n";
90
+ if (promotedBlocks[0].type === "text") {
91
+ promotedBlocks[0].text = `${prefix}${promotedBlocks[0].text}`;
92
+ } else {
93
+ promotedBlocks.unshift({
94
+ id: crypto.randomUUID(),
95
+ type: "text",
96
+ text: `${prefix}`
97
+ });
98
+ }
99
+ const lastBlock = promotedBlocks[promotedBlocks.length - 1];
100
+ if (lastBlock.type === "text") {
101
+ promotedBlocks[promotedBlocks.length - 1] = {
102
+ id: crypto.randomUUID(),
103
+ type: "text",
104
+ text: `${lastBlock.text}</system-info>`
105
+ };
106
+ } else {
107
+ promotedBlocks.push({
108
+ id: crypto.randomUUID(),
109
+ type: "text",
110
+ text: `</system-info>`
111
+ });
112
+ }
113
+ }
114
+ return {
115
+ text: textualOutput.join("\n"),
116
+ promotedMsg: createMsg({ name: "user", content: promotedBlocks, role: "user" })
117
+ };
118
+ }
119
+ };
120
+
121
+ // src/formatter/dashscope-chat-formatter.ts
122
+ var DashScopeChatFormatter = class extends FormatterBase {
123
+ promoteMultimodalToolResult;
124
+ /**
125
+ * Initialize a DashScopeChatFormatter instance.
126
+ *
127
+ * @param promoteMultimodalToolResult - Since DashScope API doesn't support multimodal tool outputs, this option
128
+ * indicates whether to promote the multimodal tool results to the prompt messages, so that LLMs can see them.
129
+ * Note you should ensure your model supports the corresponding modalities.
130
+ * @param promoteMultimodalToolResult.promoteMultimodalToolResult
131
+ */
132
+ constructor({ promoteMultimodalToolResult = false } = {}) {
133
+ super();
134
+ this.promoteMultimodalToolResult = promoteMultimodalToolResult;
135
+ }
136
+ /**
137
+ * Format the input message objects into the required format by DashScope API.
138
+ *
139
+ * @param msgs - An array of Msg instances to be formatted.
140
+ * @param msgs.msgs
141
+ * @returns A promise that resolves to an array of formatted message objects.
142
+ */
143
+ async format({ msgs }) {
144
+ const formattedMsgs = [];
145
+ let index = 0;
146
+ while (index < msgs.length) {
147
+ const msg = msgs[index];
148
+ const formattedMsg = {
149
+ role: msg.role,
150
+ content: []
151
+ };
152
+ const cachedMsgs = [];
153
+ for (const block of getContentBlocks(msg)) {
154
+ switch (block.type) {
155
+ case "text":
156
+ formattedMsg.content.push(this._formatTextBlock(block));
157
+ break;
158
+ case "thinking":
159
+ break;
160
+ case "tool_call":
161
+ if (!formattedMsg.tool_calls) {
162
+ formattedMsg.tool_calls = [];
163
+ }
164
+ formattedMsg.tool_calls.push({
165
+ id: block.id,
166
+ type: "function",
167
+ function: {
168
+ name: block.name,
169
+ arguments: block.input
170
+ }
171
+ });
172
+ break;
173
+ case "tool_result":
174
+ const formattedToolResult = this.convertToolOutputToString(
175
+ block.output,
176
+ this.promoteMultimodalToolResult
177
+ );
178
+ cachedMsgs.push({
179
+ role: "tool",
180
+ tool_call_id: block.id,
181
+ name: block.name,
182
+ content: formattedToolResult.text
183
+ });
184
+ if (formattedToolResult.promotedMsg) {
185
+ msgs.splice(index + 1, 0, formattedToolResult.promotedMsg);
186
+ }
187
+ break;
188
+ case "data":
189
+ formattedMsg.content.push(...this._formatMultimodalBlock(block));
190
+ break;
191
+ }
192
+ }
193
+ if (formattedMsg.content.length > 0 || formattedMsg.tool_calls) {
194
+ formattedMsgs.push(formattedMsg);
195
+ }
196
+ if (cachedMsgs.length > 0) {
197
+ formattedMsgs.push(...cachedMsgs);
198
+ }
199
+ index++;
200
+ }
201
+ return formattedMsgs;
202
+ }
203
+ /**
204
+ * Format a text content block into the required format.
205
+ *
206
+ * @param block - The text content block to format.
207
+ * @returns An object representing the formatted text content.
208
+ */
209
+ _formatTextBlock(block) {
210
+ return { text: block.text };
211
+ }
212
+ /**
213
+ * Format a multimodal data block into the required format.
214
+ * In DashScope API, the local file paths should be prefixed with "file://". URLs are kept unchanged.
215
+ *
216
+ * @param block - The multimodal content block to format.
217
+ * @returns An object representing the formatted multimodal content.
218
+ */
219
+ _formatMultimodalBlock(block) {
220
+ const type = block.source.mediaType.split("/")[0];
221
+ if (!["image", "audio", "video"].includes(type)) {
222
+ console.log(
223
+ `Skip unsupported media type ${block.source.mediaType} in DashScopeChatFormatter. Only image, audio and video are supported.`
224
+ );
225
+ return [];
226
+ }
227
+ if (block.source.type === "url") {
228
+ return [{ [type]: block.source.url }];
229
+ }
230
+ return [
231
+ {
232
+ [type]: `data:${block.source.mediaType};base64,${block.source.data}`
233
+ }
234
+ ];
235
+ }
236
+ };
237
+
238
+ // src/formatter/deepseek-chat-formatter.ts
239
+ var DeepSeekChatFormatter = class extends FormatterBase {
240
+ promoteMultimodalToolResult;
241
+ /**
242
+ * Initializes a new instance of the DeepSeekChatFormatter class.
243
+ * @param root0
244
+ * @param root0.promoteMultimodalToolResult
245
+ */
246
+ constructor({ promoteMultimodalToolResult = false } = {}) {
247
+ super();
248
+ this.promoteMultimodalToolResult = promoteMultimodalToolResult;
249
+ }
250
+ /**
251
+ * Format the input messages into the structure expected by DeepSeek Chat Completions API.
252
+ * @param root0
253
+ * @param root0.msgs
254
+ * @returns An array of formatted message objects ready to be sent to the DeepSeek API.
255
+ */
256
+ async format({ msgs }) {
257
+ const formattedMsgs = [];
258
+ let index = 0;
259
+ while (index < msgs.length) {
260
+ const msg = msgs[index];
261
+ const formattedMsg = {
262
+ role: msg.role,
263
+ name: msg.name,
264
+ content: null
265
+ };
266
+ const content = [];
267
+ const cachedMsgs = [];
268
+ for (const block of getContentBlocks(msg)) {
269
+ switch (block.type) {
270
+ case "text":
271
+ content.push({
272
+ type: "text",
273
+ text: block.text
274
+ });
275
+ break;
276
+ case "thinking":
277
+ break;
278
+ case "tool_call":
279
+ if (!formattedMsg.tool_calls) {
280
+ formattedMsg.tool_calls = [];
281
+ }
282
+ formattedMsg.tool_calls.push({
283
+ id: block.id,
284
+ type: "function",
285
+ function: {
286
+ name: block.name,
287
+ arguments: block.input
288
+ }
289
+ });
290
+ break;
291
+ case "tool_result":
292
+ const formattedToolResult = this.convertToolOutputToString(
293
+ block.output,
294
+ this.promoteMultimodalToolResult
295
+ );
296
+ cachedMsgs.push({
297
+ role: "tool",
298
+ tool_call_id: block.id,
299
+ name: block.name,
300
+ content: formattedToolResult.text
301
+ });
302
+ if (formattedToolResult.promotedMsg?.content.length) {
303
+ msgs.splice(index + 1, 0, formattedToolResult.promotedMsg);
304
+ }
305
+ break;
306
+ case "data":
307
+ console.warn(
308
+ `DeepSeek models don't support multimodal data for now (2026-03), skip the data block in message content.`
309
+ );
310
+ break;
311
+ }
312
+ }
313
+ if (content.length > 0) {
314
+ formattedMsg.content = content;
315
+ }
316
+ if (formattedMsg.content || formattedMsg.tool_calls) {
317
+ formattedMsgs.push(formattedMsg);
318
+ }
319
+ if (cachedMsgs.length > 0) {
320
+ formattedMsgs.push(...cachedMsgs);
321
+ }
322
+ index++;
323
+ }
324
+ return formattedMsgs;
325
+ }
326
+ };
327
+
328
+ // src/formatter/ollama-chat-formatter.ts
329
+ var OllamaChatFormatter = class extends FormatterBase {
330
+ // eslint-disable-next-line jsdoc/require-returns
331
+ /**
332
+ * Format messages for Ollama API
333
+ * @param root0
334
+ * @param root0.msgs
335
+ */
336
+ async format({ msgs }) {
337
+ const formattedMsgs = [];
338
+ for (const msg of msgs) {
339
+ const formattedMsg = {
340
+ role: msg.role,
341
+ content: ""
342
+ };
343
+ const textContent = getTextContent(msg);
344
+ if (textContent) {
345
+ formattedMsg.content = textContent;
346
+ }
347
+ const toolCalls = getContentBlocks(msg, "tool_call");
348
+ if (toolCalls.length > 0) {
349
+ formattedMsg.tool_calls = toolCalls.map((toolCall) => ({
350
+ function: {
351
+ name: toolCall.name,
352
+ arguments: JSON.parse(toolCall.input)
353
+ }
354
+ }));
355
+ }
356
+ const toolResults = getContentBlocks(msg, "tool_result");
357
+ for (const toolResult of toolResults) {
358
+ const resultText = this.convertToolOutputToString(toolResult.output, false);
359
+ formattedMsgs.push({
360
+ role: "tool",
361
+ content: resultText.text
362
+ });
363
+ }
364
+ if (formattedMsg.content || formattedMsg.tool_calls) {
365
+ formattedMsgs.push(formattedMsg);
366
+ }
367
+ }
368
+ return formattedMsgs;
369
+ }
370
+ };
371
+
372
+ // src/formatter/openai-chat-formatter.ts
373
+ import { existsSync } from "fs";
374
+ import { readFile } from "fs/promises";
375
+ import { extname } from "path";
376
+ import { fileURLToPath } from "url";
377
+ var OpenAIChatFormatter = class extends FormatterBase {
378
+ promoteMultimodalToolResult;
379
+ /**
380
+ * Initializes a new instance of the OpenAIChatFormatter class.
381
+ * @param root0
382
+ * @param root0.promoteMultimodalToolResult
383
+ */
384
+ constructor({ promoteMultimodalToolResult = false } = {}) {
385
+ super();
386
+ this.promoteMultimodalToolResult = promoteMultimodalToolResult;
387
+ }
388
+ /**
389
+ * Format the input messages into OpenAI Chat Completions message format.
390
+ * @param root0
391
+ * @param root0.msgs
392
+ * @returns An array of formatted messages compatible with OpenAI Chat Completions API.
393
+ */
394
+ async format({ msgs }) {
395
+ const formattedMsgs = [];
396
+ let index = 0;
397
+ while (index < msgs.length) {
398
+ const msg = msgs[index];
399
+ const formattedMsg = {
400
+ role: msg.role,
401
+ name: msg.name,
402
+ content: null
403
+ };
404
+ const content = [];
405
+ const cachedMsgs = [];
406
+ for (const block of getContentBlocks(msg)) {
407
+ switch (block.type) {
408
+ case "text":
409
+ content.push(this._formatTextBlock(block));
410
+ break;
411
+ case "thinking":
412
+ break;
413
+ case "tool_call":
414
+ if (!formattedMsg.tool_calls) {
415
+ formattedMsg.tool_calls = [];
416
+ }
417
+ formattedMsg.tool_calls.push({
418
+ id: block.id,
419
+ type: "function",
420
+ function: {
421
+ name: block.name,
422
+ arguments: block.input
423
+ }
424
+ });
425
+ break;
426
+ case "tool_result":
427
+ const formattedToolResult = this.convertToolOutputToString(
428
+ block.output,
429
+ this.promoteMultimodalToolResult
430
+ );
431
+ cachedMsgs.push({
432
+ role: "tool",
433
+ tool_call_id: block.id,
434
+ name: block.name,
435
+ content: formattedToolResult.text
436
+ });
437
+ if (formattedToolResult.promotedMsg?.content.length) {
438
+ msgs.splice(index + 1, 0, formattedToolResult.promotedMsg);
439
+ }
440
+ break;
441
+ case "data":
442
+ content.push(
443
+ ...await this._formatMultimodalBlock({ block, role: msg.role })
444
+ );
445
+ break;
446
+ }
447
+ }
448
+ if (content.length > 0) {
449
+ formattedMsg.content = content;
450
+ }
451
+ if (formattedMsg.content || formattedMsg.tool_calls) {
452
+ formattedMsgs.push(formattedMsg);
453
+ }
454
+ if (cachedMsgs.length > 0) {
455
+ formattedMsgs.push(...cachedMsgs);
456
+ }
457
+ index++;
458
+ }
459
+ return formattedMsgs;
460
+ }
461
+ /**
462
+ * Format a text block into OpenAI Chat Completions message content format.
463
+ * @param block
464
+ * @returns An object representing the formatted text block.
465
+ */
466
+ _formatTextBlock(block) {
467
+ return {
468
+ type: "text",
469
+ text: block.text
470
+ };
471
+ }
472
+ /**
473
+ * Format a multimodal data block into OpenAI Chat Completions message content format.
474
+ * @param root0
475
+ * @param root0.block
476
+ * @param root0.role
477
+ * @returns The formatted content blocks
478
+ */
479
+ async _formatMultimodalBlock({
480
+ block,
481
+ role
482
+ }) {
483
+ const type = block.source.mediaType.split("/")[0];
484
+ if (type === "image") {
485
+ return [
486
+ {
487
+ type: "image_url",
488
+ image_url: {
489
+ url: await this._toOpenAIImageURL(block)
490
+ }
491
+ }
492
+ ];
493
+ }
494
+ if (type === "audio") {
495
+ if (role === "assistant") {
496
+ return [];
497
+ }
498
+ return [
499
+ {
500
+ type: "input_audio",
501
+ input_audio: await this._toOpenAIAudioData(block)
502
+ }
503
+ ];
504
+ }
505
+ console.log(
506
+ `Skip unsupported media type ${block.source.mediaType} in OpenAIChatFormatter. Only image and audio are supported.`
507
+ );
508
+ return [];
509
+ }
510
+ /**
511
+ * Convert the data block to an OpenAI compatible image URL.
512
+ * @param block
513
+ * @returns A promise that resolves to a string representing the image URL in a format compatible with OpenAI Chat Completions API.
514
+ */
515
+ async _toOpenAIImageURL(block) {
516
+ if (block.source.type === "base64") {
517
+ return `data:${block.source.mediaType};base64,${block.source.data}`;
518
+ }
519
+ const sourceUrl = block.source.url;
520
+ if (sourceUrl.startsWith("http://") || sourceUrl.startsWith("https://")) {
521
+ return sourceUrl;
522
+ }
523
+ if (sourceUrl.startsWith("data:")) {
524
+ return sourceUrl;
525
+ }
526
+ const localPath = this._toLocalPath(sourceUrl);
527
+ if (!localPath || !existsSync(localPath)) {
528
+ throw new Error(`Image path not found: ${sourceUrl}`);
529
+ }
530
+ const ext = extname(localPath).toLowerCase();
531
+ const supportedImageExtensions = [".png", ".jpg", ".jpeg", ".gif", ".webp"];
532
+ if (!supportedImageExtensions.includes(ext)) {
533
+ throw new TypeError(
534
+ `Unsupported image extension: ${ext}. Supported: ${supportedImageExtensions.join(", ")}`
535
+ );
536
+ }
537
+ const file = await readFile(localPath);
538
+ const mime = block.source.mediaType || `image/${ext.slice(1)}`;
539
+ return `data:${mime};base64,${file.toString("base64")}`;
540
+ }
541
+ /**
542
+ * Converts a data block to OpenAI compatible audio data format.
543
+ *
544
+ * @param block - The data block containing audio information.
545
+ * @returns A promise that resolves to an object with audio data and format.
546
+ */
547
+ async _toOpenAIAudioData(block) {
548
+ const supportedMediaTypes = /* @__PURE__ */ new Map([
549
+ ["audio/wav", "wav"],
550
+ ["audio/mp3", "mp3"],
551
+ ["audio/mpeg", "mp3"]
552
+ ]);
553
+ if (block.source.type === "base64") {
554
+ const format2 = supportedMediaTypes.get(block.source.mediaType);
555
+ if (!format2) {
556
+ throw new TypeError(
557
+ `Unsupported audio media type: ${block.source.mediaType}, only audio/wav and audio/mp3 are supported.`
558
+ );
559
+ }
560
+ return { data: block.source.data, format: format2 };
561
+ }
562
+ const sourceUrl = block.source.url;
563
+ const localPath = this._toLocalPath(sourceUrl);
564
+ let data;
565
+ if (localPath && existsSync(localPath)) {
566
+ const file = await readFile(localPath);
567
+ data = file.toString("base64");
568
+ } else if (sourceUrl.startsWith("http://") || sourceUrl.startsWith("https://")) {
569
+ const response = await fetch(sourceUrl);
570
+ if (!response.ok) {
571
+ throw new Error(
572
+ `Failed to fetch audio from URL: ${sourceUrl} (${response.status})`
573
+ );
574
+ }
575
+ const arr = await response.arrayBuffer();
576
+ data = Buffer.from(arr).toString("base64");
577
+ } else {
578
+ throw new Error(
579
+ `Unsupported audio source: ${sourceUrl}, it should be a local file path, file URL, or an HTTP URL.`
580
+ );
581
+ }
582
+ const ext = extname(localPath || sourceUrl).toLowerCase();
583
+ const extToFormat = /* @__PURE__ */ new Map([
584
+ [".wav", "wav"],
585
+ [".mp3", "mp3"]
586
+ ]);
587
+ const format = extToFormat.get(ext);
588
+ if (!format) {
589
+ throw new TypeError(`Unsupported audio extension: ${ext}, wav and mp3 are supported.`);
590
+ }
591
+ return { data, format };
592
+ }
593
+ /**
594
+ * Converts a URL or path to a local file path.
595
+ *
596
+ * @param urlOrPath - The URL or path to convert.
597
+ * @returns The local file path, or null if not a local path.
598
+ */
599
+ _toLocalPath(urlOrPath) {
600
+ if (urlOrPath.startsWith("file://")) {
601
+ return fileURLToPath(urlOrPath);
602
+ }
603
+ if (!urlOrPath.includes("://")) {
604
+ return urlOrPath;
605
+ }
606
+ return null;
607
+ }
608
+ };
609
+ export {
610
+ DashScopeChatFormatter,
611
+ DeepSeekChatFormatter,
612
+ FormatterBase,
613
+ OllamaChatFormatter,
614
+ OpenAIChatFormatter
615
+ };
616
+ //# sourceMappingURL=index.mjs.map