@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/.changeset/nice-pants-rule.md +10 -0
- package/.changeset/pre.json +1 -0
- package/CHANGELOG.md +11 -0
- package/architecture.tldr +175 -0
- package/dist/index.d.mts +44 -37
- package/dist/index.d.ts +44 -37
- package/dist/index.js +28 -53
- package/dist/index.mjs +28 -53
- package/examples/learn-from-feedback.ts +2 -2
- package/package.json +1 -1
- package/src/agent.test.ts +106 -8
- package/src/agent.ts +14 -15
- package/src/decide.test.ts +22 -0
- package/src/decide.ts +16 -18
- package/src/strategies/chainOfThought.ts +1 -1
- package/src/strategies/{simple.ts → simpleStrategy.ts} +1 -1
- package/src/text.ts +0 -1
- package/src/types.ts +27 -35
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/
|
|
29
|
+
import { simpleStrategy } from './strategies/simpleStrategy';
|
|
30
30
|
import { agentDecide } from './decide';
|
|
31
|
-
import {
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
|
|
519
|
+
input: AgentDecideInput<this>
|
|
521
520
|
): Promise<AgentDecision<this> | undefined> {
|
|
522
|
-
return agentDecide(this,
|
|
521
|
+
return agentDecide(this, input);
|
|
523
522
|
}
|
|
524
523
|
}
|
package/src/decide.test.ts
CHANGED
|
@@ -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:
|
|
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<
|
|
107
|
-
|
|
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(
|
|
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 './
|
|
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
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
|
|
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
|
-
|
|
157
|
+
AgentDecideInput<T>,
|
|
154
158
|
'state'
|
|
155
159
|
> & {
|
|
156
160
|
state?: never;
|
|
157
161
|
};
|
|
158
162
|
|
|
159
|
-
export type
|
|
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
|
|
192
|
-
|
|
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
|
|
347
|
+
goal?: string | undefined;
|
|
356
348
|
}
|
|
357
349
|
|
|
358
350
|
export type AgentDecisionInput = {
|