@statelyai/agent 1.0.0-beta.0 → 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.
@@ -0,0 +1,12 @@
1
+ ---
2
+ '@statelyai/agent': minor
3
+ ---
4
+
5
+ Added four new methods for easily retrieving agent messages, observations, feedback, and plans:
6
+
7
+ - `agent.getMessages()`
8
+ - `agent.getObservations()`
9
+ - `agent.getFeedback()`
10
+ - `agent.getPlans()`
11
+
12
+ The `agent.select(…)` method is deprecated in favor of these methods.
@@ -0,0 +1,5 @@
1
+ ---
2
+ '@statelyai/agent': patch
3
+ ---
4
+
5
+ Messages are now properly included in `agent.decide(…)`, when specified.
@@ -0,0 +1,26 @@
1
+ ---
2
+ '@statelyai/agent': minor
3
+ ---
4
+
5
+ You can now add `context` Zod schema to your agent. For now, this is meant to be passed directly to the state machine, but in the future, the schema can be shared with the LLM agent to better understand the state machine and its context for decision making.
6
+
7
+ Breaking: The `context` and `events` types are now in `agent.types` instead of ~~`agent.eventTypes`.
8
+
9
+ ```ts
10
+ const agent = createAgent({
11
+ // ...
12
+ context: {
13
+ score: z.number().describe('The score of the game'),
14
+ // ...
15
+ },
16
+ });
17
+
18
+ const machine = setup({
19
+ types: agent.types,
20
+ }).createMachine({
21
+ context: {
22
+ score: 0,
23
+ },
24
+ // ...
25
+ });
26
+ ```
package/.env.template CHANGED
@@ -1,6 +1,3 @@
1
1
 
2
2
  # Get your OpenAI API key from: https://platform.openai.com/signup/
3
3
  OPENAI_API_KEY="sk-..."
4
-
5
- # Get your Tavily API key from: https://app.tavily.com/
6
- TAVILY_API_KEY="tvly-..."
@@ -11,9 +11,9 @@ permissions: {}
11
11
  jobs:
12
12
  release:
13
13
  permissions:
14
- contents: write # to create release (changesets/action)
14
+ contents: write # to create release (changesets/action)
15
15
  issues: write # to post issue comments (changesets/action)
16
- pull-requests: write # to create pull request (changesets/action)
16
+ pull-requests: write # to create pull request (changesets/action)
17
17
 
18
18
  if: github.repository == 'statelyai/agent'
19
19
 
@@ -22,7 +22,7 @@ jobs:
22
22
  runs-on: ubuntu-latest
23
23
 
24
24
  steps:
25
- - uses: actions/checkout@v3
25
+ - uses: actions/checkout@v4
26
26
  - uses: ./.github/actions/ci-setup
27
27
 
28
28
  - name: Create Release Pull Request or Publish to npm
package/dist/index.d.mts CHANGED
@@ -1,17 +1,38 @@
1
1
  import { EventObject, AnyStateMachine, AnyEventObject, AnyActorRef, SnapshotFrom, EventFrom, PromiseActorLogic, ActorLogic, TransitionSnapshot, Values, ActorRefFrom, Subscription, StateValue, ObservableActorLogic } from 'xstate';
2
- import { TypeOf, SomeZodObject } from 'zod';
2
+ import { SomeZodObject, ZodType, TypeOf } from 'zod';
3
3
  import { generateText, streamText, LanguageModel, CoreMessage, GenerateTextResult, StreamTextResult, CoreTool } from 'ai';
4
4
 
5
+ type ZodEventMapping = {
6
+ [eventType: string]: SomeZodObject;
7
+ };
8
+ type ZodContextMapping = {
9
+ [contextKey: string]: ZodType;
10
+ };
11
+
5
12
  type GenerateTextOptions = Parameters<typeof generateText>[0];
6
13
  type StreamTextOptions = Parameters<typeof streamText>[0];
