@statelyai/agent 2.0.0-next.1 → 2.0.0-next.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/.changeset/grumpy-dolphins-think.md +17 -0
- package/.changeset/old-teachers-tap.md +5 -0
- package/.changeset/pre.json +4 -1
- package/.changeset/smart-yaks-pull.md +23 -0
- package/CHANGELOG.md +40 -0
- package/dist/index.d.mts +55 -46
- package/dist/index.d.ts +55 -46
- package/dist/index.js +78 -50
- package/dist/index.mjs +81 -53
- package/examples/chatbot.ts +2 -2
- package/examples/cot.ts +6 -24
- package/examples/customer-service-sim.ts +3 -3
- package/examples/email.ts +9 -3
- package/examples/example.ts +2 -2
- package/examples/goal.ts +2 -2
- package/examples/joke.ts +2 -2
- package/examples/jugs.ts +4 -7
- package/examples/learn-from-feedback.ts +100 -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 +42 -38
- package/examples/ticTacToe.ts +46 -4
- package/examples/todo.ts +2 -2
- package/examples/tutor.ts +2 -2
- package/examples/verify.ts +2 -2
- package/examples/weather-agent.ts +141 -0
- package/examples/weather.ts +23 -23
- package/examples/word.ts +8 -6
- package/package.json +2 -1
- package/src/agent.test.ts +17 -15
- package/src/agent.ts +48 -35
- package/src/decide.test.ts +56 -8
- package/src/decide.ts +34 -24
- package/src/strategies/chainOfThought.ts +48 -0
- package/src/{planners → strategies}/shortestPath.test.ts +4 -7
- package/src/strategies/shortestPath.ts +173 -0
- package/src/{planners → strategies}/simple.ts +28 -18
- package/src/types.ts +62 -28
- package/src/utils.ts +12 -0
- package/src/planners/shortestPath.ts +0 -177
- package/src/strategies/chain-of-note.ts +0 -106
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
|
});
|
|
@@ -307,7 +307,7 @@ test('You can listen for feedback events', () => {
|
|
|
307
307
|
expect(fn).toHaveBeenCalled();
|
|
308
308
|
});
|
|
309
309
|
|
|
310
|
-
test('You can listen for
|
|
310
|
+
test('You can listen for decision events', async () => {
|
|
311
311
|
const fn = vi.fn();
|
|
312
312
|
const model = new MockLanguageModelV1({
|
|
313
313
|
doGenerate: async (params: LanguageModelV1CallOptions) => {
|
|
@@ -339,7 +339,7 @@ test('You can listen for plan events', async () => {
|
|
|
339
339
|
},
|
|
340
340
|
});
|
|
341
341
|
|
|
342
|
-
agent.on('
|
|
342
|
+
agent.on('decision', fn);
|
|
343
343
|
|
|
344
344
|
await agent.decide({
|
|
345
345
|
goal: 'Win the game',
|
|
@@ -364,7 +364,7 @@ test('You can listen for plan events', async () => {
|
|
|
364
364
|
|
|
365
365
|
expect(fn).toHaveBeenCalledWith(
|
|
366
366
|
expect.objectContaining({
|
|
367
|
-
|
|
367
|
+
decision: expect.objectContaining({
|
|
368
368
|
nextEvent: {
|
|
369
369
|
type: 'WIN',
|
|
370
370
|
},
|
|
@@ -386,12 +386,14 @@ test('agent.types provides context and event types', () => {
|
|
|
386
386
|
},
|
|
387
387
|
});
|
|
388
388
|
|
|
389
|
-
|
|
389
|
+
let types = {} as TypesFromAgent<typeof agent>;
|
|
390
390
|
|
|
391
|
-
|
|
391
|
+
types satisfies { context: any; events: any };
|
|
392
|
+
|
|
393
|
+
types.context satisfies { score: number };
|
|
392
394
|
|
|
393
395
|
// @ts-expect-error
|
|
394
|
-
|
|
396
|
+
types.context satisfies { score: string };
|
|
395
397
|
});
|
|
396
398
|
|
|
397
399
|
test('It allows unrecognized events', () => {
|
|
@@ -436,15 +438,15 @@ test('You can listen for message events', () => {
|
|
|
436
438
|
);
|
|
437
439
|
});
|
|
438
440
|
|
|
439
|
-
test('agent.
|
|
441
|
+
test('agent.getDecisions() returns decisions from context', () => {
|
|
440
442
|
const agent = createAgent({
|
|
441
443
|
id: 'test',
|
|
442
444
|
events: {},
|
|
443
445
|
model: {} as any,
|
|
444
|
-
|
|
446
|
+
strategy: async (agent) => {
|
|
445
447
|
return {
|
|
446
448
|
episodeId: agent.episodeId,
|
|
447
|
-
|
|
449
|
+
strategy: 'test-strategy',
|
|
448
450
|
goal: '',
|
|
449
451
|
goalState: undefined,
|
|
450
452
|
paths: [
|
|
@@ -459,10 +461,10 @@ test('agent.getPlans() returns plans from context', () => {
|
|
|
459
461
|
},
|
|
460
462
|
});
|
|
461
463
|
|
|
462
|
-
const
|
|
464
|
+
const decisions = agent.getDecisions();
|
|
463
465
|
|
|
464
|
-
expect(
|
|
465
|
-
expect(Array.isArray(
|
|
466
|
+
expect(decisions).toBeDefined();
|
|
467
|
+
expect(Array.isArray(decisions)).toBe(true);
|
|
466
468
|
});
|
|
467
469
|
|
|
468
470
|
test('Event listeners can be unsubscribed', () => {
|
package/src/agent.ts
CHANGED
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
Actor,
|
|
3
3
|
ActorRefLike,
|
|
4
4
|
AnyEventObject,
|
|
5
|
-
AnyStateMachine,
|
|
6
5
|
EventObject,
|
|
7
6
|
fromTransition,
|
|
8
7
|
Subscription,
|
|
@@ -11,7 +10,7 @@ import { ZodContextMapping, ZodEventMapping } from './schemas';
|
|
|
11
10
|
import {
|
|
12
11
|
AgentLogic,
|
|
13
12
|
AgentMessage,
|
|
14
|
-
|
|
13
|
+
AgentStrategy,
|
|
15
14
|
EventsFromZodEventMapping,
|
|
16
15
|
GenerateTextOptions,
|
|
17
16
|
AgentLongTermMemory,
|
|
@@ -23,13 +22,15 @@ import {
|
|
|
23
22
|
AgentFeedback,
|
|
24
23
|
AgentMessageInput,
|
|
25
24
|
AgentFeedbackInput,
|
|
26
|
-
|
|
25
|
+
AgentDecision,
|
|
27
26
|
Compute,
|
|
28
27
|
AgentDecisionInput,
|
|
29
28
|
AgentDecideOptions,
|
|
30
29
|
AnyAgent,
|
|
30
|
+
EventsFromAgent,
|
|
31
|
+
AgentInteractInput,
|
|
31
32
|
} from './types';
|
|
32
|
-
import {
|
|
33
|
+
import { simpleStrategy } from './strategies/simple';
|
|
33
34
|
import { agentDecide } from './decide';
|
|
34
35
|
import { getMachineHash, isActorRef, isMachineActor, randomId } from './utils';
|
|
35
36
|
import {
|
|
@@ -69,12 +70,11 @@ export const agentLogic: AgentLogic<AnyEventObject> = fromTransition(
|
|
|
69
70
|
});
|
|
70
71
|
break;
|
|
71
72
|
}
|
|
72
|
-
case 'agent.
|
|
73
|
-
state.
|
|
73
|
+
case 'agent.decision': {
|
|
74
|
+
state.decisions.push(event.decision);
|
|
74
75
|
emit({
|
|
75
|
-
type: '
|
|
76
|
-
|
|
77
|
-
plan: event.plan,
|
|
76
|
+
type: 'decision',
|
|
77
|
+
decision: event.decision,
|
|
78
78
|
});
|
|
79
79
|
break;
|
|
80
80
|
}
|
|
@@ -91,7 +91,7 @@ export const agentLogic: AgentLogic<AnyEventObject> = fromTransition(
|
|
|
91
91
|
feedback: [],
|
|
92
92
|
messages: [],
|
|
93
93
|
observations: [],
|
|
94
|
-
|
|
94
|
+
decisions: [],
|
|
95
95
|
} as AgentMemoryContext)
|
|
96
96
|
);
|
|
97
97
|
|
|
@@ -106,7 +106,8 @@ export function createAgent<
|
|
|
106
106
|
model,
|
|
107
107
|
events,
|
|
108
108
|
context,
|
|
109
|
-
|
|
109
|
+
episodeId,
|
|
110
|
+
strategy = simpleStrategy,
|
|
110
111
|
logic = agentLogic as AgentLogic<TEvents>,
|
|
111
112
|
}: {
|
|
112
113
|
/**
|
|
@@ -134,7 +135,7 @@ export function createAgent<
|
|
|
134
135
|
*/
|
|
135
136
|
events: TEventSchemas;
|
|
136
137
|
context?: TContextSchema;
|
|
137
|
-
|
|
138
|
+
strategy?: AgentStrategy<Agent<TContextSchema, TEventSchemas>>;
|
|
138
139
|
stringify?: typeof JSON.stringify;
|
|
139
140
|
/**
|
|
140
141
|
* A function that retrieves the agent's long term memory
|
|
@@ -147,15 +148,17 @@ export function createAgent<
|
|
|
147
148
|
*/
|
|
148
149
|
logic?: AgentLogic<TEvents>;
|
|
149
150
|
model: LanguageModel;
|
|
150
|
-
|
|
151
|
+
episodeId?: string;
|
|
152
|
+
}): Agent<TContextSchema, TEventSchemas> {
|
|
151
153
|
return new Agent({
|
|
152
154
|
id,
|
|
153
155
|
context,
|
|
154
156
|
events,
|
|
155
157
|
description,
|
|
156
|
-
|
|
158
|
+
strategy: strategy,
|
|
157
159
|
model,
|
|
158
160
|
logic,
|
|
161
|
+
episodeId,
|
|
159
162
|
}) as any;
|
|
160
163
|
}
|
|
161
164
|
|
|
@@ -177,11 +180,11 @@ export class Agent<
|
|
|
177
180
|
public description?: string;
|
|
178
181
|
public events: TEventSchemas;
|
|
179
182
|
public context?: TContextSchema;
|
|
180
|
-
public
|
|
181
|
-
public types: {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
};
|
|
183
|
+
public strategy: AgentStrategy<Agent<TContextSchema, TEventSchemas>>;
|
|
184
|
+
// public types: {
|
|
185
|
+
// events: TEvents;
|
|
186
|
+
// context: Compute<TContext>;
|
|
187
|
+
// };
|
|
185
188
|
public model: LanguageModel;
|
|
186
189
|
public memory: AgentLongTermMemory | undefined;
|
|
187
190
|
public defaultOptions: AgentDecideOptions<AnyAgent> | undefined; // todo
|
|
@@ -194,7 +197,8 @@ export class Agent<
|
|
|
194
197
|
model,
|
|
195
198
|
events,
|
|
196
199
|
context,
|
|
197
|
-
|
|
200
|
+
episodeId,
|
|
201
|
+
strategy = simpleStrategy,
|
|
198
202
|
}: {
|
|
199
203
|
logic: AgentLogic<TEvents>;
|
|
200
204
|
id?: string;
|
|
@@ -203,17 +207,18 @@ export class Agent<
|
|
|
203
207
|
model: GenerateTextOptions['model'];
|
|
204
208
|
events: TEventSchemas;
|
|
205
209
|
context?: TContextSchema;
|
|
206
|
-
|
|
210
|
+
strategy?: AgentStrategy<Agent<TContextSchema, TEventSchemas>>;
|
|
211
|
+
episodeId?: string;
|
|
207
212
|
}) {
|
|
208
213
|
super(logic);
|
|
209
214
|
this.model = model;
|
|
210
|
-
this.episodeId =
|
|
215
|
+
this.episodeId = episodeId ?? randomId('episode-');
|
|
211
216
|
this.name = name;
|
|
212
217
|
this.description = description;
|
|
213
218
|
this.events = events;
|
|
214
219
|
this.context = context;
|
|
215
|
-
this.
|
|
216
|
-
this.
|
|
220
|
+
this.strategy = strategy;
|
|
221
|
+
this.id = id ?? randomId();
|
|
217
222
|
|
|
218
223
|
this.start();
|
|
219
224
|
}
|
|
@@ -225,6 +230,13 @@ export class Agent<
|
|
|
225
230
|
return this.on('message', (ev) => fn(ev.message));
|
|
226
231
|
}
|
|
227
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Called whenever the agent (LLM assistant) receives some feedback.
|
|
235
|
+
*/
|
|
236
|
+
public onFeedback(fn: (feedback: AgentFeedback) => void) {
|
|
237
|
+
return this.on('feedback', (ev) => fn(ev.feedback));
|
|
238
|
+
}
|
|
239
|
+
|
|
228
240
|
/**
|
|
229
241
|
* Retrieves messages from the agent's short-term (local) memory.
|
|
230
242
|
*/
|
|
@@ -251,7 +263,6 @@ export class Agent<
|
|
|
251
263
|
const feedback = {
|
|
252
264
|
...feedbackInput,
|
|
253
265
|
attributes: { ...feedbackInput.attributes },
|
|
254
|
-
reward: feedbackInput.reward ?? 0,
|
|
255
266
|
timestamp: feedbackInput.timestamp ?? Date.now(),
|
|
256
267
|
episodeId: this.episodeId,
|
|
257
268
|
} satisfies AgentFeedback;
|
|
@@ -300,17 +311,17 @@ export class Agent<
|
|
|
300
311
|
return this.getSnapshot().context.observations;
|
|
301
312
|
}
|
|
302
313
|
|
|
303
|
-
public
|
|
314
|
+
public addDecision(decision: AgentDecision<TEvents>) {
|
|
304
315
|
this.send({
|
|
305
|
-
type: 'agent.
|
|
306
|
-
|
|
316
|
+
type: 'agent.decision',
|
|
317
|
+
decision,
|
|
307
318
|
});
|
|
308
319
|
}
|
|
309
320
|
/**
|
|
310
321
|
* Retrieves strategies from the agent's short-term (local) memory.
|
|
311
322
|
*/
|
|
312
|
-
public
|
|
313
|
-
return this.getSnapshot().context.
|
|
323
|
+
public getDecisions() {
|
|
324
|
+
return this.getSnapshot().context.decisions;
|
|
314
325
|
}
|
|
315
326
|
|
|
316
327
|
/**
|
|
@@ -361,13 +372,13 @@ export class Agent<
|
|
|
361
372
|
actorRef: TActor,
|
|
362
373
|
getInput: (
|
|
363
374
|
observation: AgentObservation<TActor>
|
|
364
|
-
) =>
|
|
375
|
+
) => AgentInteractInput<this> | void
|
|
365
376
|
): Subscription;
|
|
366
377
|
public interact<TActor extends ActorRefLike>(
|
|
367
378
|
actorRef: TActor,
|
|
368
379
|
getInput?: (
|
|
369
380
|
observation: AgentObservation<TActor>
|
|
370
|
-
) =>
|
|
381
|
+
) => AgentInteractInput<this> | void
|
|
371
382
|
): Subscription {
|
|
372
383
|
const actorRefCheck = isActorRef(actorRef) && actorRef.src;
|
|
373
384
|
const machine = isMachineActor(actorRef) ? actorRef.src : undefined;
|
|
@@ -425,7 +436,7 @@ export class Agent<
|
|
|
425
436
|
if ((actorRef as any)._processingStatus === 1) {
|
|
426
437
|
handleObservation({
|
|
427
438
|
prevState: undefined,
|
|
428
|
-
event:
|
|
439
|
+
event: undefined,
|
|
429
440
|
state: actorRef.getSnapshot(),
|
|
430
441
|
machine: (actorRef as any).src,
|
|
431
442
|
});
|
|
@@ -478,14 +489,16 @@ export class Agent<
|
|
|
478
489
|
}
|
|
479
490
|
|
|
480
491
|
/**
|
|
481
|
-
* Resolves with an `
|
|
492
|
+
* Resolves with an `AgentDecision` based on the information provided in the `options`, including:
|
|
482
493
|
*
|
|
483
494
|
* - The `goal` for the agent to achieve
|
|
484
495
|
* - The observed current `state`
|
|
485
496
|
* - The `machine` (e.g. a state machine) that specifies what can happen next
|
|
486
497
|
* - Additional `context`
|
|
487
498
|
*/
|
|
488
|
-
public decide(
|
|
499
|
+
public async decide(
|
|
500
|
+
opts: AgentDecideOptions<this>
|
|
501
|
+
): Promise<AgentDecision<EventsFromAgent<this>> | undefined> {
|
|
489
502
|
return agentDecide(this, opts);
|
|
490
503
|
}
|
|
491
504
|
}
|
package/src/decide.test.ts
CHANGED
|
@@ -154,7 +154,7 @@ test('interacts with an actor (late interaction)', async () => {
|
|
|
154
154
|
expect(actor.getSnapshot().value).toBe('third');
|
|
155
155
|
});
|
|
156
156
|
|
|
157
|
-
test('agent.decide() makes a decision based on goal and state (simple
|
|
157
|
+
test('agent.decide() makes a decision based on goal and state (simple strategy)', async () => {
|
|
158
158
|
const model = new MockLanguageModelV1({
|
|
159
159
|
doGenerate,
|
|
160
160
|
});
|
|
@@ -167,7 +167,7 @@ test('agent.decide() makes a decision based on goal and state (simple planner)',
|
|
|
167
167
|
},
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
-
const
|
|
170
|
+
const decision = await agent.decide({
|
|
171
171
|
goal: 'Make the best move',
|
|
172
172
|
state: {
|
|
173
173
|
value: 'playing',
|
|
@@ -188,8 +188,8 @@ test('agent.decide() makes a decision based on goal and state (simple planner)',
|
|
|
188
188
|
}),
|
|
189
189
|
});
|
|
190
190
|
|
|
191
|
-
expect(
|
|
192
|
-
expect(
|
|
191
|
+
expect(decision).toBeDefined();
|
|
192
|
+
expect(decision!.nextEvent).toEqual(
|
|
193
193
|
expect.objectContaining({
|
|
194
194
|
type: 'MOVE',
|
|
195
195
|
})
|
|
@@ -243,7 +243,7 @@ test.each([
|
|
|
243
243
|
},
|
|
244
244
|
});
|
|
245
245
|
|
|
246
|
-
const
|
|
246
|
+
const decision = await agent.decide({
|
|
247
247
|
goal: 'Make the best move',
|
|
248
248
|
state: {
|
|
249
249
|
value: 'playing',
|
|
@@ -263,10 +263,10 @@ test.each([
|
|
|
263
263
|
});
|
|
264
264
|
|
|
265
265
|
if (!succeed) {
|
|
266
|
-
expect(
|
|
266
|
+
expect(decision).toBeUndefined();
|
|
267
267
|
} else {
|
|
268
|
-
expect(
|
|
269
|
-
expect(
|
|
268
|
+
expect(decision).toBeDefined();
|
|
269
|
+
expect(decision!.nextEvent).toEqual(
|
|
270
270
|
expect.objectContaining({
|
|
271
271
|
type: 'MOVE',
|
|
272
272
|
})
|
|
@@ -274,3 +274,51 @@ test.each([
|
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
);
|
|
277
|
+
|
|
278
|
+
test.each([['MOVE'], ['FORFEIT']] as const)(
|
|
279
|
+
'agent.decide() respects allowedEvents constraint (event: %s)',
|
|
280
|
+
async (allowedEventType) => {
|
|
281
|
+
const model = new MockLanguageModelV1({
|
|
282
|
+
doGenerate: async (params: LanguageModelV1CallOptions) => {
|
|
283
|
+
const keys =
|
|
284
|
+
params.mode.type === 'regular'
|
|
285
|
+
? params.mode.tools?.map((t) => t.name)
|
|
286
|
+
: [];
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
...dummyResponseValues,
|
|
290
|
+
finishReason: 'tool-calls',
|
|
291
|
+
toolCalls: [
|
|
292
|
+
{
|
|
293
|
+
toolCallType: 'function',
|
|
294
|
+
toolCallId: 'call-1',
|
|
295
|
+
toolName: keys![0],
|
|
296
|
+
args: `{ "type": "${keys?.[0]}" }`,
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
} as any;
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const agent = createAgent({
|
|
304
|
+
id: 'test',
|
|
305
|
+
model,
|
|
306
|
+
events: {
|
|
307
|
+
MOVE: z.object({}),
|
|
308
|
+
SKIP: z.object({}),
|
|
309
|
+
FORFEIT: z.object({}),
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const decision = await agent.decide({
|
|
314
|
+
goal: 'Make the best move',
|
|
315
|
+
state: {
|
|
316
|
+
value: 'playing',
|
|
317
|
+
context: {},
|
|
318
|
+
},
|
|
319
|
+
allowedEvents: [allowedEventType],
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
expect(decision?.nextEvent?.type).toEqual(allowedEventType);
|
|
323
|
+
}
|
|
324
|
+
);
|
package/src/decide.ts
CHANGED
|
@@ -3,65 +3,72 @@ import {
|
|
|
3
3
|
AnyAgent,
|
|
4
4
|
AgentDecideOptions,
|
|
5
5
|
AgentDecisionLogic,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
AgentPlan,
|
|
9
|
-
EventsFromZodEventMapping,
|
|
10
|
-
AgentPlanInput,
|
|
6
|
+
AgentDecision,
|
|
7
|
+
AgentDecideInput,
|
|
11
8
|
TransitionData,
|
|
9
|
+
EventsFromAgent,
|
|
12
10
|
} from './types';
|
|
13
|
-
import { simplePlanner } from './planners/simple';
|
|
14
11
|
import { getTransitions } from './utils';
|
|
15
12
|
import { CoreMessage, CoreTool, tool } from 'ai';
|
|
16
13
|
|
|
17
14
|
export async function agentDecide<T extends AnyAgent>(
|
|
18
15
|
agent: T,
|
|
19
16
|
options: AgentDecideOptions<T>
|
|
20
|
-
): Promise<
|
|
17
|
+
): Promise<AgentDecision<EventsFromAgent<T>> | undefined> {
|
|
21
18
|
const resolvedOptions = {
|
|
22
19
|
...agent.defaultOptions,
|
|
23
20
|
...options,
|
|
24
21
|
};
|
|
25
22
|
const {
|
|
26
|
-
|
|
23
|
+
strategy = agent.strategy,
|
|
27
24
|
goal,
|
|
25
|
+
allowedEvents,
|
|
28
26
|
events = agent.events,
|
|
29
27
|
state,
|
|
30
28
|
machine,
|
|
31
29
|
model = agent.model,
|
|
32
30
|
messages,
|
|
33
|
-
...
|
|
31
|
+
...otherDecideInput
|
|
34
32
|
} = resolvedOptions;
|
|
35
33
|
|
|
34
|
+
const filteredEventSchemas = allowedEvents
|
|
35
|
+
? Object.fromEntries(
|
|
36
|
+
Object.entries(events).filter(([key]) => {
|
|
37
|
+
return allowedEvents.includes(key);
|
|
38
|
+
})
|
|
39
|
+
)
|
|
40
|
+
: events;
|
|
41
|
+
|
|
36
42
|
let attempts = 0;
|
|
37
43
|
|
|
38
44
|
const maxAttempts = resolvedOptions.maxAttempts ?? 2;
|
|
39
45
|
|
|
40
|
-
let
|
|
46
|
+
let decision: AgentDecision<any> | undefined;
|
|
41
47
|
|
|
42
48
|
while (attempts++ < maxAttempts) {
|
|
43
|
-
|
|
49
|
+
decision = await strategy(agent, {
|
|
44
50
|
model,
|
|
45
51
|
goal,
|
|
46
|
-
events,
|
|
52
|
+
events: filteredEventSchemas,
|
|
47
53
|
state,
|
|
48
54
|
machine,
|
|
49
55
|
messages: messages as CoreMessage[], // TODO: fix UIMessage thing
|
|
50
|
-
...
|
|
56
|
+
...otherDecideInput,
|
|
51
57
|
});
|
|
52
58
|
|
|
53
|
-
if (
|
|
54
|
-
agent.
|
|
55
|
-
await resolvedOptions.execute?.(
|
|
59
|
+
if (decision?.nextEvent) {
|
|
60
|
+
agent.addDecision(decision);
|
|
61
|
+
await resolvedOptions.execute?.(decision.nextEvent);
|
|
62
|
+
break;
|
|
56
63
|
}
|
|
57
64
|
}
|
|
58
65
|
|
|
59
|
-
return
|
|
66
|
+
return decision;
|
|
60
67
|
}
|
|
61
68
|
|
|
62
|
-
export function fromDecision(
|
|
63
|
-
agent:
|
|
64
|
-
defaultInput?:
|
|
69
|
+
export function fromDecision<T extends AnyAgent>(
|
|
70
|
+
agent: T,
|
|
71
|
+
defaultInput?: AgentDecideInput<EventsFromAgent<T>>
|
|
65
72
|
): AgentDecisionLogic<any> {
|
|
66
73
|
return fromPromise(async ({ input, self }) => {
|
|
67
74
|
const parentRef = self._parent;
|
|
@@ -80,22 +87,25 @@ export function fromDecision(
|
|
|
80
87
|
context: resolvedInput.context,
|
|
81
88
|
};
|
|
82
89
|
|
|
83
|
-
const
|
|
90
|
+
const decision = await agentDecide(agent, {
|
|
84
91
|
machine: (parentRef as AnyActor).logic,
|
|
85
|
-
state,
|
|
92
|
+
state: snapshot,
|
|
93
|
+
context: resolvedInput.context,
|
|
86
94
|
execute: async (event) => {
|
|
87
95
|
parentRef.send(event);
|
|
88
96
|
},
|
|
89
97
|
...resolvedInput,
|
|
98
|
+
// @ts-ignore
|
|
99
|
+
messages: resolvedInput.messages,
|
|
90
100
|
});
|
|
91
101
|
|
|
92
|
-
return
|
|
102
|
+
return decision;
|
|
93
103
|
}) as AgentDecisionLogic<any>;
|
|
94
104
|
}
|
|
95
105
|
|
|
96
106
|
export function getToolMap<T extends AnyAgent>(
|
|
97
107
|
_agent: T,
|
|
98
|
-
input:
|
|
108
|
+
input: AgentDecideInput<any>
|
|
99
109
|
): Record<string, CoreTool<any, any>> | undefined {
|
|
100
110
|
// Get all of the possible next transitions
|
|
101
111
|
const transitions: TransitionData[] = input.machine
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { generateText } from 'ai';
|
|
2
|
+
import {
|
|
3
|
+
AnyAgent,
|
|
4
|
+
AgentDecideInput,
|
|
5
|
+
AgentDecision,
|
|
6
|
+
PromptTemplate,
|
|
7
|
+
} from '../types';
|
|
8
|
+
import { getMessages } from '../text';
|
|
9
|
+
import { simpleStrategy } from './simple';
|
|
10
|
+
import { convertToXml } from '../utils';
|
|
11
|
+
|
|
12
|
+
const chainOfThoughtPromptTemplate: PromptTemplate<any> = ({
|
|
13
|
+
context,
|
|
14
|
+
goal,
|
|
15
|
+
}) => {
|
|
16
|
+
return `
|
|
17
|
+
${convertToXml({ context, goal })}
|
|
18
|
+
|
|
19
|
+
How would you achieve the goal? Think step-by-step.
|
|
20
|
+
`.trim();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export async function chainOfThoughtStrategy<T extends AnyAgent>(
|
|
24
|
+
agent: T,
|
|
25
|
+
input: AgentDecideInput<any>
|
|
26
|
+
): Promise<AgentDecision<any> | undefined> {
|
|
27
|
+
const prompt = chainOfThoughtPromptTemplate({
|
|
28
|
+
context: input.state.context,
|
|
29
|
+
goal: input.goal,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const messages = await getMessages(agent, prompt, input);
|
|
33
|
+
|
|
34
|
+
const model = input.model ? agent.wrap(input.model) : agent.model;
|
|
35
|
+
|
|
36
|
+
const result = await generateText({
|
|
37
|
+
model,
|
|
38
|
+
system: input.system ?? agent.description,
|
|
39
|
+
messages,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const decision = await simpleStrategy(agent, {
|
|
43
|
+
...input,
|
|
44
|
+
messages: messages.concat(result.response.messages),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return decision;
|
|
48
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createAgent } from '
|
|
1
|
+
import { createAgent, TypesFromAgent } from '..';
|
|
2
2
|
import { assign, createActor, setup } from 'xstate';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import {
|
|
4
|
+
import { experimental_shortestPathStrategy } from './shortestPath';
|
|
5
5
|
import { test, expect } from 'vitest';
|
|
6
6
|
import { dummyResponseValues, MockLanguageModelV1 } from '../mockModel';
|
|
7
7
|
|
|
@@ -35,10 +35,7 @@ test.skip('should find shortest path to goal', async () => {
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
const counterMachine = setup({
|
|
38
|
-
types: {
|
|
39
|
-
context: agent.types.context,
|
|
40
|
-
events: agent.types.events,
|
|
41
|
-
},
|
|
38
|
+
types: {} as TypesFromAgent<typeof agent>,
|
|
42
39
|
}).createMachine({
|
|
43
40
|
initial: 'counting',
|
|
44
41
|
context: { count: 0 },
|
|
@@ -87,7 +84,7 @@ test.skip('should find shortest path to goal', async () => {
|
|
|
87
84
|
}),
|
|
88
85
|
goal: 'Get the counter to exactly 3',
|
|
89
86
|
state: counterActor.getSnapshot(),
|
|
90
|
-
|
|
87
|
+
strategy: experimental_shortestPathStrategy,
|
|
91
88
|
});
|
|
92
89
|
|
|
93
90
|
expect(decision?.nextEvent?.type).toBe('increment');
|