@statelyai/agent 2.0.0-next.3 → 2.0.0-next.5

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 (37) hide show
  1. package/.changeset/calm-beans-talk.md +5 -0
  2. package/.changeset/long-guests-explode.md +5 -0
  3. package/.changeset/nice-pants-rule.md +10 -0
  4. package/.changeset/odd-kiwis-compare.md +5 -0
  5. package/.changeset/pre.json +4 -0
  6. package/CHANGELOG.md +23 -0
  7. package/architecture.tldr +797 -0
  8. package/dist/index.d.mts +198 -140
  9. package/dist/index.d.ts +198 -140
  10. package/dist/index.js +4397 -148
  11. package/dist/index.mjs +4397 -149
  12. package/examples/chatbot.ts +9 -5
  13. package/examples/cot.ts +2 -4
  14. package/examples/jugs.ts +2 -2
  15. package/examples/learn-from-feedback.ts +7 -7
  16. package/examples/newspaper.ts +1 -1
  17. package/examples/rewoo.ts +62 -0
  18. package/examples/river-crossing.ts +2 -2
  19. package/examples/serverless.ts +71 -0
  20. package/examples/simple.ts +1 -1
  21. package/examples/ticTacToe.ts +6 -2
  22. package/examples/wiki.ts +2 -2
  23. package/package.json +14 -12
  24. package/readme.md +57 -0
  25. package/src/agent.test.ts +387 -30
  26. package/src/agent.ts +177 -64
  27. package/src/decide.test.ts +24 -2
  28. package/src/decide.ts +34 -78
  29. package/src/index.ts +1 -0
  30. package/src/{strategies/chainOfThought.ts → policies/chainOfThoughtPolicy.ts} +7 -9
  31. package/src/policies/index.ts +3 -0
  32. package/src/{strategies/shortestPath.test.ts → policies/shortestPathPolicy.test.ts} +2 -2
  33. package/src/{strategies/shortestPath.ts → policies/shortestPathPolicy.ts} +8 -8
  34. package/src/{strategies/simple.ts → policies/toolPolicy.ts} +27 -26
  35. package/src/text.ts +17 -22
  36. package/src/types.ts +162 -166
  37. package/src/agent-experimental.ts +0 -221
package/src/agent.ts CHANGED
@@ -1,36 +1,38 @@
1
1
  import {
2
2
  Actor,
3
3
  ActorRefLike,
4
- EventObject,
5
4
  fromTransition,
5
+ SnapshotFrom,
6
6
  Subscription,
7
7
  } from 'xstate';
8
8
  import { ZodContextMapping, ZodEventMapping } from './schemas';
9
9
  import {
10
10
  AgentLogic,
11
11
  AgentMessage,
12
- AgentStrategy,
13
- EventsFromZodEventMapping,
12
+ AgentPolicy,
14
13
  GenerateTextOptions,
15
14
  AgentLongTermMemory,
16
15
  ObservedState,
17
16
  AgentObservationInput,
18
17
  AgentMemoryContext,
19
18
  AgentObservation,
20
- ContextFromZodContextMapping,
21
19
  AgentFeedback,
22
20
  AgentMessageInput,
23
21
  AgentFeedbackInput,
24
22
  AgentDecision,
25
- AgentDecideOptions,
26
23
  AnyAgent,
27
24
  AgentInteractInput,
25
+ AgentDecideInput,
26
+ EventFromAgent,
27
+ AgentInsightInput,
28
+ AgentInsight,
29
+ AgentDecisionInput,
28
30
  } from './types';
29
- import { simpleStrategy } from './strategies/simple';
30
- import { agentDecide } from './decide';
31
- import { getMachineHash, isActorRef, isMachineActor, randomId } from './utils';
31
+ import { toolPolicy } from './policies/toolPolicy';
32
+ import { isActorRef, isMachineActor, randomId } from './utils';
32
33
  import {
33
- experimental_wrapLanguageModel,
34
+ CoreMessage,
35
+ wrapLanguageModel,
34
36
  LanguageModel,
35
37
  LanguageModelV1,
36
38
  } from 'ai';
