@statelyai/agent 2.0.0-next.2 → 2.0.0-next.4

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.
Files changed (41) hide show
  1. package/.changeset/nice-pants-rule.md +10 -0
  2. package/.changeset/pink-eagles-deliver.md +13 -0
  3. package/.changeset/pre.json +7 -1
  4. package/.changeset/quiet-turtles-do.md +7 -0
  5. package/.changeset/sweet-clouds-mix.md +16 -0
  6. package/.changeset/swift-mangos-rush.md +5 -0
  7. package/.changeset/tough-ways-rhyme.md +5 -0
  8. package/CHANGELOG.md +50 -0
  9. package/architecture.tldr +175 -0
  10. package/dist/index.d.mts +116 -102
  11. package/dist/index.d.ts +116 -102
  12. package/dist/index.js +51 -74
  13. package/dist/index.mjs +51 -74
  14. package/examples/cot.ts +19 -53
  15. package/examples/customer-service-sim.ts +3 -3
  16. package/examples/email.ts +2 -10
  17. package/examples/example.ts +2 -2
  18. package/examples/goal.ts +2 -2
  19. package/examples/joke.ts +10 -10
  20. package/examples/learn-from-feedback.ts +47 -24
  21. package/examples/number.ts +2 -2
  22. package/examples/raffle.ts +2 -2
  23. package/examples/support.ts +7 -11
  24. package/examples/ticTacToe.ts +2 -2
  25. package/examples/todo.ts +3 -3
  26. package/examples/tutor.ts +2 -2
  27. package/examples/verify.ts +2 -2
  28. package/examples/weather-agent.ts +3 -5
  29. package/examples/weather.ts +10 -7
  30. package/package.json +1 -1
  31. package/src/agent.test.ts +124 -43
  32. package/src/agent.ts +61 -42
  33. package/src/decide.test.ts +22 -0
  34. package/src/decide.ts +27 -29
  35. package/src/strategies/chainOfThought.ts +5 -3
  36. package/src/strategies/shortestPath.ts +7 -2
  37. package/src/strategies/{simple.ts → simpleStrategy.ts} +5 -16
  38. package/src/templates/defaultText.ts +3 -0
  39. package/src/text.ts +13 -14
  40. package/src/types.ts +103 -104
  41. package/src/utils.ts +1 -1
package/src/agent.test.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { test, expect, vi } from 'vitest';
2
- import { createAgent, TypesFromAgent } from './';
2
+ import { AgentDecision, createAgent, TypesFromAgent } from './';
3
3
  import { createActor, createMachine } from 'xstate';
4
4
  import { LanguageModelV1CallOptions } from 'ai';
5
5
  import { z } from 'zod';
