@statelyai/agent 1.0.0-beta.1 → 1.0.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/.changeset/heavy-moons-bake.md +12 -0
- package/.changeset/silly-berries-drop.md +5 -0
- package/.changeset/wild-bobcats-care.md +26 -0
- package/.env.template +0 -3
- package/.github/workflows/release.yml +3 -3
- package/dist/index.d.mts +135 -44
- package/dist/index.d.ts +135 -44
- package/dist/index.js +155 -103
- package/dist/index.mjs +145 -101
- package/examples/chatbot.ts +9 -17
- package/examples/cot.ts +5 -7
- package/examples/email.ts +2 -2
- package/examples/example.ts +7 -7
- package/examples/goal.ts +1 -1
- package/examples/joke.ts +8 -10
- package/examples/number.ts +1 -1
- package/examples/raffle.ts +1 -1
- package/examples/sandbox.ts +28 -0
- package/examples/support.ts +1 -1
- package/examples/ticTacToe.ts +18 -21
- package/examples/todo.ts +3 -1
- package/examples/tutor.ts +1 -1
- package/examples/verify.ts +1 -1
- package/examples/weather.ts +1 -1
- package/examples/wiki.ts +1 -1
- package/examples/word.ts +1 -1
- package/package.json +8 -6
- package/src/agent.test.ts +176 -4
- package/src/agent.ts +51 -16
- package/src/decision.ts +6 -5
- package/src/index.ts +2 -2
- package/src/planners/shortestPathPlanner.ts +2 -2
- package/src/planners/simplePlanner.ts +22 -11
- package/src/schemas.ts +4 -8
- package/src/strategies/chain-of-note.ts +3 -3
- package/src/text.ts +15 -27
- package/src/types.ts +114 -32
- package/src/utils.ts +53 -9
- package/src/templates/defaultToolCall.ts +0 -10
package/examples/verify.ts
CHANGED
package/examples/weather.ts
CHANGED
package/examples/wiki.ts
CHANGED
package/examples/word.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/agent",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
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",
|
|
@@ -27,16 +27,17 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@changesets/changelog-github": "^0.5.0",
|
|
30
|
-
"@changesets/cli": "^2.27.
|
|
30
|
+
"@changesets/cli": "^2.27.7",
|
|
31
31
|
"@langchain/community": "^0.0.53",
|
|
32
32
|
"@langchain/core": "^0.1.63",
|
|
33
33
|
"@langchain/openai": "^0.0.28",
|
|
34
|
-
"@types/node": "^20.14.
|
|
34
|
+
"@types/node": "^20.14.10",
|
|
35
|
+
"@types/object-hash": "^3.0.6",
|
|
35
36
|
"dotenv": "^16.4.5",
|
|
36
37
|
"json-schema-to-ts": "^3.1.0",
|
|
37
38
|
"ts-node": "^10.9.2",
|
|
38
39
|
"tsup": "^8.1.0",
|
|
39
|
-
"typescript": "^5.
|
|
40
|
+
"typescript": "^5.5.3",
|
|
40
41
|
"vitest": "^1.6.0",
|
|
41
42
|
"wikipedia": "^2.1.2",
|
|
42
43
|
"zod": "^3.23.8"
|
|
@@ -47,8 +48,9 @@
|
|
|
47
48
|
"dependencies": {
|
|
48
49
|
"@ai-sdk/openai": "^0.0.31",
|
|
49
50
|
"@xstate/graph": "^2.0.0",
|
|
50
|
-
"ai": "^3.2.
|
|
51
|
-
"
|
|
51
|
+
"ai": "^3.2.22",
|
|
52
|
+
"object-hash": "^3.0.0",
|
|
53
|
+
"xstate": "^5.15.0"
|
|
52
54
|
},
|
|
53
55
|
"packageManager": "pnpm@8.11.0"
|
|
54
56
|
}
|
package/src/agent.test.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { test, expect } from 'vitest';
|
|
1
|
+
import { test, expect, vi } from 'vitest';
|
|
2
2
|
import { createAgent, type AIAdapter } from './';
|
|
3
3
|
import { createActor, createMachine } from 'xstate';
|
|
4
|
+
import { GenerateTextResult } from 'ai';
|
|
5
|
+
import { z } from 'zod';
|
|
4
6
|
|
|
5
7
|
test('an agent has the expected interface', () => {
|
|
6
8
|
const agent = createAgent({
|
|
@@ -13,11 +15,16 @@ test('an agent has the expected interface', () => {
|
|
|
13
15
|
expect(agent.generateText).toBeDefined();
|
|
14
16
|
expect(agent.streamText).toBeDefined();
|
|
15
17
|
|
|
16
|
-
expect(agent.addFeedback).toBeDefined();
|
|
17
18
|
expect(agent.addMessage).toBeDefined();
|
|
18
19
|
expect(agent.addObservation).toBeDefined();
|
|
20
|
+
expect(agent.addFeedback).toBeDefined();
|
|
19
21
|
expect(agent.addPlan).toBeDefined();
|
|
20
22
|
|
|
23
|
+
expect(agent.getMessages).toBeDefined();
|
|
24
|
+
expect(agent.getObservations).toBeDefined();
|
|
25
|
+
expect(agent.getFeedback).toBeDefined();
|
|
26
|
+
expect(agent.getPlans).toBeDefined();
|
|
27
|
+
|
|
21
28
|
expect(agent.interact).toBeDefined();
|
|
22
29
|
});
|
|
23
30
|
|
|
@@ -45,6 +52,11 @@ test('agent.addMessage() adds to message history', () => {
|
|
|
45
52
|
content: 'msg 1',
|
|
46
53
|
})
|
|
47
54
|
);
|
|
55
|
+
expect(agent.getMessages()).toContainEqual(
|
|
56
|
+
expect.objectContaining({
|
|
57
|
+
content: 'msg 1',
|
|
58
|
+
})
|
|
59
|
+
);
|
|
48
60
|
|
|
49
61
|
expect(agent.select((c) => c.messages)).toContainEqual(
|
|
50
62
|
expect.objectContaining({
|
|
@@ -53,6 +65,13 @@ test('agent.addMessage() adds to message history', () => {
|
|
|
53
65
|
timestamp: expect.any(Number),
|
|
54
66
|
})
|
|
55
67
|
);
|
|
68
|
+
expect(agent.getMessages()).toContainEqual(
|
|
69
|
+
expect.objectContaining({
|
|
70
|
+
content: 'response 1',
|
|
71
|
+
sessionId: expect.any(String),
|
|
72
|
+
timestamp: expect.any(Number),
|
|
73
|
+
})
|
|
74
|
+
);
|
|
56
75
|
});
|
|
57
76
|
|
|
58
77
|
test('agent.addFeedback() adds to feedback', () => {
|
|
@@ -83,6 +102,17 @@ test('agent.addFeedback() adds to feedback', () => {
|
|
|
83
102
|
timestamp: expect.any(Number),
|
|
84
103
|
})
|
|
85
104
|
);
|
|
105
|
+
expect(agent.getFeedback()).toContainEqual(
|
|
106
|
+
expect.objectContaining({
|
|
107
|
+
attributes: {
|
|
108
|
+
score: -1,
|
|
109
|
+
},
|
|
110
|
+
goal: 'Win the game',
|
|
111
|
+
observationId: 'obs-1',
|
|
112
|
+
sessionId: expect.any(String),
|
|
113
|
+
timestamp: expect.any(Number),
|
|
114
|
+
})
|
|
115
|
+
);
|
|
86
116
|
});
|
|
87
117
|
|
|
88
118
|
test('agent.addObservation() adds to observations', () => {
|
|
@@ -111,6 +141,46 @@ test('agent.addObservation() adds to observations', () => {
|
|
|
111
141
|
);
|
|
112
142
|
});
|
|
113
143
|
|
|
144
|
+
test('agent.addObservation() adds to observations with machine hash', () => {
|
|
145
|
+
const agent = createAgent({
|
|
146
|
+
name: 'test',
|
|
147
|
+
events: {},
|
|
148
|
+
model: {} as any,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const machine = createMachine({
|
|
152
|
+
initial: 'playing',
|
|
153
|
+
states: {
|
|
154
|
+
playing: {
|
|
155
|
+
on: {
|
|
156
|
+
play: 'lost',
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
lost: {},
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const observation = agent.addObservation({
|
|
164
|
+
prevState: { value: 'playing', context: {} },
|
|
165
|
+
event: { type: 'play', position: 3 },
|
|
166
|
+
state: { value: 'lost', context: {} },
|
|
167
|
+
machine,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(observation.sessionId).toEqual(agent.sessionId);
|
|
171
|
+
|
|
172
|
+
expect(agent.select((c) => c.observations)).toContainEqual(
|
|
173
|
+
expect.objectContaining({
|
|
174
|
+
prevState: { value: 'playing', context: {} },
|
|
175
|
+
event: { type: 'play', position: 3 },
|
|
176
|
+
state: { value: 'lost', context: {} },
|
|
177
|
+
machineHash: expect.any(String),
|
|
178
|
+
sessionId: expect.any(String),
|
|
179
|
+
timestamp: expect.any(Number),
|
|
180
|
+
})
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
|
|
114
184
|
test('agent.interact() observes machine actors (no 2nd arg)', () => {
|
|
115
185
|
const machine = createMachine({
|
|
116
186
|
initial: 'a',
|
|
@@ -140,6 +210,12 @@ test('agent.interact() observes machine actors (no 2nd arg)', () => {
|
|
|
140
210
|
state: expect.objectContaining({ value: 'a' }),
|
|
141
211
|
})
|
|
142
212
|
);
|
|
213
|
+
expect(agent.getObservations()).toContainEqual(
|
|
214
|
+
expect.objectContaining({
|
|
215
|
+
prevState: undefined,
|
|
216
|
+
state: expect.objectContaining({ value: 'a' }),
|
|
217
|
+
})
|
|
218
|
+
);
|
|
143
219
|
|
|
144
220
|
actor.send({ type: 'NEXT' });
|
|
145
221
|
|
|
@@ -175,7 +251,8 @@ test('Agents can use a custom adapter', async () => {
|
|
|
175
251
|
expect(res.text).toEqual('Response');
|
|
176
252
|
});
|
|
177
253
|
|
|
178
|
-
test
|
|
254
|
+
test('You can listen for feedback events', () => {
|
|
255
|
+
const fn = vi.fn();
|
|
179
256
|
const agent = createAgent({
|
|
180
257
|
name: 'test',
|
|
181
258
|
events: {},
|
|
@@ -183,5 +260,100 @@ test.skip('You can listen for emitted agent events', () => {
|
|
|
183
260
|
model: {} as any,
|
|
184
261
|
});
|
|
185
262
|
|
|
186
|
-
agent.on('feedback',
|
|
263
|
+
agent.on('feedback', fn);
|
|
264
|
+
|
|
265
|
+
agent.addFeedback({
|
|
266
|
+
attributes: {
|
|
267
|
+
score: -1,
|
|
268
|
+
},
|
|
269
|
+
goal: 'Win the game',
|
|
270
|
+
observationId: 'obs-1',
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
expect(fn).toHaveBeenCalled();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test('You can listen for plan events', async () => {
|
|
277
|
+
const fn = vi.fn();
|
|
278
|
+
const agent = createAgent({
|
|
279
|
+
name: 'test',
|
|
280
|
+
model: {} as any,
|
|
281
|
+
events: {
|
|
282
|
+
WIN: z.object({}),
|
|
283
|
+
},
|
|
284
|
+
adapter: {
|
|
285
|
+
generateText: async (arg) => {
|
|
286
|
+
const keys = Object.keys(arg.tools!);
|
|
287
|
+
|
|
288
|
+
if (keys.length !== 1) {
|
|
289
|
+
throw new Error('Expected only 1 choice');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
toolResults: [
|
|
294
|
+
{
|
|
295
|
+
result: {
|
|
296
|
+
type: keys[0],
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
} as any as GenerateTextResult<any>;
|
|
301
|
+
},
|
|
302
|
+
streamText: {} as any,
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
agent.on('plan', fn);
|
|
307
|
+
|
|
308
|
+
await agent.decide({
|
|
309
|
+
goal: 'Win the game',
|
|
310
|
+
state: {
|
|
311
|
+
value: 'playing',
|
|
312
|
+
context: {},
|
|
313
|
+
},
|
|
314
|
+
machine: createMachine({
|
|
315
|
+
initial: 'playing',
|
|
316
|
+
states: {
|
|
317
|
+
playing: {
|
|
318
|
+
on: {
|
|
319
|
+
WIN: {
|
|
320
|
+
target: 'won',
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
won: {},
|
|
325
|
+
},
|
|
326
|
+
}),
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
expect(fn).toHaveBeenCalledWith(
|
|
330
|
+
expect.objectContaining({
|
|
331
|
+
plan: expect.objectContaining({
|
|
332
|
+
nextEvent: {
|
|
333
|
+
type: 'WIN',
|
|
334
|
+
},
|
|
335
|
+
}),
|
|
336
|
+
})
|
|
337
|
+
);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test('agent.types provides context and event types', () => {
|
|
341
|
+
const agent = createAgent({
|
|
342
|
+
model: {} as any,
|
|
343
|
+
events: {
|
|
344
|
+
setScore: z.object({
|
|
345
|
+
score: z.number(),
|
|
346
|
+
}),
|
|
347
|
+
},
|
|
348
|
+
context: {
|
|
349
|
+
score: z.number(),
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
agent.types satisfies { context: any; events: any };
|
|
354
|
+
|
|
355
|
+
agent.types.context satisfies { score: number };
|
|
356
|
+
|
|
357
|
+
// @ts-expect-error
|
|
358
|
+
agent.types.context satisfies { score: string };
|
|
187
359
|
});
|
package/src/agent.ts
CHANGED
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
Observer,
|
|
8
8
|
toObserver,
|
|
9
9
|
} from 'xstate';
|
|
10
|
-
import { ZodEventMapping } from './schemas';
|
|
10
|
+
import { ZodContextMapping, ZodEventMapping } from './schemas';
|
|
11
11
|
import {
|
|
12
12
|
Agent,
|
|
13
13
|
AgentLogic,
|
|
14
|
-
|
|
14
|
+
AgentMessage,
|
|
15
15
|
AgentPlanner,
|
|
16
16
|
EventsFromZodEventMapping,
|
|
17
17
|
GenerateTextOptions,
|
|
@@ -20,12 +20,14 @@ import {
|
|
|
20
20
|
ObservedState,
|
|
21
21
|
AgentObservationInput,
|
|
22
22
|
AgentMemoryContext,
|
|
23
|
+
AgentObservation,
|
|
24
|
+
ContextFromZodContextMapping,
|
|
23
25
|
} from './types';
|
|
24
26
|
import { simplePlanner } from './planners/simplePlanner';
|
|
25
27
|
import { agentGenerateText, agentStreamText } from './text';
|
|
26
28
|
import { agentDecide } from './decision';
|
|
27
29
|
import { vercelAdapter } from './adapters/vercel';
|
|
28
|
-
import { randomId } from './utils';
|
|
30
|
+
import { getMachineHash, randomId } from './utils';
|
|
29
31
|
|
|
30
32
|
export const agentLogic: AgentLogic<AnyEventObject> = fromTransition(
|
|
31
33
|
(state, event, { emit }) => {
|
|
@@ -80,24 +82,42 @@ export const agentLogic: AgentLogic<AnyEventObject> = fromTransition(
|
|
|
80
82
|
);
|
|
81
83
|
|
|
82
84
|
export function createAgent<
|
|
85
|
+
const TContextSchema extends ZodContextMapping,
|
|
83
86
|
const TEventSchemas extends ZodEventMapping,
|
|
84
|
-
TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas
|
|
87
|
+
TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas>,
|
|
88
|
+
TContext = ContextFromZodContextMapping<TContextSchema>
|
|
85
89
|
>({
|
|
86
90
|
name,
|
|
87
91
|
description,
|
|
88
92
|
model,
|
|
89
93
|
events,
|
|
90
|
-
|
|
94
|
+
context,
|
|
95
|
+
planner = simplePlanner as AgentPlanner<Agent<TContext, TEvents>>,
|
|
91
96
|
stringify = JSON.stringify,
|
|
92
97
|
getMemory,
|
|
93
98
|
logic = agentLogic as AgentLogic<TEvents>,
|
|
94
99
|
adapter = vercelAdapter,
|
|
95
100
|
...generateTextOptions
|
|
96
101
|
}: {
|
|
102
|
+
/**
|
|
103
|
+
* The unique identifier for the agent.
|
|
104
|
+
*
|
|
105
|
+
* This should be the same across all sessions of a specific agent, as it can be
|
|
106
|
+
* used to retrieve memory for this agent.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* const agent = createAgent({
|
|
111
|
+
* id: 'recipe-assistant',
|
|
112
|
+
* // ...
|
|
113
|
+
* });
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
id?: string;
|
|
97
117
|
/**
|
|
98
118
|
* The name of the agent
|
|
99
119
|
*/
|
|
100
|
-
name
|
|
120
|
+
name?: string;
|
|
101
121
|
/**
|
|
102
122
|
* A description of the role of the agent
|
|
103
123
|
*/
|
|
@@ -107,21 +127,22 @@ export function createAgent<
|
|
|
107
127
|
* that the agent knows about.
|
|
108
128
|
*/
|
|
109
129
|
events: TEventSchemas;
|
|
110
|
-
|
|
130
|
+
context?: TContextSchema;
|
|
131
|
+
planner?: AgentPlanner<Agent<TContext, TEvents>>;
|
|
111
132
|
stringify?: typeof JSON.stringify;
|
|
112
133
|
/**
|
|
113
134
|
* A function that retrieves the agent's long term memory
|
|
114
135
|
*/
|
|
115
|
-
getMemory?: (agent: Agent<
|
|
136
|
+
getMemory?: (agent: Agent<TContext, TEvents>) => AgentLongTermMemory;
|
|
116
137
|
/**
|
|
117
138
|
* Agent logic
|
|
118
139
|
*/
|
|
119
140
|
logic?: AgentLogic<TEvents>;
|
|
120
141
|
adapter?: AIAdapter;
|
|
121
|
-
} & GenerateTextOptions): Agent<TEvents> {
|
|
122
|
-
const messageHistoryListeners: Observer<
|
|
142
|
+
} & GenerateTextOptions): Agent<TContext, TEvents> {
|
|
143
|
+
const messageHistoryListeners: Observer<AgentMessage>[] = [];
|
|
123
144
|
|
|
124
|
-
const agent = createActor(logic) as unknown as Agent<TEvents>;
|
|
145
|
+
const agent = createActor(logic) as unknown as Agent<TContext, TEvents>;
|
|
125
146
|
agent.events = events;
|
|
126
147
|
agent.model = model;
|
|
127
148
|
agent.name = name;
|
|
@@ -155,6 +176,7 @@ export function createAgent<
|
|
|
155
176
|
|
|
156
177
|
return message;
|
|
157
178
|
};
|
|
179
|
+
agent.getMessages = () => agent.getSnapshot().context.messages;
|
|
158
180
|
|
|
159
181
|
agent.generateText = (opts) => agentGenerateText(agent, opts);
|
|
160
182
|
|
|
@@ -172,14 +194,21 @@ export function createAgent<
|
|
|
172
194
|
});
|
|
173
195
|
return feedback;
|
|
174
196
|
};
|
|
197
|
+
agent.getFeedback = () => agent.getSnapshot().context.feedback;
|
|
175
198
|
|
|
176
199
|
agent.addObservation = (observationInput) => {
|
|
200
|
+
const { prevState, event, state } = observationInput;
|
|
177
201
|
const observation = {
|
|
178
|
-
|
|
202
|
+
prevState,
|
|
203
|
+
event,
|
|
204
|
+
state,
|
|
179
205
|
id: observationInput.id ?? randomId(),
|
|
180
206
|
sessionId: agent.sessionId,
|
|
181
207
|
timestamp: observationInput.timestamp ?? Date.now(),
|
|
182
|
-
|
|
208
|
+
machineHash: observationInput.machine
|
|
209
|
+
? getMachineHash(observationInput.machine)
|
|
210
|
+
: undefined,
|
|
211
|
+
} satisfies AgentObservation<any>;
|
|
183
212
|
|
|
184
213
|
agent.send({
|
|
185
214
|
type: 'agent.observe',
|
|
@@ -188,6 +217,7 @@ export function createAgent<
|
|
|
188
217
|
|
|
189
218
|
return observation;
|
|
190
219
|
};
|
|
220
|
+
agent.getObservations = () => agent.getSnapshot().context.observations;
|
|
191
221
|
|
|
192
222
|
agent.addPlan = (plan) => {
|
|
193
223
|
agent.send({
|
|
@@ -195,8 +225,9 @@ export function createAgent<
|
|
|
195
225
|
plan,
|
|
196
226
|
});
|
|
197
227
|
};
|
|
228
|
+
agent.getPlans = () => agent.getSnapshot().context.plans;
|
|
198
229
|
|
|
199
|
-
agent.interact = (actorRef, getInput) => {
|
|
230
|
+
agent.interact = ((actorRef, getInput) => {
|
|
200
231
|
let prevState: ObservedState | undefined = undefined;
|
|
201
232
|
let subscribed = true;
|
|
202
233
|
|
|
@@ -234,7 +265,8 @@ export function createAgent<
|
|
|
234
265
|
event: inspEvent.event,
|
|
235
266
|
prevState,
|
|
236
267
|
state: inspEvent.snapshot as any,
|
|
237
|
-
|
|
268
|
+
machine: (actorRef as any).src,
|
|
269
|
+
} satisfies AgentObservationInput;
|
|
238
270
|
|
|
239
271
|
await handleObservation(observationInput);
|
|
240
272
|
},
|
|
@@ -246,6 +278,7 @@ export function createAgent<
|
|
|
246
278
|
prevState: undefined,
|
|
247
279
|
event: { type: '' }, // TODO: unknown events?
|
|
248
280
|
state: actorRef.getSnapshot(),
|
|
281
|
+
machine: (actorRef as any).src,
|
|
249
282
|
});
|
|
250
283
|
}
|
|
251
284
|
|
|
@@ -254,7 +287,9 @@ export function createAgent<
|
|
|
254
287
|
subscribed = false;
|
|
255
288
|
}, // TODO: make this actually unsubscribe
|
|
256
289
|
};
|
|
257
|
-
};
|
|
290
|
+
}) as typeof agent.interact;
|
|
291
|
+
|
|
292
|
+
agent.types = {} as any;
|
|
258
293
|
|
|
259
294
|
agent.start();
|
|
260
295
|
|
package/src/decision.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { AnyMachineSnapshot, fromPromise } from 'xstate';
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
AnyAgent,
|
|
4
4
|
AgentDecideOptions,
|
|
5
5
|
AgentDecisionLogic,
|
|
6
6
|
AgentDecisionInput,
|
|
7
7
|
AgentPlanner,
|
|
8
|
+
AgentPlan,
|
|
8
9
|
} from './types';
|
|
9
10
|
import { simplePlanner } from './planners/simplePlanner';
|
|
10
11
|
|
|
11
|
-
export async function agentDecide<T extends
|
|
12
|
+
export async function agentDecide<T extends AnyAgent>(
|
|
12
13
|
agent: T,
|
|
13
14
|
options: AgentDecideOptions
|
|
14
|
-
) {
|
|
15
|
+
): Promise<AgentPlan<any> | undefined> {
|
|
15
16
|
const resolvedOptions = {
|
|
16
17
|
...agent.defaultOptions,
|
|
17
18
|
...options,
|
|
@@ -44,9 +45,9 @@ export async function agentDecide<T extends Agent<any>>(
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
export function fromDecision(
|
|
47
|
-
agent:
|
|
48
|
+
agent: AnyAgent,
|
|
48
49
|
defaultInput?: AgentDecisionInput
|
|
49
|
-
) {
|
|
50
|
+
): AgentDecisionLogic<any> {
|
|
50
51
|
return fromPromise(async ({ input, self }) => {
|
|
51
52
|
const parentRef = self._parent;
|
|
52
53
|
if (!parentRef) {
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { createAgent } from './agent';
|
|
2
|
-
export { fromText, fromTextStream
|
|
3
|
-
export { fromDecision
|
|
2
|
+
export { fromText, fromTextStream } from './text';
|
|
3
|
+
export { fromDecision } from './decision';
|
|
4
4
|
export * from './types';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Agent, AgentPlan, AgentPlanInput } from '../types';
|
|
1
|
+
import { Agent, AgentPlan, AgentPlanInput, AnyAgent } from '../types';
|
|
2
2
|
import { getShortestPaths } from '@xstate/graph';
|
|
3
3
|
|
|
4
|
-
export async function simplePlanner<T extends
|
|
4
|
+
export async function simplePlanner<T extends AnyAgent>(
|
|
5
5
|
agent: T,
|
|
6
6
|
input: AgentPlanInput<any>
|
|
7
7
|
): Promise<AgentPlan<any> | undefined> {
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { CoreTool, tool } from 'ai';
|
|
2
2
|
import {
|
|
3
|
-
Agent,
|
|
4
3
|
AgentPlan,
|
|
5
4
|
AgentPlanInput,
|
|
6
5
|
ObservedState,
|
|
7
6
|
PromptTemplate,
|
|
8
7
|
TransitionData,
|
|
8
|
+
AnyAgent,
|
|
9
9
|
} from '../types';
|
|
10
10
|
import { getAllTransitions } from '../utils';
|
|
11
11
|
import { AnyStateMachine } from 'xstate';
|
|
12
12
|
import { defaultTextTemplate } from '../templates/defaultText';
|
|
13
|
+
import { getMessages } from '../text';
|
|
13
14
|
|
|
14
15
|
function getTransitions(
|
|
15
16
|
state: ObservedState,
|
|
@@ -27,11 +28,11 @@ const simplePlannerPromptTemplate: PromptTemplate<any> = (data) => {
|
|
|
27
28
|
return `
|
|
28
29
|
${defaultTextTemplate(data)}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
Make at most one tool call to achieve the above goal. If the goal cannot be achieved with any tool calls, do not make any tool call.
|
|
31
32
|
`.trim();
|
|
32
33
|
};
|
|
33
34
|
|
|
34
|
-
export async function simplePlanner<T extends
|
|
35
|
+
export async function simplePlanner<T extends AnyAgent>(
|
|
35
36
|
agent: T,
|
|
36
37
|
input: AgentPlanInput<any>
|
|
37
38
|
): Promise<AgentPlan<any> | undefined> {
|
|
@@ -81,7 +82,7 @@ export async function simplePlanner<T extends Agent<any>>(
|
|
|
81
82
|
toolMap[toolTransitionData.name] = tool({
|
|
82
83
|
description: toolZodType?.description ?? toolTransitionData.description,
|
|
83
84
|
parameters: toolZodType,
|
|
84
|
-
execute: async (params) => {
|
|
85
|
+
execute: async (params: Record<string, any>) => {
|
|
85
86
|
const event = {
|
|
86
87
|
type: toolTransitionData.eventType,
|
|
87
88
|
...params,
|
|
@@ -92,23 +93,32 @@ export async function simplePlanner<T extends Agent<any>>(
|
|
|
92
93
|
});
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
if (!Object.keys(toolMap).length) {
|
|
97
|
+
// No valid transitions for the specified tools
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
95
101
|
// Create a prompt with the given context and goal.
|
|
96
|
-
// The template is used to ensure that a single tool call is made.
|
|
102
|
+
// The template is used to ensure that a single tool call at most is made.
|
|
97
103
|
const prompt = simplePlannerPromptTemplate({
|
|
98
104
|
context: input.state.context,
|
|
99
105
|
goal: input.goal,
|
|
100
106
|
});
|
|
101
107
|
|
|
108
|
+
const messages = await getMessages(agent, prompt, input);
|
|
109
|
+
|
|
102
110
|
const result = await agent.generateText({
|
|
111
|
+
toolChoice: 'required',
|
|
103
112
|
...input,
|
|
104
113
|
prompt,
|
|
114
|
+
messages,
|
|
105
115
|
tools: toolMap,
|
|
106
|
-
toolChoice: 'required',
|
|
107
116
|
});
|
|
108
117
|
|
|
109
118
|
const singleResult = result.toolResults[0];
|
|
110
119
|
|
|
111
120
|
if (!singleResult) {
|
|
121
|
+
console.log(toolMap);
|
|
112
122
|
// TODO: retries?
|
|
113
123
|
console.warn('No tool call results returned');
|
|
114
124
|
return undefined;
|
|
@@ -117,11 +127,12 @@ export async function simplePlanner<T extends Agent<any>>(
|
|
|
117
127
|
return {
|
|
118
128
|
goal: input.goal,
|
|
119
129
|
state: input.state,
|
|
120
|
-
|
|
121
|
-
{
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
130
|
+
execute: async (state) => {
|
|
131
|
+
if (JSON.stringify(state) === JSON.stringify(input.state)) {
|
|
132
|
+
return singleResult.result;
|
|
133
|
+
}
|
|
134
|
+
return undefined;
|
|
135
|
+
},
|
|
125
136
|
nextEvent: singleResult.result,
|
|
126
137
|
sessionId: agent.sessionId,
|
|
127
138
|
timestamp: Date.now(),
|
package/src/schemas.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { AnyEventObject } from 'xstate';
|
|
3
|
-
import { ObservedState } from './types';
|
|
1
|
+
import { ZodType, type SomeZodObject } from 'zod';
|
|
4
2
|
|
|
5
3
|
export type ZodEventMapping = {
|
|
6
4
|
// map event types to Zod types
|
|
7
5
|
[eventType: string]: SomeZodObject;
|
|
8
6
|
};
|
|
9
7
|
|
|
10
|
-
export type
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
action: (state: ObservedState, event: AnyEventObject) => Promise<void>;
|
|
14
|
-
};
|
|
8
|
+
export type ZodContextMapping = {
|
|
9
|
+
// map context keys to Zod types
|
|
10
|
+
[contextKey: string]: ZodType;
|
|
15
11
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GenerateTextResult, LanguageModel } from 'ai';
|
|
2
2
|
import wiki, { wikiSearchResult, wikiSummary } from 'wikipedia';
|
|
3
3
|
import { assign, fromPromise, setup } from 'xstate';
|
|
4
|
-
import {
|
|
4
|
+
import { AnyAgent } from '../types';
|
|
5
5
|
|
|
6
6
|
const searchWiki = fromPromise(
|
|
7
7
|
async ({
|
|
@@ -44,7 +44,7 @@ export const chainOfNote = setup({
|
|
|
44
44
|
types: {
|
|
45
45
|
input: {} as {
|
|
46
46
|
model: LanguageModel;
|
|
47
|
-
agent:
|
|
47
|
+
agent: AnyAgent;
|
|
48
48
|
prompt: string;
|
|
49
49
|
},
|
|
50
50
|
context: {} as {
|
|
@@ -56,7 +56,7 @@ export const chainOfNote = setup({
|
|
|
56
56
|
}[]
|
|
57
57
|
| null;
|
|
58
58
|
model: LanguageModel;
|
|
59
|
-
agent:
|
|
59
|
+
agent: AnyAgent;
|
|
60
60
|
prompt: string;
|
|
61
61
|
},
|
|
62
62
|
output: {} as GenerateTextResult<any>,
|