@@ -43,7 +45,6 @@ export const agentLogic: AgentLogic<any> = fromTransition(
43
45
  state.feedback.push(event.feedback);
44
46
  emit({
45
47
  type: 'feedback',
46
- // @ts-ignore TODO: fix types in XState
47
48
  feedback: event.feedback,
48
49
  });
49
50
  break;
@@ -52,7 +53,6 @@ export const agentLogic: AgentLogic<any> = fromTransition(
52
53
  state.observations.push(event.observation);
53
54
  emit({
54
55
  type: 'observation',
55
- // @ts-ignore TODO: fix types in XState
56
56
  observation: event.observation,
57
57
  });
58
58
  break;
@@ -61,7 +61,6 @@ export const agentLogic: AgentLogic<any> = fromTransition(
61
61
  state.messages.push(event.message);
62
62
  emit({
63
63
  type: 'message',
64
- // @ts-ignore TODO: fix types in XState
65
64
  message: event.message,
66
65
  });
67
66
  break;
@@ -74,8 +73,15 @@ export const agentLogic: AgentLogic<any> = fromTransition(
74
73
  });
75
74
  break;
76
75
  }
76
+ case 'agent.insight': {
77
+ state.insights.push(event.insight);
78
+ emit({
79
+ type: 'insight',
80
+ insight: event.insight,
81
+ });
82
+ break;
83
+ }
77
84
  default: {
78
- // unrecognized
79
85
  console.warn('Unrecognized event', event);
80
86
  break;
81
87
  }
@@ -88,29 +94,28 @@ export const agentLogic: AgentLogic<any> = fromTransition(
88
94
  messages: [],
89
95
  observations: [],
90
96
  decisions: [],
97
+ insights: [],
91
98
  } as AgentMemoryContext<any>)
92
99
  );
93
100
 
94
101
  export function createAgent<
95
102
  const TContextSchema extends ZodContextMapping,
96
103
  const TEventSchemas extends ZodEventMapping,
97
- TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas>,
98
- TContext = ContextFromZodContextMapping<TContextSchema>,
99
104
  TAgent extends AnyAgent = Agent<TContextSchema, TEventSchemas>
