@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.
- package/dist/agent/index.d.mts +234 -0
- package/dist/agent/index.d.ts +234 -0
- package/dist/agent/index.js +1412 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/index.mjs +1375 -0
- package/dist/agent/index.mjs.map +1 -0
- package/dist/base-BOx3UzOl.d.mts +41 -0
- package/dist/base-BoIps2RL.d.ts +41 -0
- package/dist/base-C7jwyH4Z.d.mts +52 -0
- package/dist/base-Cwi4bjze.d.ts +127 -0
- package/dist/base-DYlBMCy_.d.mts +127 -0
- package/dist/base-NX-knWOv.d.ts +52 -0
- package/dist/block-VsnHrllL.d.mts +48 -0
- package/dist/block-VsnHrllL.d.ts +48 -0
- package/dist/event/index.d.mts +181 -0
- package/dist/event/index.d.ts +181 -0
- package/dist/event/index.js +58 -0
- package/dist/event/index.js.map +1 -0
- package/dist/event/index.mjs +33 -0
- package/dist/event/index.mjs.map +1 -0
- package/dist/formatter/index.d.mts +187 -0
- package/dist/formatter/index.d.ts +187 -0
- package/dist/formatter/index.js +647 -0
- package/dist/formatter/index.js.map +1 -0
- package/dist/formatter/index.mjs +616 -0
- package/dist/formatter/index.mjs.map +1 -0
- package/dist/index-BTJDlKvQ.d.mts +195 -0
- package/dist/index-BcatlwXQ.d.ts +195 -0
- package/dist/index-CAxQAkiP.d.mts +21 -0
- package/dist/index-CAxQAkiP.d.ts +21 -0
- package/dist/mcp/index.d.mts +9 -0
- package/dist/mcp/index.d.ts +9 -0
- package/dist/mcp/index.js +432 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/index.mjs +408 -0
- package/dist/mcp/index.mjs.map +1 -0
- package/dist/message/index.d.mts +10 -0
- package/dist/message/index.d.ts +10 -0
- package/dist/message/index.js +67 -0
- package/dist/message/index.js.map +1 -0
- package/dist/message/index.mjs +37 -0
- package/dist/message/index.mjs.map +1 -0
- package/dist/message-CkN21KaY.d.mts +99 -0
- package/dist/message-CzLeTlua.d.ts +99 -0
- package/dist/model/index.d.mts +377 -0
- package/dist/model/index.d.ts +377 -0
- package/dist/model/index.js +1880 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/index.mjs +1849 -0
- package/dist/model/index.mjs.map +1 -0
- package/dist/storage/index.d.mts +68 -0
- package/dist/storage/index.d.ts +68 -0
- package/dist/storage/index.js +250 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/index.mjs +212 -0
- package/dist/storage/index.mjs.map +1 -0
- package/dist/tool/index.d.mts +311 -0
- package/dist/tool/index.d.ts +311 -0
- package/dist/tool/index.js +1494 -0
- package/dist/tool/index.js.map +1 -0
- package/dist/tool/index.mjs +1447 -0
- package/dist/tool/index.mjs.map +1 -0
- package/dist/toolkit-CEpulFi0.d.ts +99 -0
- package/dist/toolkit-CGEZSZPa.d.mts +99 -0
- package/jest.config.js +11 -0
- package/package.json +92 -0
- package/src/_utils/common.ts +104 -0
- package/src/_utils/index.ts +1 -0
- package/src/agent/agent-base.ts +0 -0
- package/src/agent/agent.test.ts +1028 -0
- package/src/agent/agent.ts +1032 -0
- package/src/agent/index.ts +2 -0
- package/src/agent/interfaces.ts +23 -0
- package/src/agent/test-compression.ts +72 -0
- package/src/event/index.ts +250 -0
- package/src/formatter/base.ts +133 -0
- package/src/formatter/dashscope-chat-formatter.test.ts +372 -0
- package/src/formatter/dashscope-chat-formatter.ts +163 -0
- package/src/formatter/deepseek-chat-formatter.ts +130 -0
- package/src/formatter/index.ts +5 -0
- package/src/formatter/ollama-chat-formatter.ts +67 -0
- package/src/formatter/openai-chat-formatter.test.ts +263 -0
- package/src/formatter/openai-chat-formatter.ts +301 -0
- package/src/formatter/openai.md +767 -0
- package/src/mcp/base.ts +114 -0
- package/src/mcp/http.test.ts +303 -0
- package/src/mcp/http.ts +224 -0
- package/src/mcp/index.ts +2 -0
- package/src/mcp/stdio.test.ts +91 -0
- package/src/mcp/stdio.ts +119 -0
- package/src/message/block.ts +60 -0
- package/src/message/enums.ts +4 -0
- package/src/message/index.ts +12 -0
- package/src/message/message.test.ts +80 -0
- package/src/message/message.ts +131 -0
- package/src/model/base.ts +226 -0
- package/src/model/dashscope-model.test.ts +335 -0
- package/src/model/dashscope-model.ts +441 -0
- package/src/model/deepseek-model.test.ts +279 -0
- package/src/model/deepseek-model.ts +401 -0
- package/src/model/index.ts +7 -0
- package/src/model/ollama-model.test.ts +307 -0
- package/src/model/ollama-model.ts +356 -0
- package/src/model/openai-model.ts +327 -0
- package/src/model/response.ts +22 -0
- package/src/model/usage.ts +12 -0
- package/src/storage/base.ts +52 -0
- package/src/storage/file-system.test.ts +587 -0
- package/src/storage/file-system.ts +269 -0
- package/src/storage/index.ts +2 -0
- package/src/tool/base.ts +23 -0
- package/src/tool/bash.test.ts +174 -0
- package/src/tool/bash.ts +152 -0
- package/src/tool/edit.test.ts +83 -0
- package/src/tool/edit.ts +95 -0
- package/src/tool/glob.test.ts +63 -0
- package/src/tool/glob.ts +166 -0
- package/src/tool/grep.test.ts +74 -0
- package/src/tool/grep.ts +256 -0
- package/src/tool/index.ts +10 -0
- package/src/tool/read.test.ts +77 -0
- package/src/tool/read.ts +117 -0
- package/src/tool/response.ts +82 -0
- package/src/tool/task.test.ts +299 -0
- package/src/tool/task.ts +399 -0
- package/src/tool/toolkit.test.ts +636 -0
- package/src/tool/toolkit.ts +601 -0
- package/src/tool/write.test.ts +52 -0
- package/src/tool/write.ts +57 -0
- package/src/type/index.ts +52 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.cjs.json +11 -0
- package/tsconfig.esm.json +10 -0
- package/tsconfig.json +14 -0
- package/tsup.config.ts +20 -0
- package/typedoc.json +52 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
import { createToolResponse } from './response';
|
|
7
|
+
import { Toolkit } from './toolkit';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A sample test tool function.
|
|
11
|
+
* @param input
|
|
12
|
+
* @param input.a
|
|
13
|
+
* @param input.b
|
|
14
|
+
* @returns ToolResponse
|
|
15
|
+
*/
|
|
16
|
+
function testFunction(input: { a: string; b: number }) {
|
|
17
|
+
return createToolResponse({
|
|
18
|
+
content: [
|
|
19
|
+
{ type: 'text', text: `Received a=${input.a}, b=${input.b}`, id: crypto.randomUUID() },
|
|
20
|
+
],
|
|
21
|
+
state: 'success',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('Toolkit', () => {
|
|
26
|
+
test('Register and execute tool function with ZodObject schema', async () => {
|
|
27
|
+
const toolkit = new Toolkit();
|
|
28
|
+
toolkit.registerToolFunction({
|
|
29
|
+
call: testFunction,
|
|
30
|
+
name: 'test_function',
|
|
31
|
+
description: 'A test function',
|
|
32
|
+
inputSchema: z.object({
|
|
33
|
+
a: z.string().max(200).describe('The first parameter'),
|
|
34
|
+
b: z
|
|
35
|
+
.number()
|
|
36
|
+
.max(10)
|
|
37
|
+
.min(0)
|
|
38
|
+
.describe('The second parameter, a number between 0 and 10'),
|
|
39
|
+
}),
|
|
40
|
+
requireUserConfirm: false,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const schemas = toolkit.getJSONSchemas();
|
|
44
|
+
expect(schemas).toEqual([
|
|
45
|
+
{
|
|
46
|
+
function: {
|
|
47
|
+
description:
|
|
48
|
+
'Retrieves the full content of a skill by reading its SKILL.md file. Skills are packages of domain expertise that extend agent capabilities. Use this tool to access detailed instructions, examples, and guidelines for a specific skill.\n\nUsage:\n- Provide the skill name as the input parameter\n- The tool will return the complete SKILL.md file content for that skill\n- If the skill is not found, an error message with available skills will be returned\n- Available skills are listed in the skills-system section of the agent prompt',
|
|
49
|
+
name: 'Skill',
|
|
50
|
+
parameters: {
|
|
51
|
+
additionalProperties: false,
|
|
52
|
+
properties: {
|
|
53
|
+
name: {
|
|
54
|
+
description: 'The name of the skill',
|
|
55
|
+
type: 'string',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
required: ['name'],
|
|
59
|
+
type: 'object',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
type: 'function',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
type: 'function',
|
|
66
|
+
function: {
|
|
67
|
+
name: 'test_function',
|
|
68
|
+
description: 'A test function',
|
|
69
|
+
parameters: {
|
|
70
|
+
additionalProperties: false,
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
a: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
maxLength: 200,
|
|
76
|
+
description: 'The first parameter',
|
|
77
|
+
},
|
|
78
|
+
b: {
|
|
79
|
+
type: 'number',
|
|
80
|
+
maximum: 10,
|
|
81
|
+
minimum: 0,
|
|
82
|
+
description: 'The second parameter, a number between 0 and 10',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
required: ['a', 'b'],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
const gen = toolkit.callToolFunction({
|
|
92
|
+
type: 'tool_call',
|
|
93
|
+
name: 'test_function',
|
|
94
|
+
input: '{"a":"hello","b":5}',
|
|
95
|
+
id: '1',
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
for await (const chunk of gen) {
|
|
99
|
+
expect(chunk.content).toEqual([
|
|
100
|
+
{ id: expect.any(String), type: 'text', text: 'Received a=hello, b=5' },
|
|
101
|
+
]);
|
|
102
|
+
expect(chunk.isLast).toBe(true);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('Register and execute tool function with ToolInputSchema', async () => {
|
|
107
|
+
const toolkit = new Toolkit();
|
|
108
|
+
toolkit.registerToolFunction({
|
|
109
|
+
call: testFunction,
|
|
110
|
+
name: 'test_function',
|
|
111
|
+
description: 'A test function',
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: 'object',
|
|
114
|
+
properties: {
|
|
115
|
+
a: { type: 'string', maxLength: 200 },
|
|
116
|
+
b: { type: 'number', maximum: 10, minimum: 0 },
|
|
117
|
+
},
|
|
118
|
+
required: ['a', 'b'],
|
|
119
|
+
},
|
|
120
|
+
requireUserConfirm: false,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const schemas = toolkit.getJSONSchemas();
|
|
124
|
+
expect(schemas).toEqual([
|
|
125
|
+
{
|
|
126
|
+
function: {
|
|
127
|
+
description:
|
|
128
|
+
'Retrieves the full content of a skill by reading its SKILL.md file. Skills are packages of domain expertise that extend agent capabilities. Use this tool to access detailed instructions, examples, and guidelines for a specific skill.\n\nUsage:\n- Provide the skill name as the input parameter\n- The tool will return the complete SKILL.md file content for that skill\n- If the skill is not found, an error message with available skills will be returned\n- Available skills are listed in the skills-system section of the agent prompt',
|
|
129
|
+
name: 'Skill',
|
|
130
|
+
parameters: {
|
|
131
|
+
additionalProperties: false,
|
|
132
|
+
properties: {
|
|
133
|
+
name: {
|
|
134
|
+
description: 'The name of the skill',
|
|
135
|
+
type: 'string',
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
required: ['name'],
|
|
139
|
+
type: 'object',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
type: 'function',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
type: 'function',
|
|
146
|
+
function: {
|
|
147
|
+
name: 'test_function',
|
|
148
|
+
description: 'A test function',
|
|
149
|
+
parameters: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
properties: {
|
|
152
|
+
a: { type: 'string', maxLength: 200 },
|
|
153
|
+
b: { type: 'number', maximum: 10, minimum: 0 },
|
|
154
|
+
},
|
|
155
|
+
required: ['a', 'b'],
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
const gen = toolkit.callToolFunction({
|
|
162
|
+
type: 'tool_call',
|
|
163
|
+
name: 'test_function',
|
|
164
|
+
input: '{"a":"hello","b":5}',
|
|
165
|
+
id: '1b',
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
for await (const chunk of gen) {
|
|
169
|
+
expect(chunk.content).toEqual([
|
|
170
|
+
{ id: expect.any(String), type: 'text', text: 'Received a=hello, b=5' },
|
|
171
|
+
]);
|
|
172
|
+
expect(chunk.isLast).toBe(true);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test('None-parameter tool function', async () => {
|
|
177
|
+
const toolkit = new Toolkit();
|
|
178
|
+
toolkit.registerToolFunction({
|
|
179
|
+
call: () =>
|
|
180
|
+
createToolResponse({
|
|
181
|
+
content: [
|
|
182
|
+
{ type: 'text', text: 'No parameters here', id: crypto.randomUUID() },
|
|
183
|
+
],
|
|
184
|
+
state: 'success',
|
|
185
|
+
}),
|
|
186
|
+
name: 'no_param_function',
|
|
187
|
+
description: 'A function with no parameters',
|
|
188
|
+
inputSchema: z.object({}),
|
|
189
|
+
requireUserConfirm: false,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const schemas = toolkit.getJSONSchemas();
|
|
193
|
+
expect(schemas).toEqual([
|
|
194
|
+
{
|
|
195
|
+
function: {
|
|
196
|
+
description:
|
|
197
|
+
'Retrieves the full content of a skill by reading its SKILL.md file. Skills are packages of domain expertise that extend agent capabilities. Use this tool to access detailed instructions, examples, and guidelines for a specific skill.\n\nUsage:\n- Provide the skill name as the input parameter\n- The tool will return the complete SKILL.md file content for that skill\n- If the skill is not found, an error message with available skills will be returned\n- Available skills are listed in the skills-system section of the agent prompt',
|
|
198
|
+
name: 'Skill',
|
|
199
|
+
parameters: {
|
|
200
|
+
additionalProperties: false,
|
|
201
|
+
properties: {
|
|
202
|
+
name: {
|
|
203
|
+
description: 'The name of the skill',
|
|
204
|
+
type: 'string',
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
required: ['name'],
|
|
208
|
+
type: 'object',
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
type: 'function',
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
type: 'function',
|
|
215
|
+
function: {
|
|
216
|
+
name: 'no_param_function',
|
|
217
|
+
description: 'A function with no parameters',
|
|
218
|
+
parameters: {
|
|
219
|
+
additionalProperties: false,
|
|
220
|
+
type: 'object',
|
|
221
|
+
properties: {},
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
]);
|
|
226
|
+
|
|
227
|
+
const res = await toolkit.callToolFunction({
|
|
228
|
+
type: 'tool_call',
|
|
229
|
+
name: 'no_param_function',
|
|
230
|
+
id: '2',
|
|
231
|
+
input: '{}',
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
for await (const chunk of res) {
|
|
235
|
+
expect(chunk.content).toEqual([
|
|
236
|
+
{ id: expect.any(String), type: 'text', text: 'No parameters here' },
|
|
237
|
+
]);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('Sync generator tool function', async () => {
|
|
242
|
+
const toolkit = new Toolkit();
|
|
243
|
+
toolkit.registerToolFunction({
|
|
244
|
+
call: function* (input: { count: number }) {
|
|
245
|
+
for (let i = 0; i < input.count; i++) {
|
|
246
|
+
yield createToolResponse({
|
|
247
|
+
content: [{ type: 'text', text: `Count: ${i}`, id: crypto.randomUUID() }],
|
|
248
|
+
stream: true,
|
|
249
|
+
isLast: i === input.count - 1,
|
|
250
|
+
state: 'success',
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
name: 'count_function',
|
|
255
|
+
description: 'A function that counts up to a number',
|
|
256
|
+
inputSchema: z.object({
|
|
257
|
+
count: z.number().min(1).max(5).describe('The number to count up to'),
|
|
258
|
+
}),
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const gen = toolkit.callToolFunction({
|
|
262
|
+
type: 'tool_call',
|
|
263
|
+
name: 'count_function',
|
|
264
|
+
id: '3',
|
|
265
|
+
input: '{"count":3}',
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Verify intermediate chunks
|
|
269
|
+
const chunks = [];
|
|
270
|
+
let finalRes;
|
|
271
|
+
while (true) {
|
|
272
|
+
const next = await gen.next();
|
|
273
|
+
if (next.done) {
|
|
274
|
+
finalRes = next.value;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
chunks.push(next.value);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
expect(chunks).toHaveLength(3);
|
|
281
|
+
expect(chunks[0].content).toEqual([
|
|
282
|
+
{ id: expect.any(String), type: 'text', text: 'Count: 0' },
|
|
283
|
+
]);
|
|
284
|
+
expect(chunks[0].isLast).toBe(false);
|
|
285
|
+
expect(chunks[1].content).toEqual([
|
|
286
|
+
{ id: expect.any(String), type: 'text', text: 'Count: 1' },
|
|
287
|
+
]);
|
|
288
|
+
expect(chunks[1].isLast).toBe(false);
|
|
289
|
+
expect(chunks[2].content).toEqual([
|
|
290
|
+
{ id: expect.any(String), type: 'text', text: 'Count: 2' },
|
|
291
|
+
]);
|
|
292
|
+
expect(chunks[2].isLast).toBe(true);
|
|
293
|
+
|
|
294
|
+
// Verify final accumulated result
|
|
295
|
+
expect(finalRes!.content).toEqual([
|
|
296
|
+
{ id: expect.any(String), type: 'text', text: 'Count: 0Count: 1Count: 2' },
|
|
297
|
+
]);
|
|
298
|
+
expect(finalRes!.isLast).toBe(true);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test('Async generator tool function', async () => {
|
|
302
|
+
const toolkit = new Toolkit();
|
|
303
|
+
toolkit.registerToolFunction({
|
|
304
|
+
call: async function* (input: { count: number }) {
|
|
305
|
+
for (let i = 0; i < input.count; i++) {
|
|
306
|
+
yield createToolResponse({
|
|
307
|
+
content: [
|
|
308
|
+
{ type: 'text', text: `Async Count: ${i}`, id: crypto.randomUUID() },
|
|
309
|
+
],
|
|
310
|
+
stream: true,
|
|
311
|
+
state: 'success',
|
|
312
|
+
isLast: i === input.count - 1,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
name: 'async_count_function',
|
|
317
|
+
description: 'An async function that counts up to a number',
|
|
318
|
+
inputSchema: z.object({
|
|
319
|
+
count: z.number().min(1).max(5).describe('The number to count up to'),
|
|
320
|
+
}),
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const gen = toolkit.callToolFunction({
|
|
324
|
+
type: 'tool_call',
|
|
325
|
+
name: 'async_count_function',
|
|
326
|
+
id: '4',
|
|
327
|
+
input: '{"count":2}',
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Verify intermediate chunks
|
|
331
|
+
const chunks = [];
|
|
332
|
+
let finalRes;
|
|
333
|
+
while (true) {
|
|
334
|
+
const next = await gen.next();
|
|
335
|
+
if (next.done) {
|
|
336
|
+
finalRes = next.value;
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
chunks.push(next.value);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
expect(chunks).toHaveLength(2);
|
|
343
|
+
expect(chunks[0].content).toEqual([
|
|
344
|
+
{ id: expect.any(String), type: 'text', text: 'Async Count: 0' },
|
|
345
|
+
]);
|
|
346
|
+
expect(chunks[0].isLast).toBe(false);
|
|
347
|
+
expect(chunks[1].content).toEqual([
|
|
348
|
+
{ id: expect.any(String), type: 'text', text: 'Async Count: 1' },
|
|
349
|
+
]);
|
|
350
|
+
expect(chunks[1].isLast).toBe(true);
|
|
351
|
+
|
|
352
|
+
// Verify final accumulated result
|
|
353
|
+
expect(finalRes!.content).toEqual([
|
|
354
|
+
{ id: expect.any(String), type: 'text', text: 'Async Count: 0Async Count: 1' },
|
|
355
|
+
]);
|
|
356
|
+
expect(finalRes!.isLast).toBe(true);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test('Sync generator tool function returning string', async () => {
|
|
360
|
+
const toolkit = new Toolkit();
|
|
361
|
+
toolkit.registerToolFunction({
|
|
362
|
+
call: function* (input: { count: number }) {
|
|
363
|
+
for (let i = 0; i < input.count; i++) {
|
|
364
|
+
yield `Chunk: ${i}`;
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
name: 'string_count_function',
|
|
368
|
+
description: 'A function that yields strings',
|
|
369
|
+
inputSchema: z.object({
|
|
370
|
+
count: z.number().min(1).max(5).describe('The number to count up to'),
|
|
371
|
+
}),
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const gen = toolkit.callToolFunction({
|
|
375
|
+
type: 'tool_call',
|
|
376
|
+
name: 'string_count_function',
|
|
377
|
+
id: '8',
|
|
378
|
+
input: '{"count":3}',
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
const chunks = [];
|
|
382
|
+
let finalRes;
|
|
383
|
+
while (true) {
|
|
384
|
+
const next = await gen.next();
|
|
385
|
+
if (next.done) {
|
|
386
|
+
finalRes = next.value;
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
chunks.push(next.value);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
expect(chunks).toHaveLength(3);
|
|
393
|
+
expect(chunks[0].content).toEqual([
|
|
394
|
+
{ id: expect.any(String), type: 'text', text: 'Chunk: 0' },
|
|
395
|
+
]);
|
|
396
|
+
expect(chunks[0].isLast).toBe(false);
|
|
397
|
+
expect(chunks[1].content).toEqual([
|
|
398
|
+
{ id: expect.any(String), type: 'text', text: 'Chunk: 1' },
|
|
399
|
+
]);
|
|
400
|
+
expect(chunks[1].isLast).toBe(false);
|
|
401
|
+
expect(chunks[2].content).toEqual([
|
|
402
|
+
{ id: expect.any(String), type: 'text', text: 'Chunk: 2' },
|
|
403
|
+
]);
|
|
404
|
+
expect(chunks[2].isLast).toBe(true);
|
|
405
|
+
|
|
406
|
+
expect(finalRes!.content).toEqual([
|
|
407
|
+
{ id: expect.any(String), type: 'text', text: 'Chunk: 0Chunk: 1Chunk: 2' },
|
|
408
|
+
]);
|
|
409
|
+
expect(finalRes!.isLast).toBe(true);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
test('Async generator tool function returning string', async () => {
|
|
413
|
+
const toolkit = new Toolkit();
|
|
414
|
+
toolkit.registerToolFunction({
|
|
415
|
+
call: async function* (input: { count: number }) {
|
|
416
|
+
for (let i = 0; i < input.count; i++) {
|
|
417
|
+
yield `Async Chunk: ${i}`;
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
name: 'async_string_count_function',
|
|
421
|
+
description: 'An async function that yields strings',
|
|
422
|
+
inputSchema: z.object({
|
|
423
|
+
count: z.number().min(1).max(5).describe('The number to count up to'),
|
|
424
|
+
}),
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const gen = toolkit.callToolFunction({
|
|
428
|
+
type: 'tool_call',
|
|
429
|
+
name: 'async_string_count_function',
|
|
430
|
+
id: '9',
|
|
431
|
+
input: '{"count":2}',
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
const chunks = [];
|
|
435
|
+
let finalRes;
|
|
436
|
+
while (true) {
|
|
437
|
+
const next = await gen.next();
|
|
438
|
+
if (next.done) {
|
|
439
|
+
finalRes = next.value;
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
chunks.push(next.value);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
expect(chunks).toHaveLength(2);
|
|
446
|
+
expect(chunks[0].content).toEqual([
|
|
447
|
+
{ id: expect.any(String), type: 'text', text: 'Async Chunk: 0' },
|
|
448
|
+
]);
|
|
449
|
+
expect(chunks[0].isLast).toBe(false);
|
|
450
|
+
expect(chunks[1].content).toEqual([
|
|
451
|
+
{ id: expect.any(String), type: 'text', text: 'Async Chunk: 1' },
|
|
452
|
+
]);
|
|
453
|
+
expect(chunks[1].isLast).toBe(true);
|
|
454
|
+
|
|
455
|
+
expect(finalRes!.content).toEqual([
|
|
456
|
+
{ id: expect.any(String), type: 'text', text: 'Async Chunk: 0Async Chunk: 1' },
|
|
457
|
+
]);
|
|
458
|
+
expect(finalRes!.isLast).toBe(true);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
test('Error conditions', async () => {
|
|
462
|
+
const toolkit = new Toolkit();
|
|
463
|
+
|
|
464
|
+
// Unregistered function
|
|
465
|
+
const res = await toolkit.callToolFunction({
|
|
466
|
+
type: 'tool_call',
|
|
467
|
+
name: 'non_existent_function',
|
|
468
|
+
id: '5',
|
|
469
|
+
input: '{}',
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
for await (const chunk of res) {
|
|
473
|
+
expect(chunk.content).toEqual([
|
|
474
|
+
{
|
|
475
|
+
id: expect.any(String),
|
|
476
|
+
type: 'text',
|
|
477
|
+
text: 'FunctionNotFoundError: Cannot find the function named non_existent_function',
|
|
478
|
+
},
|
|
479
|
+
]);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Wrong input with ZodObject schema
|
|
483
|
+
toolkit.registerToolFunction({
|
|
484
|
+
call: testFunction,
|
|
485
|
+
name: 'test_function',
|
|
486
|
+
description: 'A test function',
|
|
487
|
+
inputSchema: z.object({
|
|
488
|
+
a: z.string().max(200),
|
|
489
|
+
b: z.number().max(10).min(0),
|
|
490
|
+
}),
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
const res2 = await toolkit.callToolFunction({
|
|
494
|
+
type: 'tool_call',
|
|
495
|
+
name: 'test_function',
|
|
496
|
+
id: '6',
|
|
497
|
+
input: '{"a":"hello","b":20}',
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
for await (const chunk of res2) {
|
|
501
|
+
expect(chunk.content[0]).toEqual({
|
|
502
|
+
id: expect.any(String),
|
|
503
|
+
type: 'text',
|
|
504
|
+
text: 'InvalidArgumentError: [\n {\n "origin": "number",\n "code": "too_big",\n "maximum": 10,\n "inclusive": true,\n "path": [\n "b"\n ],\n "message": "Too big: expected number to be <=10"\n }\n]',
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Wrong input with ToolInputSchema (non-ZodObject)
|
|
509
|
+
const toolkit2 = new Toolkit();
|
|
510
|
+
toolkit2.registerToolFunction({
|
|
511
|
+
call: testFunction,
|
|
512
|
+
name: 'test_function',
|
|
513
|
+
description: 'A test function',
|
|
514
|
+
inputSchema: {
|
|
515
|
+
type: 'object',
|
|
516
|
+
properties: {
|
|
517
|
+
a: { type: 'string', maxLength: 200 },
|
|
518
|
+
b: { type: 'number', maximum: 10, minimum: 0 },
|
|
519
|
+
},
|
|
520
|
+
required: ['a', 'b'],
|
|
521
|
+
},
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
const res3 = toolkit2.callToolFunction({
|
|
525
|
+
type: 'tool_call',
|
|
526
|
+
name: 'test_function',
|
|
527
|
+
id: '7',
|
|
528
|
+
input: '{"a":"hello","b":20}',
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
for await (const chunk of res3) {
|
|
532
|
+
expect(chunk.content[0].type).toBe('text');
|
|
533
|
+
expect((chunk.content[0] as { type: 'text'; text: string }).text).toMatch(
|
|
534
|
+
/InvalidArgumentError/
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test('Skill tool with SKILL.md file', async () => {
|
|
540
|
+
// Setup: Create a temporary skill directory and SKILL.md file
|
|
541
|
+
const testSkillDir = path.join(__dirname, '__test_skill__');
|
|
542
|
+
const skillMdPath = path.join(testSkillDir, 'SKILL.md');
|
|
543
|
+
|
|
544
|
+
// Create the skill directory
|
|
545
|
+
if (!fs.existsSync(testSkillDir)) {
|
|
546
|
+
fs.mkdirSync(testSkillDir, { recursive: true });
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Write SKILL.md with YAML front matter
|
|
550
|
+
const skillContent = `---
|
|
551
|
+
name: test_skill
|
|
552
|
+
description: A test skill for unit testing
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
# Test Skill
|
|
556
|
+
|
|
557
|
+
This is a test skill used for unit testing the Skill tool functionality.
|
|
558
|
+
|
|
559
|
+
## Usage
|
|
560
|
+
|
|
561
|
+
This skill demonstrates how to use the Skill tool to retrieve skill content.`;
|
|
562
|
+
|
|
563
|
+
fs.writeFileSync(skillMdPath, skillContent, 'utf-8');
|
|
564
|
+
|
|
565
|
+
try {
|
|
566
|
+
// Initialize toolkit with builtInSkillTool enabled and register the test skill
|
|
567
|
+
const toolkit = new Toolkit({
|
|
568
|
+
builtInSkillTool: true,
|
|
569
|
+
skills: [testSkillDir],
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
// Verify that the Skill tool is registered
|
|
573
|
+
const schemas = toolkit.getJSONSchemas();
|
|
574
|
+
const skillTool = schemas.find(schema => schema.function.name === 'Skill');
|
|
575
|
+
expect(skillTool).toBeDefined();
|
|
576
|
+
expect(skillTool?.function.description).toContain(
|
|
577
|
+
'Retrieves the full content of a skill'
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
// Call the Skill tool with the test skill name
|
|
581
|
+
const gen = toolkit.callToolFunction({
|
|
582
|
+
type: 'tool_call',
|
|
583
|
+
name: 'Skill',
|
|
584
|
+
input: '{"name":"test_skill"}',
|
|
585
|
+
id: 'skill_test_1',
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// Verify the response
|
|
589
|
+
let finalRes;
|
|
590
|
+
for await (const chunk of gen) {
|
|
591
|
+
finalRes = chunk;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
expect(finalRes).toBeDefined();
|
|
595
|
+
expect(finalRes!.state).toBe('success');
|
|
596
|
+
expect(finalRes!.content).toHaveLength(1);
|
|
597
|
+
expect(finalRes!.content[0].type).toBe('text');
|
|
598
|
+
expect((finalRes!.content[0] as { type: 'text'; text: string }).text).toContain(
|
|
599
|
+
'name: test_skill'
|
|
600
|
+
);
|
|
601
|
+
expect((finalRes!.content[0] as { type: 'text'; text: string }).text).toContain(
|
|
602
|
+
'description: A test skill for unit testing'
|
|
603
|
+
);
|
|
604
|
+
expect((finalRes!.content[0] as { type: 'text'; text: string }).text).toContain(
|
|
605
|
+
'This is a test skill used for unit testing'
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
// Test error case: non-existent skill
|
|
609
|
+
const errorGen = toolkit.callToolFunction({
|
|
610
|
+
type: 'tool_call',
|
|
611
|
+
name: 'Skill',
|
|
612
|
+
input: '{"name":"non_existent_skill"}',
|
|
613
|
+
id: 'skill_test_2',
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
let errorRes;
|
|
617
|
+
for await (const chunk of errorGen) {
|
|
618
|
+
errorRes = chunk;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
expect(errorRes).toBeDefined();
|
|
622
|
+
expect(errorRes!.state).toBe('error');
|
|
623
|
+
expect((errorRes!.content[0] as { type: 'text'; text: string }).text).toContain(
|
|
624
|
+
'SkillNotFoundError'
|
|
625
|
+
);
|
|
626
|
+
} finally {
|
|
627
|
+
// Cleanup: Remove the test skill directory
|
|
628
|
+
if (fs.existsSync(skillMdPath)) {
|
|
629
|
+
fs.unlinkSync(skillMdPath);
|
|
630
|
+
}
|
|
631
|
+
if (fs.existsSync(testSkillDir)) {
|
|
632
|
+
fs.rmdirSync(testSkillDir);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
});
|