@artemiskit/adapter-deepagents 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +133 -0
- package/README.md +198 -0
- package/dist/client.d.ts +81 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +268 -0
- package/dist/types.d.ts +185 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +48 -0
- package/src/client.test.ts +382 -0
- package/src/client.ts +392 -0
- package/src/index.ts +32 -0
- package/src/types.ts +195 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for DeepAgents adapter
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it, mock } from 'bun:test';
|
|
6
|
+
import { DeepAgentsAdapter, createDeepAgentsAdapter } from './client';
|
|
7
|
+
import type { DeepAgentsCallbacks, DeepAgentsOutput, DeepAgentsSystem } from './types';
|
|
8
|
+
|
|
9
|
+
// Mock system factory for testing
|
|
10
|
+
function createMockSystem(
|
|
11
|
+
outputOrFn:
|
|
12
|
+
| string
|
|
13
|
+
| DeepAgentsOutput
|
|
14
|
+
| ((input: unknown, config?: { callbacks?: DeepAgentsCallbacks }) => Promise<DeepAgentsOutput>),
|
|
15
|
+
options?: { supportsStreaming?: boolean; method?: 'invoke' | 'run' | 'execute' }
|
|
16
|
+
): DeepAgentsSystem {
|
|
17
|
+
const execFn = mock(async (input, config) => {
|
|
18
|
+
// Trigger callbacks if provided
|
|
19
|
+
if (config?.callbacks) {
|
|
20
|
+
config.callbacks.onAgentStart?.('agent1', input);
|
|
21
|
+
config.callbacks.onAgentEnd?.('agent1', 'result');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (typeof outputOrFn === 'function') {
|
|
25
|
+
return outputOrFn(input, config);
|
|
26
|
+
}
|
|
27
|
+
if (typeof outputOrFn === 'string') {
|
|
28
|
+
return { result: outputOrFn };
|
|
29
|
+
}
|
|
30
|
+
return outputOrFn;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const system: DeepAgentsSystem = {};
|
|
34
|
+
const method = options?.method ?? 'invoke';
|
|
35
|
+
|
|
36
|
+
if (method === 'invoke') {
|
|
37
|
+
system.invoke = execFn;
|
|
38
|
+
} else if (method === 'run') {
|
|
39
|
+
system.run = execFn;
|
|
40
|
+
} else {
|
|
41
|
+
system.execute = execFn;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (options?.supportsStreaming) {
|
|
45
|
+
system.stream = mock(async function* (input) {
|
|
46
|
+
yield { type: 'agent_start', agent: 'agent1', timestamp: Date.now() };
|
|
47
|
+
yield { type: 'token', content: 'Hello' };
|
|
48
|
+
yield { type: 'token', content: ' World' };
|
|
49
|
+
yield { type: 'agent_end', agent: 'agent1', timestamp: Date.now() };
|
|
50
|
+
yield { type: 'done' };
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return system;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
describe('DeepAgentsAdapter', () => {
|
|
58
|
+
describe('constructor', () => {
|
|
59
|
+
it('should create adapter with system', () => {
|
|
60
|
+
const system = createMockSystem('test output');
|
|
61
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
62
|
+
|
|
63
|
+
expect(adapter.provider).toBe('deepagents');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should accept system with run() method', () => {
|
|
67
|
+
const system = createMockSystem('test', { method: 'run' });
|
|
68
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
69
|
+
|
|
70
|
+
expect(adapter.provider).toBe('deepagents');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should accept system with execute() method', () => {
|
|
74
|
+
const system = createMockSystem('test', { method: 'execute' });
|
|
75
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
76
|
+
|
|
77
|
+
expect(adapter.provider).toBe('deepagents');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should throw if system has no execution method', () => {
|
|
81
|
+
expect(() => {
|
|
82
|
+
new DeepAgentsAdapter({ provider: 'deepagents' }, {} as DeepAgentsSystem);
|
|
83
|
+
}).toThrow('must have an invoke(), run(), or execute() method');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('generate', () => {
|
|
88
|
+
it('should call system.invoke with string prompt', async () => {
|
|
89
|
+
const system = createMockSystem({ result: 'Generated content' });
|
|
90
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
91
|
+
|
|
92
|
+
const result = await adapter.generate({ prompt: 'Create something' });
|
|
93
|
+
|
|
94
|
+
expect(system.invoke).toHaveBeenCalled();
|
|
95
|
+
expect(result.text).toBe('Generated content');
|
|
96
|
+
expect(result.finishReason).toBe('stop');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should handle chat message array prompts', async () => {
|
|
100
|
+
const system = createMockSystem({ result: 'Response' });
|
|
101
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
102
|
+
|
|
103
|
+
const result = await adapter.generate({
|
|
104
|
+
prompt: [
|
|
105
|
+
{ role: 'system', content: 'You are a helpful team' },
|
|
106
|
+
{ role: 'user', content: 'Create content' },
|
|
107
|
+
],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(system.invoke).toHaveBeenCalledWith(
|
|
111
|
+
expect.objectContaining({
|
|
112
|
+
task: 'Create content',
|
|
113
|
+
context: { systemPrompt: 'You are a helpful team' },
|
|
114
|
+
}),
|
|
115
|
+
expect.anything()
|
|
116
|
+
);
|
|
117
|
+
expect(result.text).toBe('Response');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should extract output from various response shapes', async () => {
|
|
121
|
+
const shapes = [
|
|
122
|
+
{ result: 'from result' },
|
|
123
|
+
{ output: 'from output' },
|
|
124
|
+
{ response: 'from response' },
|
|
125
|
+
{ answer: 'from answer' },
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
for (const shape of shapes) {
|
|
129
|
+
const system = createMockSystem(shape);
|
|
130
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
131
|
+
const result = await adapter.generate({ prompt: 'Test' });
|
|
132
|
+
expect(result.text).toBe(Object.values(shape)[0]);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should extract output from traces when no direct output', async () => {
|
|
137
|
+
const system = createMockSystem({
|
|
138
|
+
traces: [{ agent: 'writer', action: 'generate', output: 'Final output from trace' }],
|
|
139
|
+
});
|
|
140
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
141
|
+
|
|
142
|
+
const result = await adapter.generate({ prompt: 'Test' });
|
|
143
|
+
|
|
144
|
+
expect(result.text).toBe('Final output from trace');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should capture traces when enabled', async () => {
|
|
148
|
+
const system = createMockSystem({
|
|
149
|
+
result: 'Done',
|
|
150
|
+
traces: [
|
|
151
|
+
{ agent: 'researcher', action: 'search', toolsUsed: ['web_search'] },
|
|
152
|
+
{ agent: 'writer', action: 'write', output: 'Article' },
|
|
153
|
+
],
|
|
154
|
+
});
|
|
155
|
+
const adapter = new DeepAgentsAdapter(
|
|
156
|
+
{ provider: 'deepagents', captureTraces: true },
|
|
157
|
+
system
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const result = await adapter.generate({ prompt: 'Research and write' });
|
|
161
|
+
|
|
162
|
+
expect(result.raw.metadata.toolsUsed).toContain('web_search');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should capture messages between agents', async () => {
|
|
166
|
+
const system = createMockSystem({
|
|
167
|
+
result: 'Final',
|
|
168
|
+
messages: [
|
|
169
|
+
{ from: 'coordinator', to: 'researcher', content: 'Find info', type: 'delegation' },
|
|
170
|
+
{ from: 'researcher', to: 'coordinator', content: 'Here is info', type: 'response' },
|
|
171
|
+
],
|
|
172
|
+
});
|
|
173
|
+
const adapter = new DeepAgentsAdapter(
|
|
174
|
+
{ provider: 'deepagents', captureMessages: true },
|
|
175
|
+
system
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const result = await adapter.generate({ prompt: 'Do research' });
|
|
179
|
+
|
|
180
|
+
expect(result.raw.metadata.totalMessages).toBe(2);
|
|
181
|
+
expect(result.raw.metadata.agentsInvolved).toContain('coordinator');
|
|
182
|
+
expect(result.raw.metadata.agentsInvolved).toContain('researcher');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should track latency', async () => {
|
|
186
|
+
const system = createMockSystem(async () => {
|
|
187
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
188
|
+
return { result: 'Delayed' };
|
|
189
|
+
});
|
|
190
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
191
|
+
|
|
192
|
+
const result = await adapter.generate({ prompt: 'Test' });
|
|
193
|
+
|
|
194
|
+
expect(result.latencyMs).toBeGreaterThanOrEqual(10);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should include model name in result', async () => {
|
|
198
|
+
const system = createMockSystem({ result: 'Test' });
|
|
199
|
+
const adapter = new DeepAgentsAdapter(
|
|
200
|
+
{ provider: 'deepagents', name: 'research-team' },
|
|
201
|
+
system
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const result = await adapter.generate({ prompt: 'Test' });
|
|
205
|
+
|
|
206
|
+
expect(result.model).toBe('research-team');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should extract token usage when available', async () => {
|
|
210
|
+
const system = createMockSystem({
|
|
211
|
+
result: 'Test',
|
|
212
|
+
tokenUsage: { prompt: 100, completion: 50, total: 150 },
|
|
213
|
+
});
|
|
214
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
215
|
+
|
|
216
|
+
const result = await adapter.generate({ prompt: 'Test' });
|
|
217
|
+
|
|
218
|
+
expect(result.tokens).toEqual({ prompt: 100, completion: 50, total: 150 });
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should use run() when invoke() not available', async () => {
|
|
222
|
+
const system = createMockSystem({ result: 'From run' }, { method: 'run' });
|
|
223
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
224
|
+
|
|
225
|
+
const result = await adapter.generate({ prompt: 'Test' });
|
|
226
|
+
|
|
227
|
+
expect(system.run).toHaveBeenCalled();
|
|
228
|
+
expect(result.text).toBe('From run');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should use execute() when invoke() and run() not available', async () => {
|
|
232
|
+
const system = createMockSystem({ result: 'From execute' }, { method: 'execute' });
|
|
233
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
234
|
+
|
|
235
|
+
const result = await adapter.generate({ prompt: 'Test' });
|
|
236
|
+
|
|
237
|
+
expect(system.execute).toHaveBeenCalled();
|
|
238
|
+
expect(result.text).toBe('From execute');
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should trigger callback tracking', async () => {
|
|
242
|
+
let callbacksCalled = false;
|
|
243
|
+
const system = createMockSystem(async (input, config) => {
|
|
244
|
+
if (config?.callbacks?.onAgentStart) {
|
|
245
|
+
config.callbacks.onAgentStart('testAgent', input);
|
|
246
|
+
callbacksCalled = true;
|
|
247
|
+
}
|
|
248
|
+
return { result: 'Done' };
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const adapter = new DeepAgentsAdapter(
|
|
252
|
+
{ provider: 'deepagents', captureTraces: true },
|
|
253
|
+
system
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
await adapter.generate({ prompt: 'Test' });
|
|
257
|
+
|
|
258
|
+
expect(callbacksCalled).toBe(true);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe('stream', () => {
|
|
263
|
+
it('should stream chunks when supported', async () => {
|
|
264
|
+
const system = createMockSystem('test', { supportsStreaming: true });
|
|
265
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
266
|
+
|
|
267
|
+
const chunks: string[] = [];
|
|
268
|
+
const onChunk = (chunk: string) => chunks.push(chunk);
|
|
269
|
+
|
|
270
|
+
for await (const chunk of adapter.stream({ prompt: 'Test' }, onChunk)) {
|
|
271
|
+
// Collect
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
expect(chunks.join('')).toBe('Hello World');
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should fallback to generate when streaming not supported', async () => {
|
|
278
|
+
const system = createMockSystem({ result: 'non-streaming result' });
|
|
279
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
280
|
+
|
|
281
|
+
const chunks: string[] = [];
|
|
282
|
+
const onChunk = (chunk: string) => chunks.push(chunk);
|
|
283
|
+
|
|
284
|
+
for await (const chunk of adapter.stream({ prompt: 'Test' }, onChunk)) {
|
|
285
|
+
// Collect
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
expect(chunks).toEqual(['non-streaming result']);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe('capabilities', () => {
|
|
293
|
+
it('should report streaming capability based on system', async () => {
|
|
294
|
+
const streamingSystem = createMockSystem('test', { supportsStreaming: true });
|
|
295
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, streamingSystem);
|
|
296
|
+
|
|
297
|
+
const caps = await adapter.capabilities();
|
|
298
|
+
expect(caps.streaming).toBe(true);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should report no streaming when not supported', async () => {
|
|
302
|
+
const system = createMockSystem('test');
|
|
303
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
304
|
+
|
|
305
|
+
const caps = await adapter.capabilities();
|
|
306
|
+
expect(caps.streaming).toBe(false);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('should report tool capabilities', async () => {
|
|
310
|
+
const system = createMockSystem('test');
|
|
311
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
312
|
+
|
|
313
|
+
const caps = await adapter.capabilities();
|
|
314
|
+
expect(caps.functionCalling).toBe(true);
|
|
315
|
+
expect(caps.toolUse).toBe(true);
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
describe('close', () => {
|
|
320
|
+
it('should complete without error', async () => {
|
|
321
|
+
const system = createMockSystem('test');
|
|
322
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
323
|
+
|
|
324
|
+
await expect(adapter.close()).resolves.toBeUndefined();
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
describe('metadata extraction', () => {
|
|
329
|
+
it('should extract unique agents from traces and messages', async () => {
|
|
330
|
+
const system = createMockSystem({
|
|
331
|
+
result: 'Done',
|
|
332
|
+
agents: ['coordinator'],
|
|
333
|
+
traces: [{ agent: 'researcher', action: 'search' }],
|
|
334
|
+
messages: [{ from: 'writer', to: 'editor', content: 'Review this' }],
|
|
335
|
+
});
|
|
336
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
337
|
+
|
|
338
|
+
const result = await adapter.generate({ prompt: 'Test' });
|
|
339
|
+
|
|
340
|
+
const agents = result.raw.metadata.agentsInvolved;
|
|
341
|
+
expect(agents).toContain('coordinator');
|
|
342
|
+
expect(agents).toContain('researcher');
|
|
343
|
+
expect(agents).toContain('writer');
|
|
344
|
+
expect(agents).toContain('editor');
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('should count tool calls correctly', async () => {
|
|
348
|
+
const system = createMockSystem({
|
|
349
|
+
result: 'Done',
|
|
350
|
+
traces: [
|
|
351
|
+
{ agent: 'agent1', action: 'use_tool', toolsUsed: ['search', 'fetch'] },
|
|
352
|
+
{ agent: 'agent2', action: 'use_tool', toolsUsed: ['calculate'] },
|
|
353
|
+
],
|
|
354
|
+
});
|
|
355
|
+
const adapter = new DeepAgentsAdapter({ provider: 'deepagents' }, system);
|
|
356
|
+
|
|
357
|
+
const result = await adapter.generate({ prompt: 'Test' });
|
|
358
|
+
|
|
359
|
+
expect(result.raw.metadata.totalToolCalls).toBe(3);
|
|
360
|
+
expect(result.raw.metadata.toolsUsed).toContain('search');
|
|
361
|
+
expect(result.raw.metadata.toolsUsed).toContain('fetch');
|
|
362
|
+
expect(result.raw.metadata.toolsUsed).toContain('calculate');
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
describe('createDeepAgentsAdapter', () => {
|
|
368
|
+
it('should create adapter with factory function', () => {
|
|
369
|
+
const system = createMockSystem('test');
|
|
370
|
+
const adapter = createDeepAgentsAdapter(system, { name: 'test-team' });
|
|
371
|
+
|
|
372
|
+
expect(adapter).toBeInstanceOf(DeepAgentsAdapter);
|
|
373
|
+
expect(adapter.provider).toBe('deepagents');
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it('should work with minimal options', () => {
|
|
377
|
+
const system = createMockSystem('test');
|
|
378
|
+
const adapter = createDeepAgentsAdapter(system);
|
|
379
|
+
|
|
380
|
+
expect(adapter).toBeInstanceOf(DeepAgentsAdapter);
|
|
381
|
+
});
|
|
382
|
+
});
|