100
105
  >({
101
106
  id,
102
- description: description,
107
+ description,
103
108
  model,
104
109
  events,
105
110
  context,
106
111
  episodeId,
107
- strategy = simpleStrategy,
108
- logic = agentLogic as AgentLogic<any>,
112
+ policy = toolPolicy,
113
+ logic = agentLogic,
109
114
  }: {
110
115
  /**
111
116
  * The unique identifier for the agent.
112
117
  *
113
- * This should be the same across all sessions of a specific agent, as it can be
118
+ * This should be the same across all episodes of a specific agent, as it can be
114
119
  * used to retrieve memory for previous episodes of this agent.
115
120
  *
116
121
  * @example
@@ -123,17 +128,23 @@ export function createAgent<
123
128
  */
124
129
  id?: string;
125
130
  /**
126
- * A description of the role of the agent
131
+ * A description of the role of the agent.
127
132
  */
128
133
  description?: string;
129
134
  /**
130
- * Events that the agent can cause (send) in an environment
131
- * that the agent knows about.
135
+ * Event schemas for events that the agent can trigger in an environment.
132
136
  */
133
137
  events: TEventSchemas;
138
+ /**
139
+ * The state context schema for the states that the agent can observe.
140
+ */
134
141
  context?: TContextSchema;
135
- strategy?: AgentStrategy<Agent<TContextSchema, TEventSchemas>>;
136
- stringify?: typeof JSON.stringify;
142
+ /**
143
+ * The default policy to use for `agent.decide(…)`.
144
+ *
145
+ * A policy is a strategy that the agent uses to decide which event to trigger next.
146
+ */
147
+ policy?: AgentPolicy<Agent<TContextSchema, TEventSchemas>>;
137
148
  /**
138
149
  * A function that retrieves the agent's long term memory
139
150
  */
@@ -141,10 +152,19 @@ export function createAgent<
141
152
  agent: Agent<TContextSchema, TEventSchemas>
142
153
  ) => AgentLongTermMemory<TAgent>;
143
154
  /**
144
- * Agent logic
155
+ * Custom agent logic, which receives events for handling feedback,
156
+ * observations, messages, decisions, and insights.
145
157
  */
146
158
  logic?: AgentLogic<TAgent>;
159
+ /**
160
+ * The default language model for the agent to use in `agent.decide(…)`.
161
+ */
147
162
  model: LanguageModel;
163
+ /**
164
+ * The unique episode ID that this agent will run on.
165
+ *
166
+ * An episode is an instance of an agent interacting with an environment.
167
+ */
148
168
  episodeId?: string;
149
169
  }): Agent<TContextSchema, TEventSchemas> {
150
170
  return new Agent({
@@ -152,7 +172,7 @@ export function createAgent<
152
172
  context,
153
173
  events,
154
174
  description,
155
- strategy: strategy,
175
+ policy: policy,
156
176
  model,
157
177
  logic,
158
178
  episodeId,
@@ -161,9 +181,7 @@ export function createAgent<
161
181
 
162
182
  export class Agent<
163
183
  const TContextSchema extends ZodContextMapping,
164
- const TEventSchemas extends ZodEventMapping,
165
- TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas>,
166
- TContext = ContextFromZodContextMapping<TContextSchema>
184
+ const TEventSchemas extends ZodEventMapping
167
185
  > extends Actor<AgentLogic<any>> {
168
186
  /**
169
187
  * The name of the agent. All agents with the same name are related and
@@ -177,14 +195,9 @@ export class Agent<
177
195
  public description?: string;
178
196
  public events: TEventSchemas;
179
197
  public context?: TContextSchema;
180
- public strategy: AgentStrategy<Agent<TContextSchema, TEventSchemas>>;
181
- // public types: {
182
- // events: TEvents;
183
- // context: Compute<TContext>;
184
- // };
198
+ public policy: AgentPolicy<Agent<TContextSchema, TEventSchemas>>;
185
199
  public model: LanguageModel;
186
200
  public memory: AgentLongTermMemory<this> | undefined;
187
- public defaultOptions: AgentDecideOptions<AnyAgent> | undefined; // todo
188
201
 
189
202
  constructor({
190
203
  logic = agentLogic as AgentLogic<any>,
@@ -195,7 +208,7 @@ export class Agent<
195
208
  events,
196
209
  context,
197
210
  episodeId,
198
- strategy = simpleStrategy,
211
+ policy = toolPolicy,
199
212
  }: {
200
213
  logic: AgentLogic<any>;
201
214
  id?: string;
@@ -204,7 +217,7 @@ export class Agent<
204
217
  model: GenerateTextOptions['model'];
205
218
  events: TEventSchemas;
206
219
  context?: TContextSchema;
207
- strategy?: AgentStrategy<Agent<TContextSchema, TEventSchemas>>;
220
+ policy?: AgentPolicy<Agent<TContextSchema, TEventSchemas>>;
208
221
  episodeId?: string;
209
222
  }) {
210
223
  super(logic);
@@ -214,30 +227,44 @@ export class Agent<
214
227
  this.description = description;
215
228
  this.events = events;
216
229
  this.context = context;
217
- this.strategy = strategy;
230
+ this.policy = policy;
218
231
  this.id = id ?? randomId();
219
232
 
220
233
  this.start();
221
234
  }
222
235
 
223
236
  /**
224
- * Called whenever the agent (LLM assistant) receives or sends a message.
237
+ * Called whenever the agent detects that a message was sent from the human, assistant, or system.
225
238
  */
226
239
  public onMessage(fn: (message: AgentMessage) => void) {
227
240
  return this.on('message', (ev) => fn(ev.message));
228
241
  }
229
242
 
230
243
  /**
231
- * Called whenever the agent (LLM assistant) receives some feedback.
244
+ * Called whenever the agent receives some feedback.
232
245
  */
233
246
  public onFeedback(fn: (feedback: AgentFeedback) => void) {
234
247
  return this.on('feedback', (ev) => fn(ev.feedback));
235
248
  }
236
249
 
237
250
  /**
238
- * Retrieves messages from the agent's short-term (local) memory.
251
+ * Called whenever the agent receives an observation.
252
+ */
253
+ public onObservation(fn: (observation: AgentObservation<this>) => void) {
254
+ return this.on('observation', (ev) => fn(ev.observation));
255
+ }
256
+
257
+ /**
258
+ * Called whenever the agent makes a decision.
259
+ */
260
+ public onDecision(fn: (decision: AgentDecision<this>) => void) {
261
+ return this.on('decision', (ev) => fn(ev.decision));
262
+ }
263
+
264
+ /**
265
+ * Adds a message to the agent's short-term (local) memory.
239
266
  */
240
- public addMessage(messageInput: AgentMessageInput) {
267
+ public addMessage(messageInput: AgentMessageInput): AgentMessage {
241
268
  const message = {
242
269
  ...messageInput,
243
270
  id: messageInput.id ?? randomId(),
@@ -259,10 +286,11 @@ export class Agent<
259
286
  public addFeedback(feedbackInput: AgentFeedbackInput) {
260
287
  const feedback = {
261
288
  ...feedbackInput,
289
+ id: feedbackInput.id ?? randomId(),
262
290
  comment: feedbackInput.comment ?? undefined,
263
291
  attributes: { ...feedbackInput.attributes },
264
292
  timestamp: feedbackInput.timestamp ?? Date.now(),
265
- episodeId: this.episodeId,
293
+ episodeId: feedbackInput.episodeId ?? this.episodeId,
266
294
  } satisfies AgentFeedback;
267
295
  this.send({
268
296
  type: 'agent.feedback',
@@ -287,11 +315,12 @@ export class Agent<
287
315
  event,
288
316
  state,
289
317
  id: observationInput.id ?? randomId(),
290
- episodeId: this.episodeId,
318
+ episodeId: observationInput.episodeId ?? this.episodeId,
291
319
  timestamp: observationInput.timestamp ?? Date.now(),
292
- machineHash: observationInput.machine
293
- ? getMachineHash(observationInput.machine)
294
- : undefined,
320
+ decisionId: observationInput.decisionId,
321
+ // machineHash: observationInput.machine
322
+ // ? getMachineHash(observationInput.machine)
323
+ // : undefined,
295
324
  } satisfies AgentObservation<any>;
296
325
 
297
326
  this.send({
@@ -309,10 +338,40 @@ export class Agent<
309
338
  return this.getSnapshot().context.observations;
310
339
  }
311
340
 
312
- public addDecision(decision: AgentDecision<this>) {
341
+ public addInsight(insightInput: AgentInsightInput): AgentInsight {
342
+ const insight = {
343
+ ...insightInput,
344
+ episodeId: insightInput.episodeId ?? this.episodeId,
345
+ id: insightInput.id ?? randomId(),
346
+ timestamp: insightInput.timestamp ?? Date.now(),
347
+ } satisfies AgentInsight;
348
+
349
+ this.send({
350
+ type: 'agent.insight',
351
+ insight,
352
+ });
353
+
354
+ return insight;
355
+ }
356
+
357
+ public getInsights() {
358
+ return this.getSnapshot().context.insights;
359
+ }
360
+
361
+ public addDecision(input: AgentDecisionInput<this>) {
313
362
  this.send({
314
363
  type: 'agent.decision',
315
- decision,
364
+ decision: {
365
+ id: input.id ?? randomId(),
366
+ episodeId: input.episodeId ?? this.episodeId,
367
+ timestamp: input.timestamp ?? Date.now(),
368
+ decisionId: input.decisionId ?? null,
369
+ policy: input.policy ?? null,
370
+ goalState: input.goalState ?? null,
371
+ nextEvent: input.nextEvent ?? null,
372
+ paths: input.paths ?? [],
373
+ ...input,
374
+ },
316
375
  });
317
376
  }
318
377
  /**
@@ -386,15 +445,15 @@ export class Agent<
386
445
 
387
446
  const agent = this;
388
447
 
389
- async function handleObservation(
448
+ const handleObservation = async (
390
449
  observationInput: AgentObservationInput<any>
391
- ) {
450
+ ) => {
392
451
  const observation = agent.addObservation(observationInput);
393
452
 
394
453
  const interactInput = getInput?.(observation);
395
454
 
396
455
  if (interactInput) {
397
- const decision = await agentDecide(agent, {
456
+ const decision = await this.decide({
398
457
  machine,
399
458
  state: observation.state,
400
459
  ...interactInput,
@@ -408,7 +467,7 @@ export class Agent<
408
467
  }
409
468
 
410
469
  prevState = observationInput.state;
411
- }
470
+ };
412
471
 
413
472
  // Inspect system, but only observe specified actor
414
473
  const sub = actorRefCheck
@@ -426,16 +485,18 @@ export class Agent<
426
485
  | string
427
486
  | undefined;
428
487
 
488
+ const decisions = agent.getDecisions();
489
+
429
490
  const decision = decisionId
430
- ? agent.getDecisions().find((d) => d.id === decisionId)
491
+ ? decisions.find((d) => d.id === decisionId)
431
492
  : undefined;
432
493
 
433
494
  const observationInput = {
434
495
  event: inspEvent.event,
435
496
  prevState,
436
- state: inspEvent.snapshot as any,
437
- machine: (actorRef as any).src,
497
+ state: inspEvent.snapshot as SnapshotFrom<TActor>,
438
498
  goal: decision?.goal,
499
+ decisionId,
439
500
  } satisfies AgentObservationInput<any>;
440
501
 
441
502
  await handleObservation(observationInput);
@@ -446,11 +507,10 @@ export class Agent<
446
507
  // If actor already started, interact with current state
447
508
  if ((actorRef as any)._processingStatus === 1) {
448
509
  handleObservation({
510
+ decisionId: undefined,
449
511
  prevState: undefined,
450
512
  event: undefined,
451
513
  state: actorRef.getSnapshot(),
452
- machine: (actorRef as any).src,
453
- goal: undefined,
454
514
  });
455
515
  }
456
516
 
@@ -479,15 +539,18 @@ export class Agent<
479
539
  const decisionId = inspEvent.event['_decision'] as
480
540
  | string
481
541
  | undefined;
542
+
543
+ const decisions = this.getDecisions();
544
+
482
545
  const decision = decisionId
483
- ? this.getDecisions().find((d) => d.id === decisionId)
546
+ ? decisions.find((d) => d.id === decisionId)
484
547
  : undefined;
485
548
 
486
549
  const observationInput = {
550
+ decisionId,
487
551
  event: inspEvent.event,
488
552
  prevState,
489
- state: inspEvent.snapshot as any,
490
- machine: (actorRef as any).src,
553
+ state: inspEvent.snapshot as SnapshotFrom<TActor>,
491
554
  goal: decision?.goal,
492
555
  } satisfies AgentObservationInput<this>;
493
556
 
@@ -502,7 +565,7 @@ export class Agent<
502
565
  }
503
566
 
504
567
  public wrap(modelToWrap: LanguageModelV1) {
505
- return experimental_wrapLanguageModel({
568
+ return wrapLanguageModel({
506
569
  model: modelToWrap,
507
570
  middleware: createAgentMiddleware(this),
508
571
  });
@@ -517,8 +580,58 @@ export class Agent<
517
580
  * - Additional `context`
518
581
  */
519
582
  public async decide(
520
- opts: AgentDecideOptions<this>
583
+ input: AgentDecideInput<this>
521
584
  ): Promise<AgentDecision<this> | undefined> {
522
- return agentDecide(this, opts);
585
+ const resolvedOptions = input;
586
+ const {
587
+ policy = this.policy,
588
+ goal,
589
+ allowedEvents,
590
+ events = this.events,
591
+ state,
592
+ machine,
593
+ model = this.model,
594
+ messages,
595
+ episodeId = this.episodeId,
596
+ maxAttempts = 2,
597
+ ...otherDecideInput
598
+ } = resolvedOptions;
599
+
600
+ const filteredEventSchemas = allowedEvents
601
+ ? Object.fromEntries(
602
+ Object.entries(events).filter(([key]) => {
603
+ return allowedEvents.includes(key as EventFromAgent<this>['type']);
604
+ })
605
+ )
606
+ : events;
607
+
608
+ let attempts = 0;
609
+
610
+ let decision: AgentDecision<this> | undefined;
611
+
612
+ const minimalState = {
613
+ value: state.value,
614
+ context: state.context,
615
+ };
616
+
617
+ while (attempts++ < maxAttempts) {
618
+ decision = await policy(this, {
619
+ episodeId,
620
+ model,
621
+ goal,
622
+ events: filteredEventSchemas,
623
+ state: minimalState,
624
+ machine,
625
+ messages: messages as CoreMessage[], // TODO: fix UIMessage thing
626
+ ...otherDecideInput,
627
+ });
628
+
629
+ if (decision?.nextEvent) {
630
+ this.addDecision(decision);
631
+ break;
632
+ }
633
+ }
634
+
635
+ return decision;
523
636
  }
524
637
  }
@@ -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 strategy)', async () => {
157
+ test('agent.decide() makes a decision based on goal and state (tool policy)', async () => {
158
158
  const model = new MockLanguageModelV1({
159
159
  doGenerate,
160
160
  });
@@ -211,7 +211,7 @@ test.each([
211
211
  ? params.mode.tools?.map((t) => t.name)
212
212
  : [];
213
213
 
214
- console.log('try', attempts, 'max', maxAttempts);
214
+ // console.log('try', attempts, 'max', maxAttempts);
215
215
 
216
216
  const toolCalls =
217
217
  succeed && attempts++ === (maxAttempts ?? 2) - 1
@@ -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,80 +1,34 @@
1
- import { AnyActor, AnyMachineSnapshot, fromPromise } from 'xstate';
1
+ import {
2
+ AnyActor,
3
+ AnyMachineSnapshot,
4
+ fromPromise,
5
+ PromiseActorLogic,
6
+ } from 'xstate';
2
7
  import {
3
8
  AnyAgent,
4
- AgentDecideOptions,
5
- AgentDecisionLogic,
6
- AgentDecision,
7
9
  AgentDecideInput,
8
10
  TransitionData,
9
- EventFromAgent,
11
+ AgentDecision,
10
12
  } from './types';
11
13
  import { getTransitions } from './utils';
12
- import { CoreMessage, CoreTool, tool } from 'ai';
13
-
14
- export async function agentDecide<TAgent extends AnyAgent>(
15
- agent: TAgent,
16
- options: AgentDecideOptions<TAgent>
17
- ): Promise<AgentDecision<TAgent> | undefined> {
18
- const resolvedOptions = {
19
- ...agent.defaultOptions,
20
- ...options,
21
- };
22
- const {
23
- strategy = agent.strategy,
24
- goal,
25
- allowedEvents,
26
- events = agent.events,
27
- state,
28
- machine,
29
- model = agent.model,
30
- messages,
31
- ...otherDecideInput
32
- } = resolvedOptions;
33
-
34
- const filteredEventSchemas = allowedEvents
35
- ? Object.fromEntries(
36
- Object.entries(events).filter(([key]) => {
37
- return allowedEvents.includes(key);
38
- })
39
- )
40
- : events;
41
-
42
- let attempts = 0;
43
-
44
- const maxAttempts = resolvedOptions.maxAttempts ?? 2;
45
-
46
- let decision: AgentDecision<any> | undefined;
47
-
48
- const minimalState = {
49
- value: state.value,
50
- context: state.context,
51
- };
52
-
53
- while (attempts++ < maxAttempts) {
54
- decision = await strategy(agent, {
55
- model,
56
- goal,
57
- events: filteredEventSchemas,
58
- state: minimalState,
59
- machine,
60
- messages: messages as CoreMessage[], // TODO: fix UIMessage thing
61
- ...otherDecideInput,
62
- });
14
+ import { CoreTool, generateText, LanguageModel, tool } from 'ai';
15
+ import { ZodEventMapping } from './schemas';
63
16
 
64
- if (decision?.nextEvent) {
65
- agent.addDecision(decision);
66
- await resolvedOptions.execute?.(decision.nextEvent);
67
- break;
68
- }
69
- }
17
+ export type AgentDecideLogicInput = {
18
+ goal: string;
19
+ model?: LanguageModel;
20
+ context?: Record<string, any>;
21
+ } & Omit<Parameters<typeof generateText>[0], 'model' | 'tools' | 'prompt'>;
70
22
 
71
- return decision;
72
- }
23
+ export type MachineDecisionLogic<TAgent extends AnyAgent> = PromiseActorLogic<
24
+ AgentDecision<TAgent> | undefined,
25
+ AgentDecideLogicInput | string
26
+ >;
73
27
 
74
- export function fromDecision<T extends AnyAgent>(
75
- agent: T,
76
- defaultInput?: AgentDecideInput<EventFromAgent<T>>
77
- ): AgentDecisionLogic<any> {
28
+ export function fromDecision<TAgent extends AnyAgent>(
29
+ agent: TAgent,
30
+ defaultInput?: AgentDecideInput<TAgent>
31
+ ): MachineDecisionLogic<any> {
78
32
  return fromPromise(async ({ input, self }) => {
79
33
  const parentRef = self._parent;
80
34
  if (!parentRef) {
@@ -88,37 +42,39 @@ export function fromDecision<T extends AnyAgent>(
88
42
  ...inputObject,
89
43
  };
90
44
 
91
- const decision = await agentDecide(agent, {
45
+ const decision = await agent.decide({
92
46
  machine: (parentRef as AnyActor).logic,
93
47
  state: snapshot,
94
- execute: async (event) => {
95
- parentRef.send(event);
96
- },
48
+ allowedEvents: resolvedInput.allowedEvents as any[],
97
49
  ...resolvedInput,
98
50
  // @ts-ignore
99
51
  messages: resolvedInput.messages,
100
52
  });
101
53
 
54
+ if (decision?.nextEvent) {
55
+ parentRef.send(decision.nextEvent);
56
+ }
57
+
102
58
  return decision;
103
- }) as AgentDecisionLogic<any>;
59
+ }) as MachineDecisionLogic<any>;
104
60
  }
105
61
 
106
- export function getToolMap<T extends AnyAgent>(
107
- _agent: T,
62
+ export function getToolMap<TAgent extends AnyAgent>(
63
+ agent: TAgent,
108
64
  input: AgentDecideInput<any>
109
65
  ): Record<string, CoreTool<any, any>> | undefined {
66
+ const events = input.events ?? (agent.events as ZodEventMapping);
110
67
  // Get all of the possible next transitions
111
68
  const transitions: TransitionData[] = input.machine
112
69
  ? getTransitions(input.state, input.machine)
113
- : Object.entries(input.events).map(([eventType, { description }]) => ({
70
+ : Object.entries(events).map(([eventType, { description }]) => ({
114
71
  eventType,
115
72
  description,
116
73
  }));
117
74
 
118
75
  // Only keep the transitions that match the event types that are in the event mapping
119
76
  // TODO: allow for custom filters
120
- const filter = (eventType: string) =>
121
- Object.keys(input.events).includes(eventType);
77
+ const filter = (eventType: string) => Object.keys(events).includes(eventType);
122
78
 
123
79
  // Mapping of each event type (e.g. "mouse.click")
124
80
  // to a valid function name (e.g. "mouse_click")
package/src/index.ts CHANGED
@@ -2,3 +2,4 @@ export { createAgent } from './agent';
2
2
  export { fromText, fromTextStream } from './text';
3
3
  export { fromDecision } from './decide';
4
4
  export * from './types';
5
+ export * from './policies';