@statelyai/agent 2.0.0-next.1 → 2.0.0-next.3
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/grumpy-dolphins-think.md +17 -0
- package/.changeset/old-teachers-tap.md +5 -0
- package/.changeset/pink-eagles-deliver.md +13 -0
- package/.changeset/pre.json +9 -1
- package/.changeset/quiet-turtles-do.md +7 -0
- package/.changeset/smart-yaks-pull.md +23 -0
- package/.changeset/sweet-clouds-mix.md +16 -0
- package/.changeset/swift-mangos-rush.md +5 -0
- package/.changeset/tough-ways-rhyme.md +5 -0
- package/CHANGELOG.md +79 -0
- package/dist/index.d.mts +116 -100
- package/dist/index.d.ts +116 -100
- package/dist/index.js +102 -72
- package/dist/index.mjs +105 -75
- package/examples/chatbot.ts +2 -2
- package/examples/cot.ts +21 -73
- package/examples/customer-service-sim.ts +3 -3
- package/examples/email.ts +3 -5
- package/examples/example.ts +2 -2
- package/examples/goal.ts +2 -2
- package/examples/joke.ts +12 -12
- package/examples/jugs.ts +4 -7
- package/examples/learn-from-feedback.ts +123 -0
- package/examples/number.ts +2 -2
- package/examples/raffle.ts +2 -2
- package/examples/river-crossing.ts +4 -7
- package/examples/simple.ts +13 -10
- package/examples/summary.ts +2 -5
- package/examples/support.ts +38 -38
- package/examples/ticTacToe.ts +46 -4
- package/examples/todo.ts +3 -3
- package/examples/tutor.ts +2 -2
- package/examples/verify.ts +2 -2
- package/examples/weather-agent.ts +139 -0
- package/examples/weather.ts +26 -23
- package/examples/word.ts +8 -6
- package/package.json +2 -1
- package/src/agent.test.ts +37 -52
- package/src/agent.ts +93 -60
- package/src/decide.test.ts +56 -8
- package/src/decide.ts +42 -32
- package/src/strategies/chainOfThought.ts +50 -0
- package/src/{planners → strategies}/shortestPath.test.ts +4 -7
- package/src/strategies/shortestPath.ts +178 -0
- package/src/{planners → strategies}/simple.ts +25 -26
- package/src/templates/defaultText.ts +3 -0
- package/src/text.ts +13 -13
- package/src/types.ts +124 -83
- package/src/utils.ts +13 -1
- package/src/planners/shortestPath.ts +0 -177
- package/src/strategies/chain-of-note.ts +0 -106
package/examples/weather.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createAgent, fromDecision } from '../src';
|
|
1
|
+
import { createAgent, EventFromAgent, fromDecision } from '../src';
|
|
2
2
|
import { assign, createActor, fromPromise, log, setup } from 'xstate';
|
|
3
3
|
import { fromTerminal } from './helpers/helpers';
|
|
4
4
|
import { z } from 'zod';
|
|
@@ -75,10 +75,10 @@ const machine = setup({
|
|
|
75
75
|
types: {
|
|
76
76
|
context: {} as {
|
|
77
77
|
location: string;
|
|
78
|
-
history: string[];
|
|
79
78
|
count: number;
|
|
79
|
+
result: string | null;
|
|
80
80
|
},
|
|
81
|
-
events: agent
|
|
81
|
+
events: {} as EventFromAgent<typeof agent>,
|
|
82
82
|
},
|
|
83
83
|
actors: {
|
|
84
84
|
agent: fromDecision(agent),
|
|
@@ -90,7 +90,7 @@ const machine = setup({
|
|
|
90
90
|
context: {
|
|
91
91
|
location: '',
|
|
92
92
|
count: 0,
|
|
93
|
-
|
|
93
|
+
result: null,
|
|
94
94
|
},
|
|
95
95
|
states: {
|
|
96
96
|
getLocation: {
|
|
@@ -111,15 +111,6 @@ const machine = setup({
|
|
|
111
111
|
},
|
|
112
112
|
decide: {
|
|
113
113
|
entry: log('Deciding...'),
|
|
114
|
-
invoke: {
|
|
115
|
-
src: 'agent',
|
|
116
|
-
input: ({ context }) => ({
|
|
117
|
-
context: {
|
|
118
|
-
location: context.location,
|
|
119
|
-
},
|
|
120
|
-
goal: `Decide what to do based on the given location, which may or may not be a location`,
|
|
121
|
-
}),
|
|
122
|
-
},
|
|
123
114
|
on: {
|
|
124
115
|
'agent.getWeather': {
|
|
125
116
|
actions: log(({ event }) => event),
|
|
@@ -138,6 +129,7 @@ const machine = setup({
|
|
|
138
129
|
log(({ event }) => event.output),
|
|
139
130
|
assign({
|
|
140
131
|
count: ({ context }) => context.count + 1,
|
|
132
|
+
result: ({ event }) => event.output,
|
|
141
133
|
}),
|
|
142
134
|
],
|
|
143
135
|
target: 'reportWeather',
|
|
@@ -145,13 +137,6 @@ const machine = setup({
|
|
|
145
137
|
},
|
|
146
138
|
},
|
|
147
139
|
reportWeather: {
|
|
148
|
-
invoke: {
|
|
149
|
-
src: 'agent',
|
|
150
|
-
input: ({ context }) => ({
|
|
151
|
-
goal: 'Report the weather', // TODO
|
|
152
|
-
context,
|
|
153
|
-
}),
|
|
154
|
-
},
|
|
155
140
|
on: {
|
|
156
141
|
'agent.reportWeather': {
|
|
157
142
|
actions: log(({ event }) => event),
|
|
@@ -169,7 +154,25 @@ const machine = setup({
|
|
|
169
154
|
});
|
|
170
155
|
|
|
171
156
|
const actor = createActor(machine);
|
|
172
|
-
actor.subscribe((s) => {
|
|
173
|
-
console.log(s.value);
|
|
174
|
-
});
|
|
175
157
|
actor.start();
|
|
158
|
+
|
|
159
|
+
agent.interact(actor, ({ state }) => {
|
|
160
|
+
if (state.matches('decide')) {
|
|
161
|
+
return {
|
|
162
|
+
goal: `Decide what to do based on the given location, which may or may not be a location`,
|
|
163
|
+
context: {
|
|
164
|
+
location: state.context.location,
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (state.matches('reportWeather')) {
|
|
170
|
+
return {
|
|
171
|
+
goal: `Report the weather for the given location`,
|
|
172
|
+
context: {
|
|
173
|
+
location: state.context.location,
|
|
174
|
+
result: state.context.result,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
});
|
package/examples/word.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { assign, createActor, log, setup } from 'xstate';
|
|
2
2
|
import { fromTerminal } from './helpers/helpers';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ContextFromAgent,
|
|
5
|
+
createAgent,
|
|
6
|
+
fromDecision,
|
|
7
|
+
TypesFromAgent,
|
|
8
|
+
} from '../src';
|
|
4
9
|
import { z } from 'zod';
|
|
5
10
|
import { openai } from '@ai-sdk/openai';
|
|
6
11
|
|
|
@@ -36,13 +41,10 @@ const context = {
|
|
|
36
41
|
word: null,
|
|
37
42
|
guessedWord: null,
|
|
38
43
|
lettersGuessed: [],
|
|
39
|
-
} satisfies typeof agent
|
|
44
|
+
} satisfies ContextFromAgent<typeof agent>;
|
|
40
45
|
|
|
41
46
|
const wordGuesserMachine = setup({
|
|
42
|
-
types: {
|
|
43
|
-
context: agent.types.context,
|
|
44
|
-
events: agent.types.events,
|
|
45
|
-
},
|
|
47
|
+
types: {} as TypesFromAgent<typeof agent>,
|
|
46
48
|
actors: {
|
|
47
49
|
agent: fromDecision(agent),
|
|
48
50
|
getFromTerminal: fromTerminal,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/agent",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.3",
|
|
4
4
|
"description": "Stateful agents that make decisions based on finite-state machine models",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"author": "David Khourshid <david@stately.ai>",
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"devDependencies": {
|
|
18
|
+
"@ai-sdk/anthropic": "^0.0.54",
|
|
18
19
|
"@ai-sdk/openai": "^0.0.40",
|
|
19
20
|
"@changesets/changelog-github": "^0.5.0",
|
|
20
21
|
"@changesets/cli": "^2.27.9",
|
package/src/agent.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { test, expect, vi } from 'vitest';
|
|
2
|
-
import { createAgent } from './';
|
|
2
|
+
import { createAgent, TypesFromAgent } from './';
|
|
3
3
|
import { createActor, createMachine } from 'xstate';
|
|
4
4
|
import { LanguageModelV1CallOptions } from 'ai';
|
|
5
5
|
import { z } from 'zod';
|
|
@@ -17,12 +17,12 @@ test('an agent has the expected interface', () => {
|
|
|
17
17
|
expect(agent.addMessage).toBeDefined();
|
|
18
18
|
expect(agent.addObservation).toBeDefined();
|
|
19
19
|
expect(agent.addFeedback).toBeDefined();
|
|
20
|
-
expect(agent.
|
|
20
|
+
expect(agent.addDecision).toBeDefined();
|
|
21
21
|
|
|
22
22
|
expect(agent.getMessages).toBeDefined();
|
|
23
23
|
expect(agent.getObservations).toBeDefined();
|
|
24
24
|
expect(agent.getFeedback).toBeDefined();
|
|
25
|
-
expect(agent.
|
|
25
|
+
expect(agent.getDecisions).toBeDefined();
|
|
26
26
|
|
|
27
27
|
expect(agent.interact).toBeDefined();
|
|
28
28
|
});
|
|
@@ -70,34 +70,24 @@ test('agent.addFeedback() adds to feedback', () => {
|
|
|
70
70
|
model: {} as any,
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
},
|
|
73
|
+
const obs = agent.addObservation({
|
|
74
|
+
prevState: { value: 'playing' },
|
|
75
|
+
state: { value: 'lost' },
|
|
76
|
+
event: { type: 'play', position: 3 },
|
|
77
77
|
goal: 'Win the game',
|
|
78
|
-
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const feedback = agent.addFeedback({
|
|
81
|
+
score: 0,
|
|
82
|
+
observationId: obs.id,
|
|
79
83
|
});
|
|
80
84
|
|
|
81
85
|
expect(feedback.episodeId).toEqual(agent.episodeId);
|
|
82
86
|
|
|
83
87
|
expect(agent.getFeedback()).toContainEqual(
|
|
84
88
|
expect.objectContaining({
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
},
|
|
88
|
-
goal: 'Win the game',
|
|
89
|
-
observationId: 'obs-1',
|
|
90
|
-
episodeId: expect.any(String),
|
|
91
|
-
timestamp: expect.any(Number),
|
|
92
|
-
})
|
|
93
|
-
);
|
|
94
|
-
expect(agent.getFeedback()).toContainEqual(
|
|
95
|
-
expect.objectContaining({
|
|
96
|
-
attributes: {
|
|
97
|
-
score: -1,
|
|
98
|
-
},
|
|
99
|
-
goal: 'Win the game',
|
|
100
|
-
observationId: 'obs-1',
|
|
89
|
+
score: 0,
|
|
90
|
+
observationId: obs.id,
|
|
101
91
|
episodeId: expect.any(String),
|
|
102
92
|
timestamp: expect.any(Number),
|
|
103
93
|
})
|
|
@@ -115,6 +105,7 @@ test('agent.addObservation() adds to observations', () => {
|
|
|
115
105
|
prevState: { value: 'playing', context: {} },
|
|
116
106
|
event: { type: 'play', position: 3 },
|
|
117
107
|
state: { value: 'lost', context: {} },
|
|
108
|
+
goal: 'Win the game',
|
|
118
109
|
});
|
|
119
110
|
|
|
120
111
|
expect(observation.episodeId).toEqual(agent.episodeId);
|
|
@@ -139,6 +130,7 @@ test('agent.addObservation() adds to observations (initial state)', () => {
|
|
|
139
130
|
|
|
140
131
|
const observation = agent.addObservation({
|
|
141
132
|
state: { value: 'lost' },
|
|
133
|
+
goal: 'Win the game',
|
|
142
134
|
});
|
|
143
135
|
|
|
144
136
|
expect(observation.episodeId).toEqual(agent.episodeId);
|
|
@@ -176,6 +168,7 @@ test('agent.addObservation() adds to observations with machine hash', () => {
|
|
|
176
168
|
event: { type: 'play', position: 3 },
|
|
177
169
|
state: { value: 'lost', context: {} },
|
|
178
170
|
machine,
|
|
171
|
+
goal: 'Win the game',
|
|
179
172
|
});
|
|
180
173
|
|
|
181
174
|
expect(observation.episodeId).toEqual(agent.episodeId);
|
|
@@ -203,13 +196,11 @@ test('agent.addFeedback() adds to feedback (with observation)', () => {
|
|
|
203
196
|
state: {
|
|
204
197
|
value: 'playing',
|
|
205
198
|
},
|
|
199
|
+
goal: 'Win the game',
|
|
206
200
|
});
|
|
207
201
|
|
|
208
202
|
const feedback = agent.addFeedback({
|
|
209
|
-
|
|
210
|
-
score: -1,
|
|
211
|
-
},
|
|
212
|
-
goal: 'Win the game',
|
|
203
|
+
score: 0,
|
|
213
204
|
observationId: observation.id,
|
|
214
205
|
});
|
|
215
206
|
|
|
@@ -217,10 +208,7 @@ test('agent.addFeedback() adds to feedback (with observation)', () => {
|
|
|
217
208
|
|
|
218
209
|
expect(agent.getFeedback()).toContainEqual(
|
|
219
210
|
expect.objectContaining({
|
|
220
|
-
|
|
221
|
-
score: -1,
|
|
222
|
-
},
|
|
223
|
-
goal: 'Win the game',
|
|
211
|
+
score: 0,
|
|
224
212
|
observationId: observation.id,
|
|
225
213
|
episodeId: expect.any(String),
|
|
226
214
|
timestamp: expect.any(Number),
|
|
@@ -228,10 +216,7 @@ test('agent.addFeedback() adds to feedback (with observation)', () => {
|
|
|
228
216
|
);
|
|
229
217
|
expect(agent.getFeedback()).toContainEqual(
|
|
230
218
|
expect.objectContaining({
|
|
231
|
-
|
|
232
|
-
score: -1,
|
|
233
|
-
},
|
|
234
|
-
goal: 'Win the game',
|
|
219
|
+
score: 0,
|
|
235
220
|
observationId: observation.id,
|
|
236
221
|
episodeId: expect.any(String),
|
|
237
222
|
timestamp: expect.any(Number),
|
|
@@ -297,17 +282,14 @@ test('You can listen for feedback events', () => {
|
|
|
297
282
|
agent.on('feedback', fn);
|
|
298
283
|
|
|
299
284
|
agent.addFeedback({
|
|
300
|
-
|
|
301
|
-
score: -1,
|
|
302
|
-
},
|
|
303
|
-
goal: 'Win the game',
|
|
285
|
+
score: -1,
|
|
304
286
|
observationId: 'obs-1',
|
|
305
287
|
});
|
|
306
288
|
|
|
307
289
|
expect(fn).toHaveBeenCalled();
|
|
308
290
|
});
|
|
309
291
|
|
|
310
|
-
test('You can listen for
|
|
292
|
+
test('You can listen for decision events', async () => {
|
|
311
293
|
const fn = vi.fn();
|
|
312
294
|
const model = new MockLanguageModelV1({
|
|
313
295
|
doGenerate: async (params: LanguageModelV1CallOptions) => {
|
|
@@ -339,7 +321,7 @@ test('You can listen for plan events', async () => {
|
|
|
339
321
|
},
|
|
340
322
|
});
|
|
341
323
|
|
|
342
|
-
agent.on('
|
|
324
|
+
agent.on('decision', fn);
|
|
343
325
|
|
|
344
326
|
await agent.decide({
|
|
345
327
|
goal: 'Win the game',
|
|
@@ -364,7 +346,7 @@ test('You can listen for plan events', async () => {
|
|
|
364
346
|
|
|
365
347
|
expect(fn).toHaveBeenCalledWith(
|
|
366
348
|
expect.objectContaining({
|
|
367
|
-
|
|
349
|
+
decision: expect.objectContaining({
|
|
368
350
|
nextEvent: {
|
|
369
351
|
type: 'WIN',
|
|
370
352
|
},
|
|
@@ -386,12 +368,14 @@ test('agent.types provides context and event types', () => {
|
|
|
386
368
|
},
|
|
387
369
|
});
|
|
388
370
|
|
|
389
|
-
|
|
371
|
+
let types = {} as TypesFromAgent<typeof agent>;
|
|
372
|
+
|
|
373
|
+
types satisfies { context: any; events: any };
|
|
390
374
|
|
|
391
|
-
|
|
375
|
+
types.context satisfies { score: number };
|
|
392
376
|
|
|
393
377
|
// @ts-expect-error
|
|
394
|
-
|
|
378
|
+
types.context satisfies { score: string };
|
|
395
379
|
});
|
|
396
380
|
|
|
397
381
|
test('It allows unrecognized events', () => {
|
|
@@ -436,15 +420,16 @@ test('You can listen for message events', () => {
|
|
|
436
420
|
);
|
|
437
421
|
});
|
|
438
422
|
|
|
439
|
-
test('agent.
|
|
423
|
+
test('agent.getDecisions() returns decisions from context', () => {
|
|
440
424
|
const agent = createAgent({
|
|
441
425
|
id: 'test',
|
|
442
426
|
events: {},
|
|
443
427
|
model: {} as any,
|
|
444
|
-
|
|
428
|
+
strategy: async (agent) => {
|
|
445
429
|
return {
|
|
430
|
+
id: Date.now().toString(),
|
|
446
431
|
episodeId: agent.episodeId,
|
|
447
|
-
|
|
432
|
+
strategy: 'test-strategy',
|
|
448
433
|
goal: '',
|
|
449
434
|
goalState: undefined,
|
|
450
435
|
paths: [
|
|
@@ -459,10 +444,10 @@ test('agent.getPlans() returns plans from context', () => {
|
|
|
459
444
|
},
|
|
460
445
|
});
|
|
461
446
|
|
|
462
|
-
const
|
|
447
|
+
const decisions = agent.getDecisions();
|
|
463
448
|
|
|
464
|
-
expect(
|
|
465
|
-
expect(Array.isArray(
|
|
449
|
+
expect(decisions).toBeDefined();
|
|
450
|
+
expect(Array.isArray(decisions)).toBe(true);
|
|
466
451
|
});
|
|
467
452
|
|
|
468
453
|
test('Event listeners can be unsubscribed', () => {
|