@statelyai/agent 1.1.6 → 2.0.0-next.1
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/.changeset/cyan-carpets-perform.md +5 -0
- package/.changeset/fast-donkeys-argue.md +5 -0
- package/.changeset/light-hats-drive.md +9 -0
- package/.changeset/old-jobs-check.md +5 -0
- package/.changeset/pre.json +13 -0
- package/.vscode/launch.json +6 -0
- package/CHANGELOG.md +22 -0
- package/dist/index.d.mts +262 -171
- package/dist/index.d.ts +262 -171
- package/dist/index.js +383 -274
- package/dist/index.mjs +386 -272
- package/examples/chatbot-alt.ts +57 -0
- package/examples/chatbot.ts +12 -17
- package/examples/cot.ts +26 -23
- package/examples/customer-service-sim.ts +107 -0
- package/examples/email.ts +37 -41
- package/examples/example.ts +6 -6
- package/examples/executor.ts +66 -0
- package/examples/goal.ts +12 -12
- package/examples/helpers/helpers.ts +26 -14
- package/examples/joke.ts +79 -76
- package/examples/jugs.ts +125 -0
- package/examples/multi.ts +5 -5
- package/examples/newspaper.ts +98 -104
- package/examples/number.ts +6 -5
- package/examples/raffle.ts +11 -12
- package/examples/river-crossing.ts +140 -0
- package/examples/sandbox.ts +1 -1
- package/examples/simple.ts +5 -3
- package/examples/summary.ts +121 -0
- package/examples/support.ts +6 -6
- package/examples/ticTacToe.ts +86 -45
- package/examples/todo.ts +7 -7
- package/examples/tutor.ts +15 -15
- package/examples/verify.ts +3 -3
- package/examples/weather.ts +6 -9
- package/examples/wiki.ts +27 -8
- package/examples/word.ts +16 -11
- package/package.json +16 -11
- package/readme.md +1 -1
- package/src/agent-experimental.ts +1 -1
- package/src/agent.test.ts +243 -214
- package/src/agent.ts +286 -95
- package/src/decide.test.ts +276 -0
- package/src/decide.ts +163 -0
- package/src/index.ts +1 -1
- package/src/middleware.ts +91 -0
- package/src/mockModel.ts +47 -0
- package/src/planners/shortestPath.test.ts +94 -0
- package/src/planners/shortestPath.ts +177 -0
- package/src/planners/simple.ts +105 -0
- package/src/strategies/chain-of-note.ts +6 -55
- package/src/text.ts +51 -144
- package/src/types.ts +187 -212
- package/src/utils.ts +48 -4
- package/vitest.config.ts +9 -3
- package/src/adapters/vercel.ts +0 -7
- package/src/decision.test.ts +0 -179
- package/src/decision.ts +0 -84
- package/src/memory.ts +0 -25
- package/src/planners/shortestPathPlanner.ts +0 -22
- package/src/planners/simplePlanner.ts +0 -139
package/src/agent.test.ts
CHANGED
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
import { test, expect, vi } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
AgentGenerateTextResult,
|
|
4
|
-
AgentMessage,
|
|
5
|
-
createAgent,
|
|
6
|
-
type AIAdapter,
|
|
7
|
-
} from './';
|
|
2
|
+
import { createAgent } from './';
|
|
8
3
|
import { createActor, createMachine } from 'xstate';
|
|
9
|
-
import {
|
|
4
|
+
import { LanguageModelV1CallOptions } from 'ai';
|
|
10
5
|
import { z } from 'zod';
|
|
6
|
+
import { dummyResponseValues, MockLanguageModelV1 } from './mockModel';
|
|
11
7
|
|
|
12
8
|
test('an agent has the expected interface', () => {
|
|
13
9
|
const agent = createAgent({
|
|
14
|
-
|
|
10
|
+
id: 'test',
|
|
15
11
|
events: {},
|
|
16
|
-
model:
|
|
12
|
+
model: new MockLanguageModelV1(),
|
|
17
13
|
});
|
|
18
14
|
|
|
19
15
|
expect(agent.decide).toBeDefined();
|
|
20
|
-
expect(agent.generateText).toBeDefined();
|
|
21
|
-
expect(agent.streamText).toBeDefined();
|
|
22
16
|
|
|
23
17
|
expect(agent.addMessage).toBeDefined();
|
|
24
18
|
expect(agent.addObservation).toBeDefined();
|
|
@@ -34,46 +28,36 @@ test('an agent has the expected interface', () => {
|
|
|
34
28
|
});
|
|
35
29
|
|
|
36
30
|
test('agent.addMessage() adds to message history', () => {
|
|
31
|
+
const model = new MockLanguageModelV1();
|
|
32
|
+
|
|
37
33
|
const agent = createAgent({
|
|
38
|
-
|
|
34
|
+
id: 'test',
|
|
39
35
|
events: {},
|
|
40
|
-
model
|
|
36
|
+
model,
|
|
41
37
|
});
|
|
42
38
|
|
|
43
39
|
agent.addMessage({
|
|
44
|
-
content: 'msg 1',
|
|
45
40
|
role: 'user',
|
|
41
|
+
content: [{ type: 'text', text: 'msg 1' }],
|
|
46
42
|
});
|
|
47
43
|
|
|
48
44
|
const messageHistory = agent.addMessage({
|
|
49
|
-
content: 'response 1',
|
|
50
45
|
role: 'assistant',
|
|
46
|
+
content: [{ type: 'text', text: 'response 1' }],
|
|
51
47
|
});
|
|
52
48
|
|
|
53
|
-
expect(messageHistory.
|
|
49
|
+
expect(messageHistory.episodeId).toEqual(agent.episodeId);
|
|
54
50
|
|
|
55
|
-
expect(agent.select((c) => c.messages)).toContainEqual(
|
|
56
|
-
expect.objectContaining({
|
|
57
|
-
content: 'msg 1',
|
|
58
|
-
})
|
|
59
|
-
);
|
|
60
51
|
expect(agent.getMessages()).toContainEqual(
|
|
61
52
|
expect.objectContaining({
|
|
62
|
-
content: 'msg 1',
|
|
53
|
+
content: [expect.objectContaining({ text: 'msg 1' })],
|
|
63
54
|
})
|
|
64
55
|
);
|
|
65
56
|
|
|
66
|
-
expect(agent.select((c) => c.messages)).toContainEqual(
|
|
67
|
-
expect.objectContaining({
|
|
68
|
-
content: 'response 1',
|
|
69
|
-
sessionId: expect.any(String),
|
|
70
|
-
timestamp: expect.any(Number),
|
|
71
|
-
})
|
|
72
|
-
);
|
|
73
57
|
expect(agent.getMessages()).toContainEqual(
|
|
74
58
|
expect.objectContaining({
|
|
75
|
-
content: 'response 1',
|
|
76
|
-
|
|
59
|
+
content: [expect.objectContaining({ text: 'response 1' })],
|
|
60
|
+
episodeId: expect.any(String),
|
|
77
61
|
timestamp: expect.any(Number),
|
|
78
62
|
})
|
|
79
63
|
);
|
|
@@ -81,7 +65,7 @@ test('agent.addMessage() adds to message history', () => {
|
|
|
81
65
|
|
|
82
66
|
test('agent.addFeedback() adds to feedback', () => {
|
|
83
67
|
const agent = createAgent({
|
|
84
|
-
|
|
68
|
+
id: 'test',
|
|
85
69
|
events: {},
|
|
86
70
|
model: {} as any,
|
|
87
71
|
});
|
|
@@ -94,16 +78,16 @@ test('agent.addFeedback() adds to feedback', () => {
|
|
|
94
78
|
observationId: 'obs-1',
|
|
95
79
|
});
|
|
96
80
|
|
|
97
|
-
expect(feedback.
|
|
81
|
+
expect(feedback.episodeId).toEqual(agent.episodeId);
|
|
98
82
|
|
|
99
|
-
expect(agent.
|
|
83
|
+
expect(agent.getFeedback()).toContainEqual(
|
|
100
84
|
expect.objectContaining({
|
|
101
85
|
attributes: {
|
|
102
86
|
score: -1,
|
|
103
87
|
},
|
|
104
88
|
goal: 'Win the game',
|
|
105
89
|
observationId: 'obs-1',
|
|
106
|
-
|
|
90
|
+
episodeId: expect.any(String),
|
|
107
91
|
timestamp: expect.any(Number),
|
|
108
92
|
})
|
|
109
93
|
);
|
|
@@ -114,7 +98,7 @@ test('agent.addFeedback() adds to feedback', () => {
|
|
|
114
98
|
},
|
|
115
99
|
goal: 'Win the game',
|
|
116
100
|
observationId: 'obs-1',
|
|
117
|
-
|
|
101
|
+
episodeId: expect.any(String),
|
|
118
102
|
timestamp: expect.any(Number),
|
|
119
103
|
})
|
|
120
104
|
);
|
|
@@ -122,7 +106,7 @@ test('agent.addFeedback() adds to feedback', () => {
|
|
|
122
106
|
|
|
123
107
|
test('agent.addObservation() adds to observations', () => {
|
|
124
108
|
const agent = createAgent({
|
|
125
|
-
|
|
109
|
+
id: 'test',
|
|
126
110
|
events: {},
|
|
127
111
|
model: {} as any,
|
|
128
112
|
});
|
|
@@ -133,14 +117,36 @@ test('agent.addObservation() adds to observations', () => {
|
|
|
133
117
|
state: { value: 'lost', context: {} },
|
|
134
118
|
});
|
|
135
119
|
|
|
136
|
-
expect(observation.
|
|
120
|
+
expect(observation.episodeId).toEqual(agent.episodeId);
|
|
137
121
|
|
|
138
|
-
expect(agent.
|
|
122
|
+
expect(agent.getObservations()).toContainEqual(
|
|
139
123
|
expect.objectContaining({
|
|
140
124
|
prevState: { value: 'playing', context: {} },
|
|
141
125
|
event: { type: 'play', position: 3 },
|
|
142
126
|
state: { value: 'lost', context: {} },
|
|
143
|
-
|
|
127
|
+
episodeId: expect.any(String),
|
|
128
|
+
timestamp: expect.any(Number),
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('agent.addObservation() adds to observations (initial state)', () => {
|
|
134
|
+
const agent = createAgent({
|
|
135
|
+
id: 'test',
|
|
136
|
+
events: {},
|
|
137
|
+
model: {} as any,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const observation = agent.addObservation({
|
|
141
|
+
state: { value: 'lost' },
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(observation.episodeId).toEqual(agent.episodeId);
|
|
145
|
+
|
|
146
|
+
expect(agent.getObservations()).toContainEqual(
|
|
147
|
+
expect.objectContaining({
|
|
148
|
+
state: { value: 'lost', context: undefined },
|
|
149
|
+
episodeId: expect.any(String),
|
|
144
150
|
timestamp: expect.any(Number),
|
|
145
151
|
})
|
|
146
152
|
);
|
|
@@ -148,7 +154,7 @@ test('agent.addObservation() adds to observations', () => {
|
|
|
148
154
|
|
|
149
155
|
test('agent.addObservation() adds to observations with machine hash', () => {
|
|
150
156
|
const agent = createAgent({
|
|
151
|
-
|
|
157
|
+
id: 'test',
|
|
152
158
|
events: {},
|
|
153
159
|
model: {} as any,
|
|
154
160
|
});
|
|
@@ -172,15 +178,62 @@ test('agent.addObservation() adds to observations with machine hash', () => {
|
|
|
172
178
|
machine,
|
|
173
179
|
});
|
|
174
180
|
|
|
175
|
-
expect(observation.
|
|
181
|
+
expect(observation.episodeId).toEqual(agent.episodeId);
|
|
176
182
|
|
|
177
|
-
expect(agent.
|
|
183
|
+
expect(agent.getObservations()).toContainEqual(
|
|
178
184
|
expect.objectContaining({
|
|
179
185
|
prevState: { value: 'playing', context: {} },
|
|
180
186
|
event: { type: 'play', position: 3 },
|
|
181
187
|
state: { value: 'lost', context: {} },
|
|
182
188
|
machineHash: expect.any(String),
|
|
183
|
-
|
|
189
|
+
episodeId: expect.any(String),
|
|
190
|
+
timestamp: expect.any(Number),
|
|
191
|
+
})
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('agent.addFeedback() adds to feedback (with observation)', () => {
|
|
196
|
+
const agent = createAgent({
|
|
197
|
+
id: 'test',
|
|
198
|
+
events: {},
|
|
199
|
+
model: {} as any,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const observation = agent.addObservation({
|
|
203
|
+
state: {
|
|
204
|
+
value: 'playing',
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const feedback = agent.addFeedback({
|
|
209
|
+
attributes: {
|
|
210
|
+
score: -1,
|
|
211
|
+
},
|
|
212
|
+
goal: 'Win the game',
|
|
213
|
+
observationId: observation.id,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
expect(feedback.episodeId).toEqual(agent.episodeId);
|
|
217
|
+
|
|
218
|
+
expect(agent.getFeedback()).toContainEqual(
|
|
219
|
+
expect.objectContaining({
|
|
220
|
+
attributes: {
|
|
221
|
+
score: -1,
|
|
222
|
+
},
|
|
223
|
+
goal: 'Win the game',
|
|
224
|
+
observationId: observation.id,
|
|
225
|
+
episodeId: expect.any(String),
|
|
226
|
+
timestamp: expect.any(Number),
|
|
227
|
+
})
|
|
228
|
+
);
|
|
229
|
+
expect(agent.getFeedback()).toContainEqual(
|
|
230
|
+
expect.objectContaining({
|
|
231
|
+
attributes: {
|
|
232
|
+
score: -1,
|
|
233
|
+
},
|
|
234
|
+
goal: 'Win the game',
|
|
235
|
+
observationId: observation.id,
|
|
236
|
+
episodeId: expect.any(String),
|
|
184
237
|
timestamp: expect.any(Number),
|
|
185
238
|
})
|
|
186
239
|
);
|
|
@@ -198,7 +251,7 @@ test('agent.interact() observes machine actors (no 2nd arg)', () => {
|
|
|
198
251
|
});
|
|
199
252
|
|
|
200
253
|
const agent = createAgent({
|
|
201
|
-
|
|
254
|
+
id: 'test',
|
|
202
255
|
events: {},
|
|
203
256
|
model: {} as any,
|
|
204
257
|
});
|
|
@@ -209,7 +262,7 @@ test('agent.interact() observes machine actors (no 2nd arg)', () => {
|
|
|
209
262
|
|
|
210
263
|
actor.start();
|
|
211
264
|
|
|
212
|
-
expect(agent.
|
|
265
|
+
expect(agent.getObservations()).toContainEqual(
|
|
213
266
|
expect.objectContaining({
|
|
214
267
|
prevState: undefined,
|
|
215
268
|
state: expect.objectContaining({ value: 'a' }),
|
|
@@ -224,7 +277,7 @@ test('agent.interact() observes machine actors (no 2nd arg)', () => {
|
|
|
224
277
|
|
|
225
278
|
actor.send({ type: 'NEXT' });
|
|
226
279
|
|
|
227
|
-
expect(agent.
|
|
280
|
+
expect(agent.getObservations()).toContainEqual(
|
|
228
281
|
expect.objectContaining({
|
|
229
282
|
prevState: expect.objectContaining({ value: 'a' }),
|
|
230
283
|
event: { type: 'NEXT' },
|
|
@@ -233,35 +286,11 @@ test('agent.interact() observes machine actors (no 2nd arg)', () => {
|
|
|
233
286
|
);
|
|
234
287
|
});
|
|
235
288
|
|
|
236
|
-
test('Agents can use a custom adapter', async () => {
|
|
237
|
-
const adapter = {
|
|
238
|
-
generateText: async () => {
|
|
239
|
-
return {
|
|
240
|
-
text: 'Response',
|
|
241
|
-
} as any;
|
|
242
|
-
},
|
|
243
|
-
} as unknown as AIAdapter;
|
|
244
|
-
|
|
245
|
-
const agent = createAgent({
|
|
246
|
-
name: 'test',
|
|
247
|
-
events: {},
|
|
248
|
-
adapter,
|
|
249
|
-
model: {} as any,
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
const res = await agent.generateText({
|
|
253
|
-
prompt: 'Question?',
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
expect(res.text).toEqual('Response');
|
|
257
|
-
});
|
|
258
|
-
|
|
259
289
|
test('You can listen for feedback events', () => {
|
|
260
290
|
const fn = vi.fn();
|
|
261
291
|
const agent = createAgent({
|
|
262
|
-
|
|
292
|
+
id: 'test',
|
|
263
293
|
events: {},
|
|
264
|
-
adapter: {} as any,
|
|
265
294
|
model: {} as any,
|
|
266
295
|
});
|
|
267
296
|
|
|
@@ -280,32 +309,34 @@ test('You can listen for feedback events', () => {
|
|
|
280
309
|
|
|
281
310
|
test('You can listen for plan events', async () => {
|
|
282
311
|
const fn = vi.fn();
|
|
312
|
+
const model = new MockLanguageModelV1({
|
|
313
|
+
doGenerate: async (params: LanguageModelV1CallOptions) => {
|
|
314
|
+
const keys =
|
|
315
|
+
params.mode.type === 'regular'
|
|
316
|
+
? params.mode.tools?.map((tool) => tool.name)
|
|
317
|
+
: [];
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
...dummyResponseValues,
|
|
321
|
+
finishReason: 'tool-calls',
|
|
322
|
+
toolCalls: [
|
|
323
|
+
{
|
|
324
|
+
toolCallType: 'function',
|
|
325
|
+
toolCallId: 'call-1',
|
|
326
|
+
toolName: keys![0],
|
|
327
|
+
args: `{ "type": "${keys?.[0]}" }`,
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
} as any;
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
|
|
283
334
|
const agent = createAgent({
|
|
284
|
-
|
|
285
|
-
model
|
|
335
|
+
id: 'test',
|
|
336
|
+
model,
|
|
286
337
|
events: {
|
|
287
338
|
WIN: z.object({}),
|
|
288
339
|
},
|
|
289
|
-
adapter: {
|
|
290
|
-
generateText: async (arg) => {
|
|
291
|
-
const keys = Object.keys(arg.tools!);
|
|
292
|
-
|
|
293
|
-
if (keys.length !== 1) {
|
|
294
|
-
throw new Error('Expected only 1 choice');
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return {
|
|
298
|
-
toolResults: [
|
|
299
|
-
{
|
|
300
|
-
result: {
|
|
301
|
-
type: keys[0],
|
|
302
|
-
},
|
|
303
|
-
},
|
|
304
|
-
],
|
|
305
|
-
} as any as AgentGenerateTextResult;
|
|
306
|
-
},
|
|
307
|
-
streamText: {} as any,
|
|
308
|
-
},
|
|
309
340
|
});
|
|
310
341
|
|
|
311
342
|
agent.on('plan', fn);
|
|
@@ -363,144 +394,142 @@ test('agent.types provides context and event types', () => {
|
|
|
363
394
|
agent.types.context satisfies { score: string };
|
|
364
395
|
});
|
|
365
396
|
|
|
366
|
-
test
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
adapter: {
|
|
373
|
-
[method]: async (opts: any) => {
|
|
374
|
-
const res = {
|
|
375
|
-
text: 'response',
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
opts.onFinish?.(res);
|
|
379
|
-
|
|
380
|
-
return res as AgentGenerateTextResult;
|
|
381
|
-
},
|
|
382
|
-
} as any as AIAdapter,
|
|
383
|
-
});
|
|
397
|
+
test('It allows unrecognized events', () => {
|
|
398
|
+
const agent = createAgent({
|
|
399
|
+
model: {} as any,
|
|
400
|
+
events: {},
|
|
401
|
+
context: {},
|
|
402
|
+
});
|
|
384
403
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
});
|
|
404
|
+
expect(() => {
|
|
405
|
+
agent.send({
|
|
406
|
+
// @ts-expect-error
|
|
407
|
+
type: 'unrecognized',
|
|
391
408
|
});
|
|
409
|
+
}).not.toThrow();
|
|
410
|
+
});
|
|
392
411
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
412
|
+
test('You can listen for message events', () => {
|
|
413
|
+
const fn = vi.fn();
|
|
414
|
+
const agent = createAgent({
|
|
415
|
+
id: 'test',
|
|
416
|
+
events: {},
|
|
417
|
+
model: {} as any,
|
|
418
|
+
});
|
|
397
419
|
|
|
398
|
-
|
|
420
|
+
agent.onMessage(fn);
|
|
399
421
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
422
|
+
const message = {
|
|
423
|
+
role: 'user' as const,
|
|
424
|
+
content: [{ type: 'text' as const, text: 'test message' }],
|
|
425
|
+
};
|
|
404
426
|
|
|
405
|
-
|
|
406
|
-
'correlation IDs are automatically generated if not provided (%s)',
|
|
407
|
-
async (method) => {
|
|
408
|
-
const agent = createAgent({
|
|
409
|
-
model: {} as any,
|
|
410
|
-
events: {},
|
|
411
|
-
adapter: {
|
|
412
|
-
[method]: async (opts: any) => {
|
|
413
|
-
const res = {
|
|
414
|
-
text: 'response',
|
|
415
|
-
};
|
|
427
|
+
agent.addMessage(message);
|
|
416
428
|
|
|
417
|
-
|
|
429
|
+
expect(fn).toHaveBeenCalledWith(
|
|
430
|
+
expect.objectContaining({
|
|
431
|
+
role: 'user',
|
|
432
|
+
content: [{ type: 'text', text: 'test message' }],
|
|
433
|
+
episodeId: expect.any(String),
|
|
434
|
+
timestamp: expect.any(Number),
|
|
435
|
+
})
|
|
436
|
+
);
|
|
437
|
+
});
|
|
418
438
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
}
|
|
439
|
+
test('agent.getPlans() returns plans from context', () => {
|
|
440
|
+
const agent = createAgent({
|
|
441
|
+
id: 'test',
|
|
442
|
+
events: {},
|
|
443
|
+
model: {} as any,
|
|
444
|
+
planner: async (agent) => {
|
|
445
|
+
return {
|
|
446
|
+
episodeId: agent.episodeId,
|
|
447
|
+
planner: 'test-planner',
|
|
448
|
+
goal: '',
|
|
449
|
+
goalState: undefined,
|
|
450
|
+
paths: [
|
|
451
|
+
{
|
|
452
|
+
state: undefined,
|
|
453
|
+
steps: [],
|
|
454
|
+
},
|
|
455
|
+
],
|
|
456
|
+
nextEvent: undefined,
|
|
457
|
+
timestamp: Date.now(),
|
|
458
|
+
};
|
|
459
|
+
},
|
|
460
|
+
});
|
|
423
461
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
462
|
+
const plans = agent.getPlans();
|
|
463
|
+
|
|
464
|
+
expect(plans).toBeDefined();
|
|
465
|
+
expect(Array.isArray(plans)).toBe(true);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test('Event listeners can be unsubscribed', () => {
|
|
469
|
+
const fn = vi.fn();
|
|
470
|
+
const agent = createAgent({
|
|
471
|
+
id: 'test',
|
|
472
|
+
events: {},
|
|
473
|
+
model: {} as any,
|
|
474
|
+
});
|
|
427
475
|
|
|
428
|
-
|
|
476
|
+
const subscription = agent.on('message', fn);
|
|
429
477
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
478
|
+
agent.addMessage({
|
|
479
|
+
role: 'user',
|
|
480
|
+
content: [{ type: 'text', text: 'first message' }],
|
|
481
|
+
});
|
|
434
482
|
|
|
435
|
-
|
|
436
|
-
}
|
|
437
|
-
);
|
|
483
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
438
484
|
|
|
439
|
-
|
|
440
|
-
'can provide a parent correlation ID (%s)',
|
|
441
|
-
async (method) => {
|
|
442
|
-
const agent = createAgent({
|
|
443
|
-
model: {} as any,
|
|
444
|
-
events: {},
|
|
445
|
-
adapter: {
|
|
446
|
-
[method]: async (opts: any) => {
|
|
447
|
-
const res = {
|
|
448
|
-
text: 'response',
|
|
449
|
-
};
|
|
485
|
+
subscription.unsubscribe();
|
|
450
486
|
|
|
451
|
-
|
|
487
|
+
agent.addMessage({
|
|
488
|
+
role: 'user',
|
|
489
|
+
content: [{ type: 'text', text: 'second message' }],
|
|
490
|
+
});
|
|
452
491
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
} as any as AIAdapter,
|
|
456
|
-
});
|
|
492
|
+
expect(fn).toHaveBeenCalledTimes(1); // Still only called once
|
|
493
|
+
});
|
|
457
494
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
495
|
+
test('agent.observe() adds observations from actor snapshots', () => {
|
|
496
|
+
const machine = createMachine({
|
|
497
|
+
initial: 'idle',
|
|
498
|
+
states: {
|
|
499
|
+
idle: {
|
|
500
|
+
on: { START: 'running' },
|
|
501
|
+
},
|
|
502
|
+
running: {},
|
|
503
|
+
},
|
|
504
|
+
});
|
|
463
505
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
);
|
|
470
|
-
|
|
471
|
-
test.each(['generateText', 'streamText'] as const)(
|
|
472
|
-
'can add feedback to a correlation (%s)',
|
|
473
|
-
async (method) => {
|
|
474
|
-
const agent = createAgent({
|
|
475
|
-
name: 'test',
|
|
476
|
-
model: {} as any,
|
|
477
|
-
events: {},
|
|
478
|
-
adapter: {
|
|
479
|
-
[method]: async (opts: any) => {
|
|
480
|
-
const res = {
|
|
481
|
-
text: 'response',
|
|
482
|
-
};
|
|
483
|
-
|
|
484
|
-
opts.onFinish?.(res);
|
|
485
|
-
|
|
486
|
-
return res as AgentGenerateTextResult;
|
|
487
|
-
},
|
|
488
|
-
} as any as AIAdapter,
|
|
489
|
-
});
|
|
506
|
+
const agent = createAgent({
|
|
507
|
+
id: 'test',
|
|
508
|
+
events: {},
|
|
509
|
+
model: {} as any,
|
|
510
|
+
});
|
|
490
511
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
});
|
|
512
|
+
const actor = createActor(machine);
|
|
513
|
+
const subscription = agent.observe(actor);
|
|
494
514
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
reward: -1,
|
|
498
|
-
});
|
|
515
|
+
actor.start();
|
|
516
|
+
actor.send({ type: 'START' });
|
|
499
517
|
|
|
500
|
-
|
|
501
|
-
|
|
518
|
+
expect(agent.getObservations()).toContainEqual(
|
|
519
|
+
expect.objectContaining({
|
|
520
|
+
state: expect.objectContaining({ value: 'idle' }),
|
|
521
|
+
machineHash: expect.any(String),
|
|
522
|
+
})
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
expect(agent.getObservations()).toContainEqual(
|
|
526
|
+
expect.objectContaining({
|
|
527
|
+
prevState: expect.objectContaining({ value: 'idle' }),
|
|
528
|
+
event: { type: 'START' },
|
|
529
|
+
state: expect.objectContaining({ value: 'running' }),
|
|
530
|
+
machineHash: expect.any(String),
|
|
531
|
+
})
|
|
532
|
+
);
|
|
502
533
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
);
|
|
534
|
+
subscription.unsubscribe();
|
|
535
|
+
});
|