@@ -66,38 +66,43 @@ test('agent.addMessage() adds to message history', () => {
66
66
  test('agent.addFeedback() adds to feedback', () => {
67
67
  const agent = createAgent({
68
68
  id: 'test',
69
- events: {},
69
+ events: {
70
+ play: z.object({
71
+ position: z.number(),
72
+ }),
73
+ },
70
74
  model: {} as any,
71
75
  });
72
76
 
73
- const feedback = agent.addFeedback({
74
- attributes: {
75
- score: -1,
76
- },
77
+ const decision: AgentDecision<typeof agent> = {
77
78
  goal: 'Win the game',
78
- observationId: 'obs-1',
79
+ episodeId: agent.episodeId,
80
+ goalState: { value: 'won' },
81
+ id: 'decision-1',
82
+ nextEvent: { type: 'play', position: 3 },
83
+ paths: [],
84
+ strategy: 'simple',
85
+ timestamp: Date.now(),
86
+ };
87
+
88
+ const obs = agent.addObservation({
89
+ decisionId: decision.id,
90
+ prevState: { value: 'playing' },
91
+ event: { type: 'play', position: 3 },
92
+ state: { value: 'lost' },
93
+ });
94
+
95
+ const feedback = agent.addFeedback({
96
+ score: 0,
97
+ observationId: obs.id,
79
98
  });
80
99
 
81
100
  expect(feedback.episodeId).toEqual(agent.episodeId);
82
101
 
83
102
  expect(agent.getFeedback()).toContainEqual(
84
103
  expect.objectContaining({
85
- attributes: {
86
- score: -1,
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',
104
+ score: 0,
105
+ observationId: obs.id,
101
106
  episodeId: expect.any(String),
102
107
  timestamp: expect.any(Number),
103
108
  })
@@ -115,6 +120,7 @@ test('agent.addObservation() adds to observations', () => {
115
120
  prevState: { value: 'playing', context: {} },
116
121
  event: { type: 'play', position: 3 },
117
122
  state: { value: 'lost', context: {} },
123
+ goal: 'Win the game',
118
124
  });
119
125
 
120
126
  expect(observation.episodeId).toEqual(agent.episodeId);
@@ -139,6 +145,7 @@ test('agent.addObservation() adds to observations (initial state)', () => {
139
145
 
140
146
  const observation = agent.addObservation({
141
147
  state: { value: 'lost' },
148
+ goal: 'Win the game',
142
149
  });
143
150
 
144
151
  expect(observation.episodeId).toEqual(agent.episodeId);
@@ -175,7 +182,7 @@ test('agent.addObservation() adds to observations with machine hash', () => {
175
182
  prevState: { value: 'playing', context: {} },
176
183
  event: { type: 'play', position: 3 },
177
184
  state: { value: 'lost', context: {} },
178
- machine,
185
+ goal: 'Win the game',
179
186
  });
180
187
 
181
188
  expect(observation.episodeId).toEqual(agent.episodeId);
@@ -185,7 +192,6 @@ test('agent.addObservation() adds to observations with machine hash', () => {
185
192
  prevState: { value: 'playing', context: {} },
186
193
  event: { type: 'play', position: 3 },
187
194
  state: { value: 'lost', context: {} },
188
- machineHash: expect.any(String),
189
195
  episodeId: expect.any(String),
190
196
  timestamp: expect.any(Number),
191
197
  })
@@ -203,13 +209,11 @@ test('agent.addFeedback() adds to feedback (with observation)', () => {
203
209
  state: {
204
210
  value: 'playing',
205
211
  },
212
+ goal: 'Win the game',
206
213
  });
207
214
 
208
215
  const feedback = agent.addFeedback({
209
- attributes: {
210
- score: -1,
211
- },
212
- goal: 'Win the game',
216
+ score: 0,
213
217
  observationId: observation.id,
214
218
  });
215
219
 
@@ -217,10 +221,7 @@ test('agent.addFeedback() adds to feedback (with observation)', () => {
217
221
 
218
222
  expect(agent.getFeedback()).toContainEqual(
219
223
  expect.objectContaining({
220
- attributes: {
221
- score: -1,
222
- },
223
- goal: 'Win the game',
224
+ score: 0,
224
225
  observationId: observation.id,
225
226
  episodeId: expect.any(String),
226
227
  timestamp: expect.any(Number),
@@ -228,10 +229,7 @@ test('agent.addFeedback() adds to feedback (with observation)', () => {
228
229
  );
229
230
  expect(agent.getFeedback()).toContainEqual(
230
231
  expect.objectContaining({
231
- attributes: {
232
- score: -1,
233
- },
234
- goal: 'Win the game',
232
+ score: 0,
235
233
  observationId: observation.id,
236
234
  episodeId: expect.any(String),
237
235
  timestamp: expect.any(Number),
@@ -297,10 +295,7 @@ test('You can listen for feedback events', () => {
297
295
  agent.on('feedback', fn);
298
296
 
299
297
  agent.addFeedback({
300
- attributes: {
301
- score: -1,
302
- },
303
- goal: 'Win the game',
298
+ score: -1,
304
299
  observationId: 'obs-1',
305
300
  });
306
301
 
@@ -445,6 +440,7 @@ test('agent.getDecisions() returns decisions from context', () => {
445
440
  model: {} as any,
446
441
  strategy: async (agent) => {
447
442
  return {
443
+ id: Date.now().toString(),
448
444
  episodeId: agent.episodeId,
449
445
  strategy: 'test-strategy',
450
446
  goal: '',
@@ -520,7 +516,6 @@ test('agent.observe() adds observations from actor snapshots', () => {
520
516
  expect(agent.getObservations()).toContainEqual(
521
517
  expect.objectContaining({
522
518
  state: expect.objectContaining({ value: 'idle' }),
523
- machineHash: expect.any(String),
524
519
  })
525
520
  );
526
521
 
@@ -529,9 +524,95 @@ test('agent.observe() adds observations from actor snapshots', () => {
529
524
  prevState: expect.objectContaining({ value: 'idle' }),
530
525
  event: { type: 'START' },
531
526
  state: expect.objectContaining({ value: 'running' }),
532
- machineHash: expect.any(String),
533
527
  })
534
528
  );
535
529
 
536
530
  subscription.unsubscribe();
537
531
  });
532
+
533
+ test('agent.addObservation() accepts custom episodeId', () => {
534
+ const agent = createAgent({
535
+ id: 'test',
536
+ events: {},
537
+ model: {} as any,
538
+ });
539
+
540
+ const customEpisodeId = 'custom-episode-123';
541
+ const observation = agent.addObservation({
542
+ state: { value: 'playing' },
543
+ goal: 'Win the game',
544
+ episodeId: customEpisodeId,
545
+ });
546
+
547
+ expect(observation.episodeId).toEqual(customEpisodeId);
548
+ expect(agent.getObservations()).toContainEqual(
549
+ expect.objectContaining({
550
+ episodeId: customEpisodeId,
551
+ })
552
+ );
553
+ });
554
+
555
+ test('agent.addFeedback() accepts custom episodeId', () => {
556
+ const agent = createAgent({
557
+ id: 'test',
558
+ events: {},
559
+ model: {} as any,
560
+ });
561
+
562
+ const customEpisodeId = 'custom-episode-123';
563
+ const feedback = agent.addFeedback({
564
+ score: 1,
565
+ observationId: 'obs-1',
566
+ episodeId: customEpisodeId,
567
+ });
568
+
569
+ expect(feedback.episodeId).toEqual(customEpisodeId);
570
+ expect(agent.getFeedback()).toContainEqual(
571
+ expect.objectContaining({
572
+ episodeId: customEpisodeId,
573
+ })
574
+ );
575
+ });
576
+
577
+ test('agent.addObservation() accepts decisionId', () => {
578
+ const agent = createAgent({
579
+ id: 'test',
580
+ events: {},
581
+ model: {} as any,
582
+ });
583
+
584
+ const decisionId = 'decision-123';
585
+ const observation = agent.addObservation({
586
+ state: { value: 'playing' },
587
+ goal: 'Win the game',
588
+ decisionId,
589
+ });
590
+
591
+ expect(observation.decisionId).toEqual(decisionId);
592
+ expect(agent.getObservations()).toContainEqual(
593
+ expect.objectContaining({
594
+ decisionId,
595
+ })
596
+ );
597
+ });
598
+
599
+ test('agent.addFeedback() accepts decisionId', () => {
600
+ const agent = createAgent({
601
+ id: 'test',
602
+ events: {},
603
+ model: {} as any,
604
+ });
605
+
606
+ const decisionId = 'decision-123';
607
+ const feedback = agent.addFeedback({
608
+ score: 1,
609
+ decisionId,
610
+ });
611
+
612
+ expect(feedback.decisionId).toEqual(decisionId);
613
+ expect(agent.getFeedback()).toContainEqual(
614
+ expect.objectContaining({
615
+ decisionId,
616
+ })
617
+ );
618
+ });
package/src/agent.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  Actor,
3
3
  ActorRefLike,
4
- AnyEventObject,
5
4
  EventObject,
6
5
  fromTransition,
7
6
  Subscription,
@@ -23,16 +22,13 @@ import {
23
22
  AgentMessageInput,
24
23
  AgentFeedbackInput,
25
24
  AgentDecision,
26
- Compute,
27
- AgentDecisionInput,
28
- AgentDecideOptions,
29
25
  AnyAgent,
30
- EventsFromAgent,
31
26
  AgentInteractInput,
27
+ AgentDecideInput,
32
28
  } from './types';
33
- import { simpleStrategy } from './strategies/simple';
29
+ import { simpleStrategy } from './strategies/simpleStrategy';
34
30
  import { agentDecide } from './decide';
35
- import { getMachineHash, isActorRef, isMachineActor, randomId } from './utils';
31
+ import { isActorRef, isMachineActor, randomId } from './utils';
36
32
  import {
37
33
  experimental_wrapLanguageModel,
38
34
  LanguageModel,
@@ -40,7 +36,7 @@ import {
40
36
  } from 'ai';
41
37
  import { createAgentMiddleware } from './middleware';
42
38
 
43
- export const agentLogic: AgentLogic<AnyEventObject> = fromTransition(
39
+ export const agentLogic: AgentLogic<any> = fromTransition(
44
40
  (state, event, { emit }) => {
45
41
  switch (event.type) {
46
42
  case 'agent.feedback': {
@@ -92,14 +88,15 @@ export const agentLogic: AgentLogic<AnyEventObject> = fromTransition(
92
88
  messages: [],
93
89
  observations: [],
94
90
  decisions: [],
95
- } as AgentMemoryContext)
91
+ } as AgentMemoryContext<any>)
96
92
  );
97
93
 
98
94
  export function createAgent<
99
95
  const TContextSchema extends ZodContextMapping,
100
96
  const TEventSchemas extends ZodEventMapping,
101
97
  TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas>,
102
- TContext = ContextFromZodContextMapping<TContextSchema>
98
+ TContext = ContextFromZodContextMapping<TContextSchema>,
99
+ TAgent extends AnyAgent = Agent<TContextSchema, TEventSchemas>
103
100
  >({
104
101
  id,
105
102
  description: description,
@@ -108,7 +105,7 @@ export function createAgent<
108
105
  context,
109
106
  episodeId,
110
107
  strategy = simpleStrategy,
111
- logic = agentLogic as AgentLogic<TEvents>,
108
+ logic = agentLogic as AgentLogic<any>,
112
109
  }: {
113
110
  /**
114
111
  * The unique identifier for the agent.
@@ -142,11 +139,11 @@ export function createAgent<
142
139
  */
143
140
  getMemory?: (
144
141
  agent: Agent<TContextSchema, TEventSchemas>
145
- ) => AgentLongTermMemory;
142
+ ) => AgentLongTermMemory<TAgent>;
146
143
  /**
147
144
  * Agent logic
148
145
  */
149
- logic?: AgentLogic<TEvents>;
146
+ logic?: AgentLogic<TAgent>;
150
147
  model: LanguageModel;
151
148
  episodeId?: string;
152
149
  }): Agent<TContextSchema, TEventSchemas> {
@@ -167,7 +164,7 @@ export class Agent<
167
164
  const TEventSchemas extends ZodEventMapping,
168
165
  TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas>,
169
166
  TContext = ContextFromZodContextMapping<TContextSchema>
170
- > extends Actor<AgentLogic<TEvents>> {
167
+ > extends Actor<AgentLogic<any>> {
171
168
  /**
172
169
  * The name of the agent. All agents with the same name are related and
173
170
  * able to share experiences (observations, feedback) with each other.
@@ -186,11 +183,10 @@ export class Agent<
186
183
  // context: Compute<TContext>;
187
184
  // };
188
185
  public model: LanguageModel;
189
- public memory: AgentLongTermMemory | undefined;
190
- public defaultOptions: AgentDecideOptions<AnyAgent> | undefined; // todo
186
+ public memory: AgentLongTermMemory<this> | undefined;
191
187
 
192
188
  constructor({
193
- logic = agentLogic as AgentLogic<TEvents>,
189
+ logic = agentLogic as AgentLogic<any>,
194
190
  id,
195
191
  name,
196
192
  description,
@@ -200,7 +196,7 @@ export class Agent<
200
196
  episodeId,
201
197
  strategy = simpleStrategy,
202
198
  }: {
203
- logic: AgentLogic<TEvents>;
199
+ logic: AgentLogic<any>;
204
200
  id?: string;
205
201
  name?: string;
206
202
  description?: string;
@@ -262,9 +258,10 @@ export class Agent<
262
258
  public addFeedback(feedbackInput: AgentFeedbackInput) {
263
259
  const feedback = {
264
260
  ...feedbackInput,
261
+ comment: feedbackInput.comment ?? undefined,
265
262
  attributes: { ...feedbackInput.attributes },
266
263
  timestamp: feedbackInput.timestamp ?? Date.now(),
267
- episodeId: this.episodeId,
264
+ episodeId: feedbackInput.episodeId ?? this.episodeId,
268
265
  } satisfies AgentFeedback;
269
266
  this.send({
270
267
  type: 'agent.feedback',
@@ -281,7 +278,7 @@ export class Agent<
281
278
  }
282
279
 
283
280
  public addObservation(
284
- observationInput: AgentObservationInput
281
+ observationInput: AgentObservationInput<this>
285
282
  ): AgentObservation<any> {
286
283
  const { prevState, event, state } = observationInput;
287
284
  const observation = {
@@ -289,11 +286,12 @@ export class Agent<
289
286
  event,
290
287
  state,
291
288
  id: observationInput.id ?? randomId(),
292
- episodeId: this.episodeId,
289
+ episodeId: observationInput.episodeId ?? this.episodeId,
293
290
  timestamp: observationInput.timestamp ?? Date.now(),
294
- machineHash: observationInput.machine
295
- ? getMachineHash(observationInput.machine)
296
- : undefined,
291
+ decisionId: observationInput.decisionId,
292
+ // machineHash: observationInput.machine
293
+ // ? getMachineHash(observationInput.machine)
294
+ // : undefined,
297
295
  } satisfies AgentObservation<any>;
298
296
 
299
297
  this.send({
@@ -311,7 +309,7 @@ export class Agent<
311
309
  return this.getSnapshot().context.observations;
312
310
  }
313
311
 
314
- public addDecision(decision: AgentDecision<TEvents>) {
312
+ public addDecision(decision: AgentDecision<this>) {
315
313
  this.send({
316
314
  type: 'agent.decision',
317
315
  decision,
@@ -383,25 +381,29 @@ export class Agent<
383
381
  const actorRefCheck = isActorRef(actorRef) && actorRef.src;
384
382
  const machine = isMachineActor(actorRef) ? actorRef.src : undefined;
385
383
 
386
- let prevState: ObservedState | undefined = undefined;
384
+ let prevState: ObservedState<this> | undefined = undefined;
387
385
  let subscribed = true;
388
386
 
389
387
  const agent = this;
390
388
 
391
- async function handleObservation(observationInput: AgentObservationInput) {
389
+ async function handleObservation(
390
+ observationInput: AgentObservationInput<any>
391
+ ) {
392
392
  const observation = agent.addObservation(observationInput);
393
393
 
394
- const input = getInput?.(observation);
394
+ const interactInput = getInput?.(observation);
395
395
 
396
- if (input) {
397
- const res = await agentDecide(agent, {
396
+ if (interactInput) {
397
+ const decision = await agentDecide(agent, {
398
398
  machine,
399
399
  state: observation.state,
400
- ...input,
400
+ ...interactInput,
401
401
  });
402
402
 
403
- if (res?.nextEvent) {
404
- actorRef.send(res.nextEvent);
403
+ if (decision?.nextEvent) {
404
+ // @ts-ignore
405
+ decision.nextEvent['_decision'] = decision.id;
406
+ actorRef.send(decision.nextEvent);
405
407
  }
406
408
  }
407
409
 
@@ -420,12 +422,21 @@ export class Agent<
420
422
  return;
421
423
  }
422
424
 
425
+ const decisionId = inspEvent.event['_decision'] as
426
+ | string
427
+ | undefined;
428
+
429
+ const decision = decisionId
430
+ ? agent.getDecisions().find((d) => d.id === decisionId)
431
+ : undefined;
432
+
423
433
  const observationInput = {
424
434
  event: inspEvent.event,
425
435
  prevState,
426
436
  state: inspEvent.snapshot as any,
427
- machine: (actorRef as any).src,
428
- } satisfies AgentObservationInput;
437
+ goal: decision?.goal,
438
+ decisionId,
439
+ } satisfies AgentObservationInput<any>;
429
440
 
430
441
  await handleObservation(observationInput);
431
442
  },
@@ -435,10 +446,10 @@ export class Agent<
435
446
  // If actor already started, interact with current state
436
447
  if ((actorRef as any)._processingStatus === 1) {
437
448
  handleObservation({
449
+ decisionId: undefined,
438
450
  prevState: undefined,
439
451
  event: undefined,
440
452
  state: actorRef.getSnapshot(),
441
- machine: (actorRef as any).src,
442
453
  });
443
454
  }
444
455
 
@@ -451,7 +462,7 @@ export class Agent<
451
462
  }
452
463
 
453
464
  public observe<TActor extends ActorRefLike>(actorRef: TActor): Subscription {
454
- let prevState: ObservedState = actorRef.getSnapshot();
465
+ let prevState: ObservedState<this> = actorRef.getSnapshot();
455
466
  const actorRefCheck = isActorRef(actorRef);
456
467
 
457
468
  const sub = actorRefCheck
@@ -464,12 +475,20 @@ export class Agent<
464
475
  return;
465
476
  }
466
477
 
478
+ const decisionId = inspEvent.event['_decision'] as
479
+ | string
480
+ | undefined;
481
+ const decision = decisionId
482
+ ? this.getDecisions().find((d) => d.id === decisionId)
483
+ : undefined;
484
+
467
485
  const observationInput = {
486
+ decisionId,
468
487
  event: inspEvent.event,
469
488
  prevState,
470
489
  state: inspEvent.snapshot as any,
471
- machine: (actorRef as any).src,
472
- } satisfies AgentObservationInput;
490
+ goal: decision?.goal,
491
+ } satisfies AgentObservationInput<this>;
473
492
 
474
493
  prevState = observationInput.state;
475
494
 
@@ -497,8 +516,8 @@ export class Agent<
497
516
  * - Additional `context`
498
517
  */
499
518
  public async decide(
500
- opts: AgentDecideOptions<this>
501
- ): Promise<AgentDecision<EventsFromAgent<this>> | undefined> {
502
- return agentDecide(this, opts);
519
+ input: AgentDecideInput<this>
520
+ ): Promise<AgentDecision<this> | undefined> {
521
+ return agentDecide(this, input);
503
522
  }
504
523
  }
@@ -322,3 +322,25 @@ test.each([['MOVE'], ['FORFEIT']] as const)(
322
322
  expect(decision?.nextEvent?.type).toEqual(allowedEventType);
323
323
  }
324
324
  );
325
+
326
+ test('agent.decide() accepts custom episodeId', async () => {
327
+ const model = new MockLanguageModelV1({
328
+ doGenerate,
329
+ });
330
+ const agent = createAgent({
331
+ id: 'test',
332
+ events: {
333
+ WIN: z.object({}),
334
+ },
335
+ model,
336
+ });
337
+
338
+ const customEpisodeId = 'custom-episode-123';
339
+ const decision = await agent.decide({
340
+ goal: 'Win the game',
341
+ state: { value: 'playing' },
342
+ episodeId: customEpisodeId,
343
+ });
344
+
345
+ expect(decision?.episodeId).toEqual(customEpisodeId);
346
+ });
package/src/decide.ts CHANGED
@@ -1,24 +1,21 @@
1
1
  import { AnyActor, AnyMachineSnapshot, fromPromise } from 'xstate';
2
2
  import {
3
3
  AnyAgent,
4
- AgentDecideOptions,
5
4
  AgentDecisionLogic,
6
5
  AgentDecision,
7
6
  AgentDecideInput,
8
7
  TransitionData,
9
- EventsFromAgent,
8
+ EventFromAgent,
10
9
  } from './types';
11
10
  import { getTransitions } from './utils';
12
11
  import { CoreMessage, CoreTool, tool } from 'ai';
12
+ import { ZodEventMapping } from './schemas';
13
13
 
14
- export async function agentDecide<T extends AnyAgent>(
15
- agent: T,
16
- options: AgentDecideOptions<T>
17
- ): Promise<AgentDecision<EventsFromAgent<T>> | undefined> {
18
- const resolvedOptions = {
19
- ...agent.defaultOptions,
20
- ...options,
21
- };
14
+ export async function agentDecide<TAgent extends AnyAgent>(
15
+ agent: TAgent,
16
+ options: AgentDecideInput<TAgent>
17
+ ): Promise<AgentDecision<TAgent> | undefined> {
18
+ const resolvedOptions = options;
22
19
  const {
23
20
  strategy = agent.strategy,
24
21
  goal,
@@ -28,6 +25,8 @@ export async function agentDecide<T extends AnyAgent>(
28
25
  machine,
29
26
  model = agent.model,
30
27
  messages,
28
+ episodeId = agent.episodeId,
29
+ maxAttempts = 2,
31
30
  ...otherDecideInput
32
31
  } = resolvedOptions;
33
32
 
@@ -41,16 +40,20 @@ export async function agentDecide<T extends AnyAgent>(
41
40
 
42
41
  let attempts = 0;
43
42
 
44
- const maxAttempts = resolvedOptions.maxAttempts ?? 2;
45
-
46
43
  let decision: AgentDecision<any> | undefined;
47
44
 
45
+ const minimalState = {
46
+ value: state.value,
47
+ context: state.context,
48
+ };
49
+
48
50
  while (attempts++ < maxAttempts) {
49
51
  decision = await strategy(agent, {
52
+ episodeId,
50
53
  model,
51
54
  goal,
52
55
  events: filteredEventSchemas,
53
- state,
56
+ state: minimalState,
54
57
  machine,
55
58
  messages: messages as CoreMessage[], // TODO: fix UIMessage thing
56
59
  ...otherDecideInput,
@@ -58,7 +61,6 @@ export async function agentDecide<T extends AnyAgent>(
58
61
 
59
62
  if (decision?.nextEvent) {
60
63
  agent.addDecision(decision);
61
- await resolvedOptions.execute?.(decision.nextEvent);
62
64
  break;
63
65
  }
64
66
  }
@@ -68,7 +70,7 @@ export async function agentDecide<T extends AnyAgent>(
68
70
 
69
71
  export function fromDecision<T extends AnyAgent>(
70
72
  agent: T,
71
- defaultInput?: AgentDecideInput<EventsFromAgent<T>>
73
+ defaultInput?: AgentDecideInput<EventFromAgent<T>>
72
74
  ): AgentDecisionLogic<any> {
73
75
  return fromPromise(async ({ input, self }) => {
74
76
  const parentRef = self._parent;
@@ -82,43 +84,39 @@ export function fromDecision<T extends AnyAgent>(
82
84
  ...defaultInput,
83
85
  ...inputObject,
84
86
  };
85
- const state = {
86
- value: snapshot.value,
87
- context: resolvedInput.context,
88
- };
89
87
 
90
- const decision = await agentDecide(agent, {
88
+ const decision = await agentDecide<typeof agent>(agent, {
91
89
  machine: (parentRef as AnyActor).logic,
92
90
  state: snapshot,
93
- context: resolvedInput.context,
94
- execute: async (event) => {
95
- parentRef.send(event);
96
- },
97
91
  ...resolvedInput,
98
92
  // @ts-ignore
99
93
  messages: resolvedInput.messages,
100
94
  });
101
95
 
96
+ if (decision?.nextEvent) {
97
+ parentRef.send(decision.nextEvent);
98
+ }
99
+
102
100
  return decision;
103
101
  }) as AgentDecisionLogic<any>;
104
102
  }
105
103
 
106
- export function getToolMap<T extends AnyAgent>(
107
- _agent: T,
104
+ export function getToolMap<TAgent extends AnyAgent>(
105
+ agent: TAgent,
108
106
  input: AgentDecideInput<any>
109
107
  ): Record<string, CoreTool<any, any>> | undefined {
108
+ const events = input.events ?? (agent.events as ZodEventMapping);
110
109
  // Get all of the possible next transitions
111
110
  const transitions: TransitionData[] = input.machine
112
111
  ? getTransitions(input.state, input.machine)
113
- : Object.entries(input.events).map(([eventType, { description }]) => ({
112
+ : Object.entries(events).map(([eventType, { description }]) => ({
114
113
  eventType,
115
114
  description,
116
115
  }));
117
116
 
118
117
  // Only keep the transitions that match the event types that are in the event mapping
119
118
  // TODO: allow for custom filters
120
- const filter = (eventType: string) =>
121
- Object.keys(input.events).includes(eventType);
119
+ const filter = (eventType: string) => Object.keys(events).includes(eventType);
122
120
 
123
121
  // Mapping of each event type (e.g. "mouse.click")
124
122
  // to a valid function name (e.g. "mouse_click")