@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,372 @@
1
+ import { createMsg } from '../message';
2
+ import { DashScopeChatFormatter } from './dashscope-chat-formatter';
3
+
4
+ describe('DashScopeChatFormatter', () => {
5
+ test('format textual messages', async () => {
6
+ const msgs = [
7
+ createMsg({
8
+ name: 'system',
9
+ content: [
10
+ { id: crypto.randomUUID(), type: 'text', text: 'You are a helpful assistant.' },
11
+ ],
12
+ role: 'system',
13
+ }),
14
+ createMsg({
15
+ name: 'user',
16
+ content: [{ id: crypto.randomUUID(), type: 'text', text: 'Hello, how are you?' }],
17
+ role: 'user',
18
+ }),
19
+ createMsg({
20
+ name: 'assistant',
21
+ content: [{ id: crypto.randomUUID(), type: 'text', text: 'I am fine, thank you!' }],
22
+ role: 'assistant',
23
+ }),
24
+ ];
25
+
26
+ const formatter = new DashScopeChatFormatter();
27
+ const res = await formatter.format({ msgs });
28
+ expect(res).toEqual([
29
+ {
30
+ role: 'system',
31
+ content: [{ text: 'You are a helpful assistant.' }],
32
+ },
33
+ {
34
+ role: 'user',
35
+ content: [{ text: 'Hello, how are you?' }],
36
+ },
37
+ {
38
+ role: 'assistant',
39
+ content: [{ text: 'I am fine, thank you!' }],
40
+ },
41
+ ]);
42
+ });
43
+
44
+ test('format tool messages', async () => {
45
+ const msgs = [
46
+ createMsg({
47
+ name: 'system',
48
+ content: [
49
+ { type: 'text', text: 'You are a helpful assistant.', id: crypto.randomUUID() },
50
+ ],
51
+ role: 'system',
52
+ }),
53
+ createMsg({
54
+ name: 'user',
55
+ content: [{ type: 'text', text: 'Please use the tool.', id: crypto.randomUUID() }],
56
+ role: 'user',
57
+ }),
58
+ createMsg({
59
+ name: 'assistant',
60
+ content: [
61
+ {
62
+ type: 'tool_call',
63
+ id: '1',
64
+ name: 'google_search',
65
+ input: '{"query": "example1"}',
66
+ },
67
+ {
68
+ type: 'tool_call',
69
+ id: '2',
70
+ name: 'bing_search',
71
+ input: '{"query": "example2"}',
72
+ },
73
+ {
74
+ type: 'tool_result',
75
+ id: '1',
76
+ name: 'google_search',
77
+ output: 'Google search result for example1',
78
+ state: 'success',
79
+ },
80
+ {
81
+ type: 'tool_result',
82
+ id: '2',
83
+ name: 'bing_search',
84
+ output: 'Bing search result for example2',
85
+ state: 'success',
86
+ },
87
+ ],
88
+ role: 'assistant',
89
+ }),
90
+ ];
91
+
92
+ const formatter = new DashScopeChatFormatter();
93
+
94
+ const res = await formatter.format({ msgs });
95
+ expect(res).toEqual([
96
+ {
97
+ role: 'system',
98
+ content: [{ text: 'You are a helpful assistant.' }],
99
+ },
100
+ {
101
+ role: 'user',
102
+ content: [{ text: 'Please use the tool.' }],
103
+ },
104
+ {
105
+ role: 'assistant',
106
+ content: [],
107
+ tool_calls: [
108
+ {
109
+ id: '1',
110
+ type: 'function',
111
+ function: {
112
+ name: 'google_search',
113
+ arguments: '{"query": "example1"}',
114
+ },
115
+ },
116
+ {
117
+ id: '2',
118
+ type: 'function',
119
+ function: {
120
+ name: 'bing_search',
121
+ arguments: '{"query": "example2"}',
122
+ },
123
+ },
124
+ ],
125
+ },
126
+ {
127
+ role: 'tool',
128
+ tool_call_id: '1',
129
+ name: 'google_search',
130
+ content: 'Google search result for example1',
131
+ },
132
+ {
133
+ role: 'tool',
134
+ tool_call_id: '2',
135
+ name: 'bing_search',
136
+ content: 'Bing search result for example2',
137
+ },
138
+ ]);
139
+ });
140
+
141
+ test('format multimodal messages', async () => {
142
+ const msgs = [
143
+ createMsg({
144
+ name: 'system',
145
+ content: [
146
+ { id: crypto.randomUUID(), type: 'text', text: 'You are a helpful assistant.' },
147
+ ],
148
+ role: 'system',
149
+ }),
150
+ createMsg({
151
+ name: 'user',
152
+ content: [
153
+ { id: crypto.randomUUID(), type: 'text', text: 'Please see the image below.' },
154
+ {
155
+ id: crypto.randomUUID(),
156
+ type: 'data',
157
+ source: {
158
+ type: 'url',
159
+ url: 'https://example.com/image.png',
160
+ mediaType: 'image/png',
161
+ },
162
+ },
163
+ ],
164
+ role: 'user',
165
+ }),
166
+ createMsg({
167
+ name: 'assistant',
168
+ content: [
169
+ {
170
+ id: crypto.randomUUID(),
171
+ type: 'text',
172
+ text: 'Here is the image you requested.',
173
+ },
174
+ ],
175
+ role: 'assistant',
176
+ }),
177
+ createMsg({
178
+ name: 'user',
179
+ content: [
180
+ {
181
+ id: crypto.randomUUID(),
182
+ type: 'data',
183
+ source: { type: 'base64', data: 'xxx', mediaType: 'audio/mp3' },
184
+ },
185
+ {
186
+ id: crypto.randomUUID(),
187
+ type: 'data',
188
+ source: {
189
+ type: 'url',
190
+ url: 'file:///local/path/to/video.mp4',
191
+ mediaType: 'video/mp4',
192
+ },
193
+ },
194
+ {
195
+ id: crypto.randomUUID(),
196
+ type: 'data',
197
+ source: {
198
+ type: 'url',
199
+ url: 'file:///C:/local/path/to/image.jpg',
200
+ mediaType: 'image/jpg',
201
+ },
202
+ },
203
+ ],
204
+ role: 'user',
205
+ }),
206
+ ];
207
+
208
+ const formatter = new DashScopeChatFormatter();
209
+
210
+ const res = await formatter.format({ msgs });
211
+ expect(res).toEqual([
212
+ {
213
+ role: 'system',
214
+ content: [{ text: 'You are a helpful assistant.' }],
215
+ },
216
+ {
217
+ role: 'user',
218
+ content: [
219
+ { text: 'Please see the image below.' },
220
+ {
221
+ image: 'https://example.com/image.png',
222
+ },
223
+ ],
224
+ },
225
+ {
226
+ role: 'assistant',
227
+ content: [{ text: 'Here is the image you requested.' }],
228
+ },
229
+ {
230
+ role: 'user',
231
+ content: [
232
+ {
233
+ audio: 'data:audio/mp3;base64,xxx',
234
+ },
235
+ {
236
+ video: 'file:///local/path/to/video.mp4',
237
+ },
238
+ {
239
+ image: 'file:///C:/local/path/to/image.jpg',
240
+ },
241
+ ],
242
+ },
243
+ ]);
244
+ });
245
+
246
+ test('format multimodal tool results', async () => {
247
+ // Mock Math.random to generate predictable IDs
248
+ const mockRandom = jest.spyOn(Math, 'random');
249
+ mockRandom.mockReturnValueOnce(0.123456789); // Will generate '4fzzzxjy'
250
+ mockRandom.mockReturnValueOnce(0.456789012); // Will generate 'gfzy4slm'
251
+
252
+ const msgs = [
253
+ createMsg({
254
+ name: 'user',
255
+ content: [{ id: crypto.randomUUID(), type: 'text', text: 'Please use the tool.' }],
256
+ role: 'user',
257
+ }),
258
+ createMsg({
259
+ name: 'assistant',
260
+ content: [
261
+ {
262
+ type: 'tool_call',
263
+ id: '1',
264
+ name: 'google_search',
265
+ input: '{\"query\": \"example1\"}',
266
+ },
267
+ {
268
+ type: 'tool_result',
269
+ id: '1',
270
+ name: 'google_search',
271
+ output: [
272
+ { type: 'text', text: 'content 1', id: crypto.randomUUID() },
273
+ {
274
+ id: crypto.randomUUID(),
275
+ type: 'data',
276
+ source: {
277
+ type: 'url',
278
+ url: 'https://example.com/image1.png',
279
+ mediaType: 'image/png',
280
+ },
281
+ },
282
+ { id: crypto.randomUUID(), type: 'text', text: 'content 2' },
283
+ {
284
+ id: crypto.randomUUID(),
285
+ type: 'data',
286
+ source: { type: 'base64', data: 'xxx', mediaType: 'image/png' },
287
+ },
288
+ {
289
+ id: crypto.randomUUID(),
290
+ type: 'data',
291
+ source: { type: 'base64', data: 'yyy', mediaType: 'audio/mp3' },
292
+ },
293
+ {
294
+ id: crypto.randomUUID(),
295
+ type: 'data',
296
+ source: {
297
+ type: 'url',
298
+ url: '/local/path/to/video1.mp4',
299
+ mediaType: 'video/mp4',
300
+ },
301
+ },
302
+ ],
303
+ state: 'success',
304
+ },
305
+ ],
306
+ role: 'assistant',
307
+ }),
308
+ ];
309
+
310
+ const formatter = new DashScopeChatFormatter({ promoteMultimodalToolResult: true });
311
+
312
+ const res = await formatter.format({ msgs });
313
+
314
+ // Restore the mock
315
+ mockRandom.mockRestore();
316
+
317
+ expect(res).toEqual([
318
+ {
319
+ content: [
320
+ {
321
+ text: 'Please use the tool.',
322
+ },
323
+ ],
324
+ role: 'user',
325
+ },
326
+ {
327
+ content: [],
328
+ role: 'assistant',
329
+ tool_calls: [
330
+ {
331
+ function: {
332
+ arguments: '{"query": "example1"}',
333
+ name: 'google_search',
334
+ },
335
+ id: '1',
336
+ type: 'function',
337
+ },
338
+ ],
339
+ },
340
+ {
341
+ content:
342
+ "content 1\n<system-info>One returned image can be found at: https://example.com/image1.png</system-info>\ncontent 2\n<system-info>One returned image is embedded with ID '4fzzzxjy' and will be attached within '<system-info></system-info>' tags later.</system-info>\n<system-info>One returned audio is embedded with ID 'gfzy4slm' and will be attached within '<system-info></system-info>' tags later.</system-info>\n<system-info>One returned video can be found at: /local/path/to/video1.mp4</system-info>",
343
+ name: 'google_search',
344
+ role: 'tool',
345
+ tool_call_id: '1',
346
+ },
347
+ {
348
+ content: [
349
+ {
350
+ text: "<system-info>The multimodal contents returned from the tool call are as follows:\n<image_data id='4fzzzxjy'>",
351
+ },
352
+ {
353
+ image: 'data:image/png;base64,xxx',
354
+ },
355
+ {
356
+ text: '</image_data>\n',
357
+ },
358
+ {
359
+ text: "<audio_data id='gfzy4slm'>",
360
+ },
361
+ {
362
+ audio: 'data:audio/mp3;base64,yyy',
363
+ },
364
+ {
365
+ text: '</audio_data>\n</system-info>',
366
+ },
367
+ ],
368
+ role: 'user',
369
+ },
370
+ ]);
371
+ });
372
+ });
@@ -0,0 +1,163 @@
1
+ import { FormatterBase } from './base';
2
+ import { Msg, TextBlock, getContentBlocks } from '../message';
3
+ import { DataBlock } from '../message';
4
+
5
+ interface DashScopeFormatterOptions {
6
+ /**
7
+ * Since DashScope API doesn't support multimodal tool outputs, this option indicates whether to
8
+ * promote the multimodal tool results to the prompt messages, so that LLMs can see them.
9
+ * Note you should ensure your model supports the corresponding modalities.
10
+ */
11
+ promoteMultimodalToolResult?:
12
+ | {
13
+ image?: boolean;
14
+ audio?: boolean;
15
+ video?: boolean;
16
+ }
17
+ | boolean;
18
+ }
19
+
20
+ /**
21
+ *
22
+ */
23
+ export class DashScopeChatFormatter extends FormatterBase {
24
+ private promoteMultimodalToolResult:
25
+ | { image?: boolean; audio?: boolean; video?: boolean }
26
+ | boolean;
27
+
28
+ /**
29
+ * Initialize a DashScopeChatFormatter instance.
30
+ *
31
+ * @param promoteMultimodalToolResult - Since DashScope API doesn't support multimodal tool outputs, this option
32
+ * indicates whether to promote the multimodal tool results to the prompt messages, so that LLMs can see them.
33
+ * Note you should ensure your model supports the corresponding modalities.
34
+ * @param promoteMultimodalToolResult.promoteMultimodalToolResult
35
+ */
36
+ constructor({ promoteMultimodalToolResult = false }: DashScopeFormatterOptions = {}) {
37
+ super();
38
+ this.promoteMultimodalToolResult = promoteMultimodalToolResult;
39
+ }
40
+
41
+ /**
42
+ * Format the input message objects into the required format by DashScope API.
43
+ *
44
+ * @param msgs - An array of Msg instances to be formatted.
45
+ * @param msgs.msgs
46
+ * @returns A promise that resolves to an array of formatted message objects.
47
+ */
48
+ async format({ msgs }: { msgs: Array<Msg> }): Promise<Record<string, unknown>[]> {
49
+ const formattedMsgs: Array<Record<string, unknown>> = [];
50
+ let index = 0;
51
+ while (index < msgs.length) {
52
+ const msg = msgs[index];
53
+ const formattedMsg: {
54
+ role: string;
55
+ content: Record<string, unknown>[];
56
+ tool_calls?: {
57
+ id: string;
58
+ type: 'function';
59
+ function: {
60
+ name: string;
61
+ arguments: string;
62
+ };
63
+ }[];
64
+ } = {
65
+ role: msg.role,
66
+ content: [],
67
+ };
68
+
69
+ // The cached messages that should be pushed after the current message, to keep the order of messages correct.
70
+ const cachedMsgs = [];
71
+ for (const block of getContentBlocks(msg)) {
72
+ switch (block.type) {
73
+ case 'text':
74
+ formattedMsg.content.push(this._formatTextBlock(block));
75
+ break;
76
+ case 'thinking':
77
+ break;
78
+ case 'tool_call':
79
+ if (!formattedMsg.tool_calls) {
80
+ formattedMsg.tool_calls = [];
81
+ }
82
+ formattedMsg.tool_calls.push({
83
+ id: block.id,
84
+ type: 'function',
85
+ function: {
86
+ name: block.name,
87
+ arguments: block.input,
88
+ },
89
+ });
90
+ break;
91
+ case 'tool_result':
92
+ const formattedToolResult = this.convertToolOutputToString(
93
+ block.output,
94
+ this.promoteMultimodalToolResult
95
+ );
96
+
97
+ cachedMsgs.push({
98
+ role: 'tool',
99
+ tool_call_id: block.id,
100
+ name: block.name,
101
+ content: formattedToolResult.text,
102
+ });
103
+ if (formattedToolResult.promotedMsg) {
104
+ // Insert the promoted message into the array as the next message to be processed
105
+ msgs.splice(index + 1, 0, formattedToolResult.promotedMsg);
106
+ }
107
+ break;
108
+ case 'data':
109
+ formattedMsg.content.push(...this._formatMultimodalBlock(block));
110
+ break;
111
+ }
112
+ }
113
+ if (formattedMsg.content.length > 0 || formattedMsg.tool_calls) {
114
+ formattedMsgs.push(formattedMsg);
115
+ }
116
+ if (cachedMsgs.length > 0) {
117
+ formattedMsgs.push(...cachedMsgs);
118
+ }
119
+
120
+ // Process next message
121
+ index++;
122
+ }
123
+ return formattedMsgs;
124
+ }
125
+
126
+ /**
127
+ * Format a text content block into the required format.
128
+ *
129
+ * @param block - The text content block to format.
130
+ * @returns An object representing the formatted text content.
131
+ */
132
+ _formatTextBlock(block: TextBlock) {
133
+ return { text: block.text };
134
+ }
135
+
136
+ /**
137
+ * Format a multimodal data block into the required format.
138
+ * In DashScope API, the local file paths should be prefixed with "file://". URLs are kept unchanged.
139
+ *
140
+ * @param block - The multimodal content block to format.
141
+ * @returns An object representing the formatted multimodal content.
142
+ */
143
+ _formatMultimodalBlock(block: DataBlock) {
144
+ const type = block.source.mediaType.split('/')[0];
145
+
146
+ if (!['image', 'audio', 'video'].includes(type)) {
147
+ console.log(
148
+ `Skip unsupported media type ${block.source.mediaType} in DashScopeChatFormatter. Only image, audio and video are supported.`
149
+ );
150
+ return [];
151
+ }
152
+
153
+ if (block.source.type === 'url') {
154
+ return [{ [type]: block.source.url }];
155
+ }
156
+
157
+ return [
158
+ {
159
+ [type]: `data:${block.source.mediaType};base64,${block.source.data}`,
160
+ },
161
+ ];
162
+ }
163
+ }
@@ -0,0 +1,130 @@
1
+ import { FormatterBase } from './base';
2
+ import { Msg, getContentBlocks } from '../message';
3
+
4
+ interface DeepSeekFormatterOptions {
5
+ /**
6
+ * Most LLM APIs don't support multimodal tool outputs, this option controls whether to
7
+ * promote multimodal tool results to follow-up user messages.
8
+ */
9
+ promoteMultimodalToolResult?:
10
+ | {
11
+ image?: boolean;
12
+ audio?: boolean;
13
+ video?: boolean;
14
+ }
15
+ | boolean;
16
+ }
17
+
18
+ /**
19
+ * Format AgentScope message objects into DeepSeek Chat Completions message format.
20
+ */
21
+ export class DeepSeekChatFormatter extends FormatterBase {
22
+ private promoteMultimodalToolResult:
23
+ | { image?: boolean; audio?: boolean; video?: boolean }
24
+ | boolean;
25
+
26
+ /**
27
+ * Initializes a new instance of the DeepSeekChatFormatter class.
28
+ * @param root0
29
+ * @param root0.promoteMultimodalToolResult
30
+ */
31
+ constructor({ promoteMultimodalToolResult = false }: DeepSeekFormatterOptions = {}) {
32
+ super();
33
+ this.promoteMultimodalToolResult = promoteMultimodalToolResult;
34
+ }
35
+
36
+ /**
37
+ * Format the input messages into the structure expected by DeepSeek Chat Completions API.
38
+ * @param root0
39
+ * @param root0.msgs
40
+ * @returns An array of formatted message objects ready to be sent to the DeepSeek API.
41
+ */
42
+ async format({ msgs }: { msgs: Array<Msg> }): Promise<Record<string, unknown>[]> {
43
+ const formattedMsgs: Array<Record<string, unknown>> = [];
44
+ let index = 0;
45
+
46
+ while (index < msgs.length) {
47
+ const msg = msgs[index];
48
+ const formattedMsg: {
49
+ role: string;
50
+ name: string;
51
+ content: Record<string, unknown>[] | null;
52
+ tool_calls?: {
53
+ id: string;
54
+ type: 'function';
55
+ function: {
56
+ name: string;
57
+ arguments: string;
58
+ };
59
+ }[];
60
+ } = {
61
+ role: msg.role,
62
+ name: msg.name,
63
+ content: null,
64
+ };
65
+ const content: Record<string, unknown>[] = [];
66
+
67
+ // Cache tool-result messages to keep the sequence right after current message.
68
+ const cachedMsgs: Record<string, unknown>[] = [];
69
+ for (const block of getContentBlocks(msg)) {
70
+ switch (block.type) {
71
+ case 'text':
72
+ content.push({
73
+ type: 'text',
74
+ text: block.text,
75
+ });
76
+ break;
77
+ case 'thinking':
78
+ break;
79
+ case 'tool_call':
80
+ if (!formattedMsg.tool_calls) {
81
+ formattedMsg.tool_calls = [];
82
+ }
83
+ formattedMsg.tool_calls.push({
84
+ id: block.id,
85
+ type: 'function',
86
+ function: {
87
+ name: block.name,
88
+ arguments: block.input,
89
+ },
90
+ });
91
+ break;
92
+ case 'tool_result':
93
+ const formattedToolResult = this.convertToolOutputToString(
94
+ block.output,
95
+ this.promoteMultimodalToolResult
96
+ );
97
+ cachedMsgs.push({
98
+ role: 'tool',
99
+ tool_call_id: block.id,
100
+ name: block.name,
101
+ content: formattedToolResult.text,
102
+ });
103
+ if (formattedToolResult.promotedMsg?.content.length) {
104
+ msgs.splice(index + 1, 0, formattedToolResult.promotedMsg);
105
+ }
106
+ break;
107
+ case 'data':
108
+ console.warn(
109
+ `DeepSeek models don't support multimodal data for now (2026-03), skip the data block in message content.`
110
+ );
111
+ break;
112
+ }
113
+ }
114
+
115
+ if (content.length > 0) {
116
+ formattedMsg.content = content;
117
+ }
118
+ if (formattedMsg.content || formattedMsg.tool_calls) {
119
+ formattedMsgs.push(formattedMsg);
120
+ }
121
+ if (cachedMsgs.length > 0) {
122
+ formattedMsgs.push(...cachedMsgs);
123
+ }
124
+
125
+ index++;
126
+ }
127
+
128
+ return formattedMsgs;
129
+ }
130
+ }
@@ -0,0 +1,5 @@
1
+ export { FormatterBase } from './base';
2
+ export { DashScopeChatFormatter } from './dashscope-chat-formatter';
3
+ export { DeepSeekChatFormatter } from './deepseek-chat-formatter';
4
+ export { OllamaChatFormatter } from './ollama-chat-formatter';
5
+ export { OpenAIChatFormatter } from './openai-chat-formatter';