7
- type AgentPlanInput<TEvent extends EventObject> = {
8
- model: LanguageModel;
14
+ type AgentPlanInput<TEvent extends EventObject> = Omit<GenerateTextOptions, 'prompt' | 'messages' | 'tools'> & {
15
+ /**
16
+ * The currently observed state.
17
+ */
9
18
  state: ObservedState;
19
+ /**
20
+ * The goal for the agent to accomplish.
21
+ * The agent will create a plan based on this goal.
22
+ */
10
23
  goal: string;
24
+ /**
25
+ * The events that the agent can trigger. This is a mapping of
26
+ * event types to Zod event schemas.
27
+ */
11
28
  events: ZodEventMapping;
29
+ /**
30
+ * The state machine that represents the environment the agent
31
+ * is interacting with.
32
+ */
12
33
  machine?: AnyStateMachine;
13
34
  /**
14
- * The previous plan
35
+ * The previous plan.
15
36
  */
16
37
  previousPlan?: AgentPlan<TEvent>;
17
38
  };
@@ -19,10 +40,11 @@ type AgentPlan<TEvent extends EventObject> = {
19
40
  goal: string;
20
41
  state: ObservedState;
21
42
  content?: string;
22
- steps?: Array<{
23
- event: TEvent;
24
- state?: ObservedState;
25
- }>;
43
+ /**
44
+ * Executes the plan based on the given `state` and resolves with
45
+ * a potential next `event` to trigger to achieve the `goal`.
46
+ */
47
+ execute: (state: ObservedState) => Promise<TEvent | undefined>;
26
48
  nextEvent: TEvent | undefined;
27
49
  sessionId: string;
28
50
  timestamp: number;
@@ -60,10 +82,10 @@ type PromptTemplate<TEvents extends EventObject> = (data: {
60
82
  */
61
83
  observations?: AgentObservation<any>[];
62
84
  feedback?: AgentFeedback[];
63
- messages?: AgentMessageHistory[];
85
+ messages?: AgentMessage[];
64
86
  plans?: AgentPlan<TEvents>[];
65
87
  }) => string;
66
- type AgentPlanner<T extends Agent<any>> = (agent: T['eventTypes'], options: AgentPlanInput<T['eventTypes']>) => Promise<AgentPlan<T['eventTypes']> | undefined>;
88
+ type AgentPlanner<T extends AnyAgent> = (agent: T, input: AgentPlanInput<T['types']['events']>) => Promise<AgentPlan<T['types']['events']> | undefined>;
67
89
  type AgentDecideOptions = {
68
90
  goal: string;
69
91
  model?: LanguageModel;
@@ -87,7 +109,7 @@ interface AgentFeedbackInput {
87
109
  attributes: Record<string, any>;
88
110
  timestamp?: number;
89
111
  }
90
- type AgentMessageHistory = CoreMessage & {
112
+ type AgentMessage = CoreMessage & {
91
113
  timestamp: number;
92
114
  id: string;
93
115
  /**
@@ -98,7 +120,7 @@ type AgentMessageHistory = CoreMessage & {
98
120
  result?: GenerateTextResult<any>;
99
121
  sessionId: string;
100
122
  };
101
- type AgentMessageHistoryInput = CoreMessage & {
123
+ type AgentMessageInput = CoreMessage & {
102
124
  timestamp?: number;
103
125
  id?: string;
104
126
  /**
@@ -113,6 +135,7 @@ interface AgentObservation<TActor extends AnyActorRef> {
113
135
  prevState: SnapshotFrom<TActor> | undefined;
114
136
  event: EventFrom<TActor>;
115
137
  state: SnapshotFrom<TActor>;
138
+ machineHash: string | undefined;
116
139
  sessionId: string;
117
140
  timestamp: number;
118
141
  }
@@ -121,6 +144,7 @@ interface AgentObservationInput {
121
144
  prevState: ObservedState | undefined;
122
145
  event: AnyEventObject;
123
146
  state: ObservedState;
147
+ machine?: AnyStateMachine;
124
148
  timestamp?: number;
125
149
  }
126
150
  type AgentDecisionInput = {
@@ -137,7 +161,7 @@ type AgentEmitted<TEvents extends EventObject> = {
137
161
  observation: AgentObservation<any>;
138
162
  } | {
139
163
  type: 'message';
140
- message: AgentMessageHistory;
164
+ message: AgentMessage;
141
165
  } | {
142
166
  type: 'plan';
143
167
  plan: AgentPlan<TEvents>;
@@ -150,7 +174,7 @@ type AgentLogic<TEvents extends EventObject> = ActorLogic<TransitionSnapshot<Age
150
174
  observation: AgentObservation<any>;
151
175
  } | {
152
176
  type: 'agent.message';
153
- message: AgentMessageHistory;
177
+ message: AgentMessage;
154
178
  } | {
155
179
  type: 'agent.plan';
156
180
  plan: AgentPlan<TEvents>;
@@ -161,19 +185,25 @@ type EventsFromZodEventMapping<TEventSchemas extends ZodEventMapping> = Values<{
161
185
  type: K;
162
186
  } & TypeOf<TEventSchemas[K]>;
163
187
  }>;
164
- type Agent<TEvents extends EventObject> = ActorRefFrom<AgentLogic<TEvents>> & {
188
+ type ContextFromZodContextMapping<TContextSchema extends ZodContextMapping> = {
189
+ [K in keyof TContextSchema & string]: TypeOf<TContextSchema[K]>;
190
+ };
191
+ type Agent<TContext, TEvents extends EventObject> = ActorRefFrom<AgentLogic<TEvents>> & {
165
192
  /**
166
- * The general name of the agent. All agents with the same name are related and
193
+ * The name of the agent. All agents with the same name are related and
167
194
  * able to share experiences (observations, feedback) with each other.
168
195
  */
169
- name: string;
196
+ name?: string;
170
197
  /**
171
- * The unique id of the agent. This is used to partition message history.
198
+ * The unique identifier for the agent.
172
199
  */
173
200
  id?: string;
174
201
  description?: string;
175
202
  events: ZodEventMapping;
176
- eventTypes: TEvents;
203
+ types: {
204
+ events: TEvents;
205
+ context: Compute<TContext>;
206
+ };
177
207
  model: LanguageModel;
178
208
  defaultOptions: GenerateTextOptions;
179
209
  memory: AgentLongTermMemory | undefined;
@@ -189,37 +219,99 @@ type Agent<TEvents extends EventObject> = ActorRefFrom<AgentLogic<TEvents>> & {
189
219
  *
190
220
  * - The `goal` for the agent to achieve
191
221
  * - The observed current `state`
192
- * - The `logic` (e.g. a state machine) that specifies what can happen next
222
+ * - The `machine` (e.g. a state machine) that specifies what can happen next
193
223
  * - Additional `context`
194
224
  */
195
225
  decide: (options: AgentDecideOptions) => Promise<AgentPlan<TEvents> | undefined>;
196
226
  generateText: (options: AgentGenerateTextOptions) => Promise<GenerateTextResult<Record<string, any>>>;
197
227
  streamText: (options: AgentStreamTextOptions) => Promise<StreamTextResult<Record<string, CoreTool<any, any>>>>;
198
- addObservation: (observation: AgentObservationInput) => AgentObservation<any>;
199
- addMessage: (history: AgentMessageHistoryInput) => AgentMessageHistory;
200
- addFeedback: (feedbackItem: AgentFeedbackInput) => AgentFeedback;
228
+ addObservation: (observationInput: AgentObservationInput) => AgentObservation<any>;
229
+ addMessage: (messageInput: AgentMessageInput) => AgentMessage;
230
+ addFeedback: (feedbackInput: AgentFeedbackInput) => AgentFeedback;
201
231
  addPlan: (plan: AgentPlan<TEvents>) => void;
202
232
  /**
203
233
  * Called whenever the agent (LLM assistant) receives or sends a message.
204
234
  */
205
- onMessage: (callback: (message: AgentMessageHistory) => void) => void;
235
+ onMessage: (callback: (message: AgentMessage) => void) => void;
206
236
  /**
207
237
  * Selects agent data from its context.
238
+ *
239
+ * @deprecated Select from `agent.getSnapshot().context` directly or:
240
+ * - `agent.getMessages()`
241
+ * - `agent.getObservations()`
242
+ * - `agent.getFeedback()`
243
+ * - `agent.getPlans()`
208
244
  */
209
245
  select: <T>(selector: (context: AgentMemoryContext) => T) => T;
210
246
  /**
211
- * Inspects state machine actor transitions and automatically observes
212
- * (prevState, event, state) tuples.
247
+ * Retrieves messages from the agent's short-term (local) memory.
213
248
  */
214
- interact: <TActor extends AnyActorRef>(actorRef: TActor, getInput?: (observation: AgentObservation<TActor>) => AgentDecisionInput | undefined) => Subscription;
249
+ getMessages: () => AgentMessage[];
250
+ /**
251
+ * Retrieves observations from the agent's short-term (local) memory.
252
+ */
253
+ getObservations: () => AgentObservation<Agent<TContext, TEvents>>[];
254
+ /**
255
+ * Retrieves feedback from the agent's short-term (local) memory.
256
+ */
257
+ getFeedback: () => AgentFeedback[];
258
+ /**
259
+ * Retrieves strategies from the agent's short-term (local) memory.
260
+ */
261
+ getPlans: () => AgentPlan<TEvents>[];
262
+ /**
263
+ * Interacts with this state machine actor by inspecting state transitions and storing them as observations.
264
+ *
265
+ * Observations contain the `prevState`, `event`, and current `state` of this
266
+ * actor, as well as other properties that are useful when recalled.
267
+ * These observations are stored in the `agent`'s short-term (local) memory
268
+ * and can be retrieved via `agent.getObservations()`.
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * // Only observes the actor's state transitions
273
+ * agent.interact(actor);
274
+ *
275
+ * actor.start();
276
+ * ```
277
+ */
278
+ interact<TActor extends AnyActorRef>(actorRef: TActor): Subscription;
279
+ /**
280
+ * Interacts with this state machine actor by:
281
+ * 1. Inspecting state transitions and storing them as observations
282
+ * 2. Deciding what to do next (which event to send the actor) based on
283
+ * the agent input returned from `getInput(observation)`, if `getInput(…)` is provided as the 2nd argument.
284
+ *
285
+ * Observations contain the `prevState`, `event`, and current `state` of this
286
+ * actor, as well as other properties that are useful when recalled.
287
+ * These observations are stored in the `agent`'s short-term (local) memory
288
+ * and can be retrieved via `agent.getObservations()`.
289
+ *
290
+ * @example
291
+ * ```ts
292
+ * // Observes the actor's state transitions and
293
+ * // makes a decision if on the "summarize" state
294
+ * agent.interact(actor, observed => {
295
+ * if (observed.state.matches('summarize')) {
296
+ * return {
297
+ * context: observed.state.context,
298
+ * goal: 'Summarize the message'
299
+ * }
300
+ * }
301
+ * });
302
+ *
303
+ * actor.start();
304
+ * ```
305
+ */
306
+ interact<TActor extends AnyActorRef>(actorRef: TActor, getInput: (observation: AgentObservation<TActor>) => AgentDecisionInput | undefined): Subscription;
215
307
  };
216
- type AnyAgent = Agent<any>;
308
+ type AnyAgent = Agent<any, any>;
217
309
  type FromAgent<T> = T | ((self: AnyAgent) => T | Promise<T>);
218
310
  interface CommonTextOptions {
219
311
  prompt: FromAgent<string>;
220
312
  model?: LanguageModel;
221
313
  context?: Record<string, any>;
222
- messages?: FromAgent<CoreMessage[]> | true;
314
+ messages?: FromAgent<CoreMessage[]>;
223
315
  template?: PromptTemplate<any>;
224
316
  }
225
317
  type AgentGenerateTextOptions = Omit<GenerateTextOptions, 'model' | 'prompt' | 'messages'> & CommonTextOptions;
@@ -238,7 +330,7 @@ interface ObservedState {
238
330
  type ObservedStateFrom<TActor extends AnyActorRef> = Pick<SnapshotFrom<TActor>, 'value' | 'context'>;
239
331
  type AgentMemoryContext = {
240
332
  observations: AgentObservation<any>[];
241
- messages: AgentMessageHistory[];
333
+ messages: AgentMessage[];
242
334
  plans: AgentPlan<any>[];
243
335
  feedback: AgentFeedback[];
244
336
  };
@@ -256,16 +348,30 @@ interface AIAdapter {
256
348
  generateText: typeof generateText;
257
349
  streamText: typeof streamText;
258
350
  }
351
+ type Compute<A extends any> = {
352
+ [K in keyof A]: A[K];
353
+ } & unknown;
259
354
 
260
- type ZodEventMapping = {
261
- [eventType: string]: SomeZodObject;
262
- };
263
-
264
- declare function createAgent<const TEventSchemas extends ZodEventMapping, TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas>>({ name, description, model, events, planner, stringify, getMemory, logic, adapter, ...generateTextOptions }: {
355
+ declare function createAgent<const TContextSchema extends ZodContextMapping, const TEventSchemas extends ZodEventMapping, TEvents extends EventObject = EventsFromZodEventMapping<TEventSchemas>, TContext = ContextFromZodContextMapping<TContextSchema>>({ name, description, model, events, context, planner, stringify, getMemory, logic, adapter, ...generateTextOptions }: {
356
+ /**
357
+ * The unique identifier for the agent.
358
+ *
359
+ * This should be the same across all sessions of a specific agent, as it can be
360
+ * used to retrieve memory for this agent.
361
+ *
362
+ * @example
363
+ * ```ts
364
+ * const agent = createAgent({
365
+ * id: 'recipe-assistant',
366
+ * // ...
367
+ * });
368
+ * ```
369
+ */
370
+ id?: string;
265
371
  /**
266
372
  * The name of the agent
267
373
  */
268
- name: string;
374
+ name?: string;
269
375
  /**
270
376
  * A description of the role of the agent
271
377
  */
@@ -275,30 +381,29 @@ declare function createAgent<const TEventSchemas extends ZodEventMapping, TEvent
275
381
  * that the agent knows about.
276
382
  */
277
383
  events: TEventSchemas;
278
- planner?: AgentPlanner<Agent<TEvents>>;
384
+ context?: TContextSchema;
385
+ planner?: AgentPlanner<Agent<TContext, TEvents>>;
279
386
  stringify?: typeof JSON.stringify;
280
387
  /**
281
388
  * A function that retrieves the agent's long term memory
282
389
  */
283
- getMemory?: (agent: Agent<any>) => AgentLongTermMemory;
390
+ getMemory?: (agent: Agent<TContext, TEvents>) => AgentLongTermMemory;
284
391
  /**
285
392
  * Agent logic
286
393
  */
287
394
  logic?: AgentLogic<TEvents>;
288
395
  adapter?: AIAdapter;
289
- } & GenerateTextOptions): Agent<TEvents>;
396
+ } & GenerateTextOptions): Agent<TContext, TEvents>;
290
397
 
291
- declare function agentGenerateText<T extends Agent<any>>(agent: T, options: AgentGenerateTextOptions): Promise<GenerateTextResult<Record<string, CoreTool<any, any>>>>;
292
- declare function fromTextStream<T extends Agent<any>>(agent: T, defaultOptions?: AgentStreamTextOptions): ObservableActorLogic<{
398
+ declare function fromTextStream<T extends AnyAgent>(agent: T, defaultOptions?: AgentStreamTextOptions): ObservableActorLogic<{
293
399
  textDelta: string;
294
400
  }, Omit<AgentStreamTextOptions, 'context'> & {
295
- context?: AgentStreamTextOptions['context'] | boolean;
401
+ context?: AgentStreamTextOptions['context'];
296
402
  }>;
297
- declare function fromText<T extends Agent<any>>(agent: T, defaultOptions?: AgentGenerateTextOptions): PromiseActorLogic<GenerateTextResult<Record<string, CoreTool<any, any>>>, Omit<AgentGenerateTextOptions, 'context'> & {
298
- context?: AgentGenerateTextOptions['context'] | boolean;
403
+ declare function fromText<T extends AnyAgent>(agent: T, defaultOptions?: AgentGenerateTextOptions): PromiseActorLogic<GenerateTextResult<Record<string, CoreTool<any, any>>>, Omit<AgentGenerateTextOptions, 'context'> & {
404
+ context?: AgentGenerateTextOptions['context'];
299
405
  }>;
300
406
 
301
- declare function agentDecide<T extends Agent<any>>(agent: T, options: AgentDecideOptions): Promise<AgentPlan<any> | undefined>;
302
- declare function fromDecision(agent: Agent<any>, defaultInput?: AgentDecisionInput): AgentDecisionLogic<any>;
407
+ declare function fromDecision(agent: AnyAgent, defaultInput?: AgentDecisionInput): AgentDecisionLogic<any>;
303
408
 
304
- export { type AIAdapter, type Agent, type AgentDecideOptions, type AgentDecisionInput, type AgentDecisionLogic, type AgentEmitted, type AgentFeedback, type AgentFeedbackInput, type AgentGenerateTextOptions, type AgentLogic, type AgentLongTermMemory, type AgentMemory, type AgentMemoryContext, type AgentMessageHistory, type AgentMessageHistoryInput, type AgentObservation, type AgentObservationInput, type AgentPlan, type AgentPlanInput, type AgentPlanner, type AgentStreamTextOptions, type AnyAgent, type AppendOnlyStorage, type CommonTextOptions, type EventsFromZodEventMapping, type FromAgent, type GenerateTextOptions, type ObservedState, type ObservedStateFrom, type PromptTemplate, type StreamTextOptions, type TransitionData, agentDecide, agentGenerateText, createAgent, fromDecision, fromText, fromTextStream };
409
+ export { type AIAdapter, type Agent, type AgentDecideOptions, type AgentDecisionInput, type AgentDecisionLogic, type AgentEmitted, type AgentFeedback, type AgentFeedbackInput, type AgentGenerateTextOptions, type AgentLogic, type AgentLongTermMemory, type AgentMemory, type AgentMemoryContext, type AgentMessage, type AgentMessageInput, type AgentObservation, type AgentObservationInput, type AgentPlan, type AgentPlanInput, type AgentPlanner, type AgentStreamTextOptions, type AnyAgent, type AppendOnlyStorage, type CommonTextOptions, type Compute, type ContextFromZodContextMapping, type EventsFromZodEventMapping, type FromAgent, type GenerateTextOptions, type ObservedState, type ObservedStateFrom, type PromptTemplate, type StreamTextOptions, type TransitionData, createAgent, fromDecision, fromText, fromTextStream };