@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.
- package/.changeset/calm-beans-talk.md +5 -0
- package/.changeset/long-guests-explode.md +5 -0
- package/.changeset/nice-pants-rule.md +10 -0
- package/.changeset/odd-kiwis-compare.md +5 -0
- package/.changeset/pre.json +4 -0
- package/CHANGELOG.md +23 -0
- package/architecture.tldr +797 -0
- package/dist/index.d.mts +198 -140
- package/dist/index.d.ts +198 -140
- package/dist/index.js +4397 -148
- package/dist/index.mjs +4397 -149
- package/examples/chatbot.ts +9 -5
- package/examples/cot.ts +2 -4
- package/examples/jugs.ts +2 -2
- package/examples/learn-from-feedback.ts +7 -7
- package/examples/newspaper.ts +1 -1
- package/examples/rewoo.ts +62 -0
- package/examples/river-crossing.ts +2 -2
- package/examples/serverless.ts +71 -0
- package/examples/simple.ts +1 -1
- package/examples/ticTacToe.ts +6 -2
- package/examples/wiki.ts +2 -2
- package/package.json +14 -12
- package/readme.md +57 -0
- package/src/agent.test.ts +387 -30
- package/src/agent.ts +177 -64
- package/src/decide.test.ts +24 -2
- package/src/decide.ts +34 -78
- package/src/index.ts +1 -0
- package/src/{strategies/chainOfThought.ts → policies/chainOfThoughtPolicy.ts} +7 -9
- package/src/policies/index.ts +3 -0
- package/src/{strategies/shortestPath.test.ts → policies/shortestPathPolicy.test.ts} +2 -2
- package/src/{strategies/shortestPath.ts → policies/shortestPathPolicy.ts} +8 -8
- package/src/{strategies/simple.ts → policies/toolPolicy.ts} +27 -26
- package/src/text.ts +17 -22
- package/src/types.ts +162 -166
- 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
|
-
|
|
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 {
|
|
30
|
-
import {
|
|
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
|
-
|
|
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
|
|
107
|
+
description,
|
|
103
108
|
model,
|
|
104
109
|
events,
|
|
105
110
|
context,
|
|
106
111
|
episodeId,
|
|
107
|
-
|
|
108
|
-
logic = agentLogic
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
136
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
?
|
|
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
|
|
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
|
-
?
|
|
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
|
|
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
|
|
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
|
-
|
|
583
|
+
input: AgentDecideInput<this>
|
|
521
584
|
): Promise<AgentDecision<this> | undefined> {
|
|
522
|
-
|
|
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
|
}
|
package/src/decide.test.ts
CHANGED
|
@@ -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 (
|
|
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 {
|
|
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
|
-
|
|
11
|
+
AgentDecision,
|
|
10
12
|
} from './types';
|
|
11
13
|
import { getTransitions } from './utils';
|
|
12
|
-
import {
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
72
|
-
|
|
23
|
+
export type MachineDecisionLogic<TAgent extends AnyAgent> = PromiseActorLogic<
|
|
24
|
+
AgentDecision<TAgent> | undefined,
|
|
25
|
+
AgentDecideLogicInput | string
|
|
26
|
+
>;
|
|
73
27
|
|
|
74
|
-
export function fromDecision<
|
|
75
|
-
agent:
|
|
76
|
-
defaultInput?: AgentDecideInput<
|
|
77
|
-
):
|
|
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
|
|
45
|
+
const decision = await agent.decide({
|
|
92
46
|
machine: (parentRef as AnyActor).logic,
|
|
93
47
|
state: snapshot,
|
|
94
|
-
|
|
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
|
|
59
|
+
}) as MachineDecisionLogic<any>;
|
|
104
60
|
}
|
|
105
61
|
|
|
106
|
-
export function getToolMap<
|
|
107
|
-
|
|
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(
|
|
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