@statelyai/agent 2.0.0-next.3 → 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.
package/src/agent.ts CHANGED
@@ -22,13 +22,13 @@ import {
22
22
  AgentMessageInput,
23
23
  AgentFeedbackInput,
24
24
  AgentDecision,
25
- AgentDecideOptions,
26
25
  AnyAgent,
27
26
  AgentInteractInput,
27
+ AgentDecideInput,
28
28
  } from './types';
29
- import { simpleStrategy } from './strategies/simple';
29
+ import { simpleStrategy } from './strategies/simpleStrategy';
30
30
  import { agentDecide } from './decide';
31
- import { getMachineHash, isActorRef, isMachineActor, randomId } from './utils';
31
+ import { isActorRef, isMachineActor, randomId } from './utils';
32
32
  import {
33
33
  experimental_wrapLanguageModel,
34
34
  LanguageModel,
@@ -184,7 +184,6 @@ export class Agent<
184
184
  // };
185
185
  public model: LanguageModel;
186
186
  public memory: AgentLongTermMemory<this> | undefined;
187
- public defaultOptions: AgentDecideOptions<AnyAgent> | undefined; // todo
188
187
 
189
188
  constructor({
190
189
  logic = agentLogic as AgentLogic<any>,
@@ -262,7 +261,7 @@ export class Agent<
262
261
  comment: feedbackInput.comment ?? undefined,
263
262
  attributes: { ...feedbackInput.attributes },
264
263
  timestamp: feedbackInput.timestamp ?? Date.now(),
265
- episodeId: this.episodeId,
264
+ episodeId: feedbackInput.episodeId ?? this.episodeId,
266
265
  } satisfies AgentFeedback;
267
266
  this.send({
268
267
  type: 'agent.feedback',
@@ -287,11 +286,12 @@ export class Agent<
287
286
  event,
288
287
  state,
289
288
  id: observationInput.id ?? randomId(),
290
- episodeId: this.episodeId,
289
+ episodeId: observationInput.episodeId ?? this.episodeId,
291
290
  timestamp: observationInput.timestamp ?? Date.now(),
292
- machineHash: observationInput.machine
293
- ? getMachineHash(observationInput.machine)
294
- : undefined,
291
+ decisionId: observationInput.decisionId,
292
+ // machineHash: observationInput.machine
293
+ // ? getMachineHash(observationInput.machine)
294
+ // : undefined,
295
295
  } satisfies AgentObservation<any>;
296
296
 
297
297
  this.send({
@@ -434,8 +434,8 @@ export class Agent<
434
434
  event: inspEvent.event,
435
435
  prevState,
436
436
  state: inspEvent.snapshot as any,
437
- machine: (actorRef as any).src,
438
437
  goal: decision?.goal,
438
+ decisionId,
439
439
  } satisfies AgentObservationInput<any>;
440
440
 
441
441
  await handleObservation(observationInput);
@@ -446,11 +446,10 @@ export class Agent<
446
446
  // If actor already started, interact with current state
447
447
  if ((actorRef as any)._processingStatus === 1) {
448
448
  handleObservation({
449
+ decisionId: undefined,
449
450
  prevState: undefined,
450
451
  event: undefined,
451
452
  state: actorRef.getSnapshot(),
452
- machine: (actorRef as any).src,
453
- goal: undefined,
454
453
  });
455
454
  }
456
455
 
@@ -484,10 +483,10 @@ export class Agent<
484
483
  : undefined;
485
484
 
486
485
  const observationInput = {
486
+ decisionId,
487
487
  event: inspEvent.event,
488
488
  prevState,
489
489
  state: inspEvent.snapshot as any,
490
- machine: (actorRef as any).src,
491
490
  goal: decision?.goal,
492
491
  } satisfies AgentObservationInput<this>;
493
492
 
@@ -517,8 +516,8 @@ export class Agent<
517
516
  * - Additional `context`
518
517
  */
519
518
  public async decide(
520
- opts: AgentDecideOptions<this>
519
+ input: AgentDecideInput<this>
521
520
  ): Promise<AgentDecision<this> | undefined> {
522
- return agentDecide(this, opts);
521
+ return agentDecide(this, input);
523
522
  }
524
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,7 +1,6 @@
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,
@@ -10,15 +9,13 @@ import {
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
14
  export async function agentDecide<TAgent extends AnyAgent>(
15
15
  agent: TAgent,
16
- options: AgentDecideOptions<TAgent>
16
+ options: AgentDecideInput<TAgent>
17
17
  ): Promise<AgentDecision<TAgent> | undefined> {
18
- const resolvedOptions = {
19
- ...agent.defaultOptions,
20
- ...options,
21
- };
18
+ const resolvedOptions = options;
22
19
  const {
23
20
  strategy = agent.strategy,
24
21
  goal,
@@ -28,6 +25,8 @@ export async function agentDecide<TAgent 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,8 +40,6 @@ export async function agentDecide<TAgent 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
 
48
45
  const minimalState = {
@@ -52,6 +49,7 @@ export async function agentDecide<TAgent extends AnyAgent>(
52
49
 
53
50
  while (attempts++ < maxAttempts) {
54
51
  decision = await strategy(agent, {
52
+ episodeId,
55
53
  model,
56
54
  goal,
57
55
  events: filteredEventSchemas,
@@ -63,7 +61,6 @@ export async function agentDecide<TAgent extends AnyAgent>(
63
61
 
64
62
  if (decision?.nextEvent) {
65
63
  agent.addDecision(decision);
66
- await resolvedOptions.execute?.(decision.nextEvent);
67
64
  break;
68
65
  }
69
66
  }
@@ -88,37 +85,38 @@ export function fromDecision<T extends AnyAgent>(
88
85
  ...inputObject,
89
86
  };
90
87
 
91
- const decision = await agentDecide(agent, {
88
+ const decision = await agentDecide<typeof agent>(agent, {
92
89
  machine: (parentRef as AnyActor).logic,
93
90
  state: snapshot,
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")
@@ -6,7 +6,7 @@ import {
6
6
  PromptTemplate,
7
7
  } from '../types';
8
8
  import { getMessages } from '../text';
9
- import { simpleStrategy } from './simple';
9
+ import { simpleStrategy } from './simpleStrategy';
10
10
  import { convertToXml } from '../utils';
11
11
 
12
12
  const chainOfThoughtPromptTemplate: PromptTemplate<any> = ({
@@ -84,7 +84,7 @@ export async function simpleStrategy<T extends AnyAgent>(
84
84
  goal: input.goal,
85
85
  goalState: input.state,
86
86
  nextEvent: singleResult.result,
87
- episodeId: agent.episodeId,
87
+ episodeId: input.episodeId ?? agent.episodeId,
88
88
  timestamp: Date.now(),
89
89
  paths: [
90
90
  {
package/src/text.ts CHANGED
@@ -116,7 +116,6 @@ export function fromText<TAgent extends AnyAgent>(
116
116
  }
117
117
  > {
118
118
  const resolvedOptions = {
119
- ...agent.defaultOptions,
120
119
  ...options,
121
120
  };
122
121
 
package/src/types.ts CHANGED
@@ -33,8 +33,9 @@ export type CostFunction<TAgent extends AnyAgent> = (
33
33
 
34
34
  export type AgentDecideInput<TAgent extends AnyAgent> = Omit<
35
35
  AgentGenerateTextOptions<TAgent>,
36
- 'prompt' | 'tools'
36
+ 'model' | 'prompt' | 'tools'
37
37
  > & {
38
+ episodeId?: string;
38
39
  /**
39
40
  * The currently observed state.
40
41
  */
@@ -52,7 +53,8 @@ export type AgentDecideInput<TAgent extends AnyAgent> = Omit<
52
53
  * The events that the agent can trigger. This is a mapping of
53
54
  * event types to Zod event schemas.
54
55
  */
55
- events: ZodEventMapping;
56
+ events?: ZodEventMapping;
57
+ allowedEvents?: Array<EventFromAgent<TAgent>['type']>;
56
58
  /**
57
59
  * The state machine that represents the environment the agent
58
60
  * is interacting with.
@@ -69,6 +71,8 @@ export type AgentDecideInput<TAgent extends AnyAgent> = Omit<
69
71
  * Defaults to 2.
70
72
  */
71
73
  maxAttempts?: number;
74
+ strategy?: AgentStrategy<TAgent>;
75
+ model?: LanguageModel;
72
76
  };
73
77
 
74
78
  export type AgentStep<TAgent extends AnyAgent> = {
@@ -150,51 +154,33 @@ export type AgentStrategy<TAgent extends AnyAgent> = (
150
154
  ) => Promise<AgentDecision<TAgent> | undefined>;
151
155
 
152
156
  export type AgentInteractInput<T extends AnyAgent> = Omit<
153
- AgentDecideOptions<T>,
157
+ AgentDecideInput<T>,
154
158
  'state'
155
159
  > & {
156
160
  state?: never;
157
161
  };
158
162
 
159
- export type AgentDecideOptions<TAgent extends AnyAgent> = {
160
- goal: string;
161
- state: ObservedState<TAgent>;
162
- /**
163
- * The context to provide in the prompt to the agent. This overrides the `state.context`.
164
- */
165
- context?: Record<string, any>;
166
- machine?: AnyStateMachine;
167
- model?: LanguageModel;
168
- execute?: (event: AnyEventObject) => Promise<void>;
169
- strategy?: AgentStrategy<TAgent>;
170
- events?: ZodEventMapping;
171
- allowedEvents?: Array<EventFromAgent<TAgent>['type']>;
172
- /**
173
- * The maximum number of times the agent will attempt to make a decision.
174
- * Defaults to 2.
175
- */
176
- maxAttempts?: number;
177
- } & Omit<Parameters<typeof generateText>[0], 'model' | 'tools' | 'prompt'>;
178
-
179
- export interface AgentFeedback {
180
- observationId: string;
163
+ export type AgentFeedback = {
181
164
  score: number;
182
165
  comment: string | undefined;
183
- /**
184
- * The message correlation that the feedback is relevant for
185
- */
186
166
  attributes: Record<string, any>;
187
167
  timestamp: number;
188
168
  episodeId: string;
189
- }
169
+ } & (
170
+ | { observationId: string; decisionId?: never }
171
+ | { decisionId: string; observationId?: never }
172
+ );
190
173
 
191
- export interface AgentFeedbackInput {
192
- observationId: string;
174
+ export type AgentFeedbackInput = {
175
+ episodeId?: string;
193
176
  score: number;
194
177
  comment?: string;
195
178
  attributes?: Record<string, any>;
196
179
  timestamp?: number;
197
- }
180
+ } & (
181
+ | { observationId: string; decisionId?: never }
182
+ | { decisionId: string; observationId?: never }
183
+ );
198
184
 
199
185
  export type AgentMessage = CoreMessage & {
200
186
  timestamp: number;
@@ -336,23 +322,29 @@ export type AgentMessageInput = CoreMessage & {
336
322
 
337
323
  export interface AgentObservation<TActor extends ActorRefLike> {
338
324
  id: string;
325
+ decisionId?: string | undefined;
339
326
  goal?: string;
340
327
  prevState: SnapshotFrom<TActor> | undefined;
341
328
  event: EventFrom<TActor> | undefined;
342
329
  state: SnapshotFrom<TActor>;
343
- machineHash: string | undefined;
330
+ // machineHash: string | undefined;
344
331
  episodeId: string;
345
332
  timestamp: number;
346
333
  }
347
334
 
348
335
  export interface AgentObservationInput<TAgent extends AnyAgent> {
349
336
  id?: string;
337
+ episodeId?: string;
338
+ /**
339
+ * The agent decision that the observation is relevant for
340
+ */
341
+ decisionId?: string | undefined;
350
342
  prevState?: ObservedState<TAgent>;
351
343
  event?: AnyEventObject;
352
344
  state: ObservedState<TAgent>;
353
- machine?: AnyStateMachine;
345
+ // machine?: AnyStateMachine;
354
346
  timestamp?: number;
355
- goal: string | undefined;
347
+ goal?: string | undefined;
356
348
  }
357
349
 
358
350
  export type AgentDecisionInput = {