@statelyai/agent 2.0.0-next.1 → 2.0.0-next.2

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 (44) hide show
  1. package/.changeset/grumpy-dolphins-think.md +17 -0
  2. package/.changeset/old-teachers-tap.md +5 -0
  3. package/.changeset/pre.json +4 -1
  4. package/.changeset/smart-yaks-pull.md +23 -0
  5. package/CHANGELOG.md +40 -0
  6. package/dist/index.d.mts +55 -46
  7. package/dist/index.d.ts +55 -46
  8. package/dist/index.js +78 -50
  9. package/dist/index.mjs +81 -53
  10. package/examples/chatbot.ts +2 -2
  11. package/examples/cot.ts +6 -24
  12. package/examples/customer-service-sim.ts +3 -3
  13. package/examples/email.ts +9 -3
  14. package/examples/example.ts +2 -2
  15. package/examples/goal.ts +2 -2
  16. package/examples/joke.ts +2 -2
  17. package/examples/jugs.ts +4 -7
  18. package/examples/learn-from-feedback.ts +100 -0
  19. package/examples/number.ts +2 -2
  20. package/examples/raffle.ts +2 -2
  21. package/examples/river-crossing.ts +4 -7
  22. package/examples/simple.ts +13 -10
  23. package/examples/summary.ts +2 -5
  24. package/examples/support.ts +42 -38
  25. package/examples/ticTacToe.ts +46 -4
  26. package/examples/todo.ts +2 -2
  27. package/examples/tutor.ts +2 -2
  28. package/examples/verify.ts +2 -2
  29. package/examples/weather-agent.ts +141 -0
  30. package/examples/weather.ts +23 -23
  31. package/examples/word.ts +8 -6
  32. package/package.json +2 -1
  33. package/src/agent.test.ts +17 -15
  34. package/src/agent.ts +48 -35
  35. package/src/decide.test.ts +56 -8
  36. package/src/decide.ts +34 -24
  37. package/src/strategies/chainOfThought.ts +48 -0
  38. package/src/{planners → strategies}/shortestPath.test.ts +4 -7
  39. package/src/strategies/shortestPath.ts +173 -0
  40. package/src/{planners → strategies}/simple.ts +28 -18
  41. package/src/types.ts +62 -28
  42. package/src/utils.ts +12 -0
  43. package/src/planners/shortestPath.ts +0 -177
  44. package/src/strategies/chain-of-note.ts +0 -106
@@ -0,0 +1,173 @@
1
+ import { generateObject } from 'ai';
2
+ import {
3
+ AgentDecision,
4
+ AgentDecideInput,
5
+ AgentStrategy,
6
+ AgentStep,
7
+ AnyAgent,
8
+ CostFunction,
9
+ ObservedState,
10
+ } from '../types';
11
+ import { getShortestPaths } from '@xstate/graph';
12
+ import { z } from 'zod';
13
+ import { zodToJsonSchema } from 'zod-to-json-schema';
14
+ import Ajv from 'ajv';
15
+ import { AnyMachineSnapshot } from 'xstate';
16
+
17
+ const ajv = new Ajv();
18
+
19
+ function observedStatesEqual(state1: ObservedState, state2: ObservedState) {
20
+ // check state value && state context
21
+ return (
22
+ JSON.stringify(state1.value) === JSON.stringify(state2.value) &&
23
+ JSON.stringify(state1.context) === JSON.stringify(state2.context)
24
+ );
25
+ }
26
+
27
+ function trimSteps(steps: AgentStep<any>[], currentState: ObservedState) {
28
+ const index = steps.findIndex(
29
+ (step) => step.state && observedStatesEqual(step.state, currentState)
30
+ );
31
+
32
+ if (index === -1) {
33
+ return undefined;
34
+ }
35
+
36
+ return steps.slice(index + 1, steps.length);
37
+ }
38
+
39
+ export async function experimental_shortestPathStrategy<T extends AnyAgent>(
40
+ agent: T,
41
+ input: AgentDecideInput<any>
42
+ ): Promise<AgentDecision<any> | undefined> {
43
+ const costFunction: CostFunction<any> =
44
+ input.costFunction ?? ((path) => path.weight ?? Infinity);
45
+ const existingDecision = agent
46
+ .getDecisions()
47
+ .find((p) => p.strategy === 'shortestPath' && p.goal === input.goal);
48
+
49
+ let paths = existingDecision?.paths;
50
+
51
+ if (existingDecision) {
52
+ console.log('Existing decision found');
53
+ }
54
+
55
+ if (!input.machine && !existingDecision) {
56
+ return;
57
+ }
58
+
59
+ if (input.machine && !existingDecision) {
60
+ const contextSchema = zodToJsonSchema(z.object(agent.context));
61
+ const result = await generateObject({
62
+ model: agent.model,
63
+ system: input.system ?? agent.description,
64
+ prompt: `
65
+ <goal>
66
+ ${input.goal}
67
+ </goal>
68
+ <contextSchema>
69
+ ${contextSchema}
70
+ </contextSchema>
71
+
72
+
73
+ Update the context JSON schema so that it validates the context to determine that it reaches the goal. Return the result as a diff.
74
+
75
+ The contextSchema properties must not change. Do not add or remove properties, or modify the name of the properties.
76
+ Use "const" for exact required values and define ranges/types for flexible conditions.
77
+
78
+ Examples:
79
+ 1. For "user is logged in with admin role":
80
+ {
81
+ "contextSchema": "{"type": "object", "properties": {"role": {"const": "admin"}, "lastLogin": {"type": "string"}}, "required": ["role"]}"
82
+ }
83
+
84
+ 2. For "score is above 100":
85
+ {
86
+ "contextSchema": "{"type": "object", "properties": {"score": {"type": "number", "minimum": 100}}, "required": ["score"]}"
87
+ }
88
+
89
+ 3. For "fruits contain apple, orange, banana":
90
+ {
91
+ "type": "array",
92
+ "allOf": [
93
+ { "contains": { "const": "apple" } },
94
+ { "contains": { "const": "orange" } },
95
+ { "contains": { "const": "banana" } }
96
+ ]
97
+ }
98
+ `.trim(),
99
+ schema: z.object({
100
+ // valueSchema: z
101
+ // .string()
102
+ // .describe('The JSON Schema representing the goal state value'),
103
+ contextSchema: z
104
+ .object({
105
+ type: z.literal('object'),
106
+ properties: z.object(
107
+ Object.keys((contextSchema as any).properties).reduce(
108
+ (acc, key) => {
109
+ acc[key] = z.any();
110
+ return acc;
111
+ },
112
+ {} as any
113
+ )
114
+ ),
115
+ required: z.array(z.string()).optional(),
116
+ })
117
+ .describe('The JSON Schema representing the goal state context'),
118
+ }),
119
+ });
120
+
121
+ console.log(result.object);
122
+ const validateContext = ajv.compile(result.object.contextSchema);
123
+
124
+ const stateFilter = (state: AnyMachineSnapshot) => {
125
+ return validateContext(state.context);
126
+ };
127
+
128
+ const resolvedState = input.machine.resolveState({
129
+ ...input.state,
130
+ context: input.state.context ?? {},
131
+ });
132
+
133
+ paths = getShortestPaths(input.machine, {
134
+ fromState: resolvedState,
135
+ toState: stateFilter,
136
+ });
137
+ }
138
+
139
+ if (!paths) {
140
+ return undefined;
141
+ }
142
+
143
+ const trimmedPaths = paths
144
+ .map((path) => {
145
+ const trimmedSteps = trimSteps(path.steps, input.state);
146
+ if (!trimmedSteps) {
147
+ return undefined;
148
+ }
149
+ return {
150
+ ...path,
151
+ steps: trimmedSteps,
152
+ };
153
+ })
154
+ .filter((p): p is NonNullable<typeof p> => p !== undefined);
155
+
156
+ // Sort paths from least weight to most weight
157
+ const sortedPaths = trimmedPaths.sort(
158
+ (a, b) => costFunction(a) - costFunction(b)
159
+ );
160
+
161
+ const leastWeightPath = sortedPaths[0];
162
+ const nextStep = leastWeightPath?.steps[0];
163
+
164
+ return {
165
+ strategy: 'shortestPath',
166
+ episodeId: agent.episodeId,
167
+ goal: input.goal,
168
+ goalState: paths[0]?.state,
169
+ nextEvent: nextStep?.event,
170
+ paths,
171
+ timestamp: Date.now(),
172
+ };
173
+ }
@@ -1,23 +1,27 @@
1
1
  import { CoreMessage, generateText } from 'ai';
2
- import { AgentPlan, AgentPlanInput, PromptTemplate, AnyAgent } from '../types';
3
- import { randomId } from '../utils';
2
+ import {
3
+ AgentDecision,
4
+ AgentDecideInput,
5
+ PromptTemplate,
6
+ AnyAgent,
7
+ } from '../types';
8
+ import { convertToXml, randomId } from '../utils';
4
9
  import { getNextSnapshot } from 'xstate';
5
- import { defaultTextTemplate } from '../templates/defaultText';
6
10
  import { getMessages } from '../text';
7
11
  import { getToolMap } from '../decide';
8
12
 
9
- const simplePlannerPromptTemplate: PromptTemplate<any> = (data) => {
13
+ const simpleStrategyPromptTemplate: PromptTemplate<any> = (data) => {
10
14
  return `
11
- ${defaultTextTemplate(data)}
15
+ ${convertToXml(data)}
12
16
 
13
17
  Make at most one tool call to achieve the above goal. If the goal cannot be achieved with any tool calls, do not make any tool call.
14
18
  `.trim();
15
19
  };
16
20
 
17
- export async function simplePlanner<T extends AnyAgent>(
21
+ export async function simpleStrategy<T extends AnyAgent>(
18
22
  agent: T,
19
- input: AgentPlanInput<any>
20
- ): Promise<AgentPlan<any> | undefined> {
23
+ input: AgentDecideInput<any>
24
+ ): Promise<AgentDecision<any> | undefined> {
21
25
  const toolMap = getToolMap(agent, input);
22
26
 
23
27
  if (!toolMap) {
@@ -27,8 +31,8 @@ export async function simplePlanner<T extends AnyAgent>(
27
31
 
28
32
  // Create a prompt with the given context and goal.
29
33
  // The template is used to ensure that a single tool call at most is made.
30
- const prompt = simplePlannerPromptTemplate({
31
- context: input.state.context,
34
+ const prompt = simpleStrategyPromptTemplate({
35
+ context: input.context,
32
36
  goal: input.goal,
33
37
  });
34
38
 
@@ -38,20 +42,22 @@ export async function simplePlanner<T extends AnyAgent>(
38
42
 
39
43
  const {
40
44
  state,
45
+ context,
41
46
  machine,
42
- previousPlan,
47
+ prevDecision,
43
48
  events,
44
49
  goal,
45
50
  model: _,
46
51
  ...rest
47
52
  } = input;
48
53
 
49
- const machineState = input.machine
50
- ? input.machine.resolveState({
51
- ...input.state,
52
- context: input.state.context,
53
- })
54
- : undefined;
54
+ const machineState =
55
+ input.machine && input.state
56
+ ? input.machine.resolveState({
57
+ ...input.state,
58
+ context: input.state.context ?? {},
59
+ })
60
+ : undefined;
55
61
 
56
62
  const result = await generateText({
57
63
  ...rest,
@@ -81,7 +87,7 @@ export async function simplePlanner<T extends AnyAgent>(
81
87
  }
82
88
 
83
89
  return {
84
- planner: 'simple',
90
+ strategy: 'simple',
85
91
  goal: input.goal,
86
92
  goalState: input.state,
87
93
  nextEvent: singleResult.result,
@@ -103,3 +109,7 @@ export async function simplePlanner<T extends AnyAgent>(
103
109
  ],
104
110
  };
105
111
  }
112
+
113
+ export function createSimpleStrategy<T extends AnyAgent>() {
114
+ return simpleStrategy;
115
+ }
package/src/types.ts CHANGED
@@ -31,7 +31,7 @@ export type CostFunction<TEvent extends EventObject> = (
31
31
  path: AgentPath<TEvent>
32
32
  ) => number;
33
33
 
34
- export type AgentPlanInput<TEvent extends EventObject> = Omit<
34
+ export type AgentDecideInput<TEvent extends EventObject> = Omit<
35
35
  AgentGenerateTextOptions,
36
36
  'prompt' | 'tools'
37
37
  > & {
@@ -41,7 +41,7 @@ export type AgentPlanInput<TEvent extends EventObject> = Omit<
41
41
  state: ObservedState;
42
42
  /**
43
43
  * The goal for the agent to accomplish.
44
- * The agent will create a plan based on this goal.
44
+ * The agent will make a decision based on this goal.
45
45
  */
46
46
  goal: string;
47
47
  /**
@@ -55,9 +55,9 @@ export type AgentPlanInput<TEvent extends EventObject> = Omit<
55
55
  */
56
56
  machine?: AnyStateMachine;
57
57
  /**
58
- * The previous plan.
58
+ * The previous decision made by the agent.
59
59
  */
60
- previousPlan?: AgentPlan<TEvent>;
60
+ prevDecision?: AgentDecision<TEvent>;
61
61
 
62
62
  /**
63
63
  * The total cost of the path to the goal state.
@@ -65,7 +65,7 @@ export type AgentPlanInput<TEvent extends EventObject> = Omit<
65
65
  costFunction?: CostFunction<TEvent>;
66
66
 
67
67
  /**
68
- * The maximum number of attempts to generate a plan.
68
+ * The maximum number of attempts to make a decision.
69
69
  * Defaults to 2.
70
70
  */
71
71
  maxAttempts?: number;
@@ -86,14 +86,14 @@ export type AgentPath<TEvent extends EventObject> = {
86
86
  weight?: number;
87
87
  };
88
88
 
89
- export type AgentPlan<TEvent extends EventObject> = {
89
+ export type AgentDecision<TEvent extends EventObject> = {
90
90
  /**
91
- * The planner used to generate the plan
91
+ * The strategy used to generate the decision
92
92
  */
93
- planner: string;
93
+ strategy: string;
94
94
  goal: string;
95
95
  /**
96
- * The ending state of the plan.
96
+ * The ending state of the decision.
97
97
  */
98
98
  goalState: ObservedState | undefined;
99
99
  /**
@@ -144,22 +144,29 @@ export type PromptTemplate<TEvents extends EventObject> = (data: {
144
144
  observations?: AgentObservation<any>[]; // TODO
145
145
  feedback?: AgentFeedback[];
146
146
  messages?: AgentMessage[];
147
- plans?: AgentPlan<TEvents>[];
147
+ decisions?: AgentDecision<TEvents>[];
148
148
  }) => string;
149
149
 
150
- export type AgentPlanner<T extends AnyAgent> = (
150
+ export type AgentStrategy<T extends AnyAgent> = (
151
151
  agent: T,
152
- input: AgentPlanInput<T['types']['events']>
153
- ) => Promise<AgentPlan<T['types']['events']> | undefined>;
152
+ input: AgentDecideInput<EventsFromAgent<T>>
153
+ ) => Promise<AgentDecision<EventsFromAgent<T>> | undefined>;
154
+
155
+ export type AgentInteractInput<T extends AnyAgent> = Omit<
156
+ AgentDecideOptions<T>,
157
+ 'state'
158
+ >;
154
159
 
155
160
  export type AgentDecideOptions<T extends AnyAgent> = {
156
161
  goal: string;
157
- model?: LanguageModel;
158
162
  state: ObservedState;
163
+ context?: Record<string, any>;
159
164
  machine?: AnyStateMachine;
165
+ model?: LanguageModel;
160
166
  execute?: (event: AnyEventObject) => Promise<void>;
161
- planner?: AgentPlanner<T>;
167
+ strategy?: AgentStrategy<T>;
162
168
  events?: ZodEventMapping;
169
+ allowedEvents?: Array<EventsFromAgent<T>['type']>;
163
170
  /**
164
171
  * The maximum number of times the agent will attempt to make a decision.
165
172
  * Defaults to 2.
@@ -168,23 +175,21 @@ export type AgentDecideOptions<T extends AnyAgent> = {
168
175
  } & Omit<Parameters<typeof generateText>[0], 'model' | 'tools' | 'prompt'>;
169
176
 
170
177
  export interface AgentFeedback {
171
- goal?: string;
172
- observationId?: string;
178
+ goal: string;
179
+ observationId: string;
173
180
  /**
174
181
  * The message correlation that the feedback is relevant for
175
182
  */
176
183
  attributes: Record<string, any>;
177
- reward: number;
178
184
  timestamp: number;
179
185
  episodeId: string;
180
186
  }
181
187
 
182
188
  export interface AgentFeedbackInput {
183
- goal?: string;
184
- observationId?: string;
185
- attributes?: Record<string, any>;
189
+ goal: string;
190
+ observationId: string;
191
+ attributes: Record<string, any>;
186
192
  timestamp?: number;
187
- reward?: number;
188
193
  }
189
194
 
190
195
  export type AgentMessage = CoreMessage & {
@@ -327,6 +332,7 @@ export type AgentMessageInput = CoreMessage & {
327
332
 
328
333
  export interface AgentObservation<TActor extends ActorRefLike> {
329
334
  id: string;
335
+ // TODO: goal
330
336
  prevState: SnapshotFrom<TActor> | undefined;
331
337
  event: EventFrom<TActor> | undefined;
332
338
  state: SnapshotFrom<TActor>;
@@ -351,7 +357,7 @@ export type AgentDecisionInput = {
351
357
  } & Omit<Parameters<typeof generateText>[0], 'model' | 'tools' | 'prompt'>;
352
358
 
353
359
  export type AgentDecisionLogic<TEvents extends EventObject> = PromiseActorLogic<
354
- AgentPlan<TEvents> | undefined,
360
+ AgentDecision<TEvents> | undefined,
355
361
  AgentDecisionInput | string
356
362
  >;
357
363
 
@@ -369,8 +375,8 @@ export type AgentEmitted<TEvents extends EventObject> =
369
375
  message: AgentMessage;
370
376
  }
371
377
  | {
372
- type: 'plan';
373
- plan: AgentPlan<TEvents>;
378
+ type: 'decision';
379
+ decision: AgentDecision<TEvents>;
374
380
  };
375
381
 
376
382
  export type AgentLogic<TEvents extends EventObject> = ActorLogic<
@@ -388,8 +394,8 @@ export type AgentLogic<TEvents extends EventObject> = ActorLogic<
388
394
  message: AgentMessage;
389
395
  }
390
396
  | {
391
- type: 'agent.plan';
392
- plan: AgentPlan<TEvents>;
397
+ type: 'agent.decision';
398
+ decision: AgentDecision<TEvents>;
393
399
  },
394
400
  any, // TODO: input
395
401
  any,
@@ -453,7 +459,7 @@ export type ObservedStateFrom<TActor extends ActorRefLike> = Pick<
453
459
  export type AgentMemoryContext = {
454
460
  observations: AgentObservation<any>[]; // TODO
455
461
  messages: AgentMessage[];
456
- plans: AgentPlan<any>[];
462
+ decisions: AgentDecision<any>[];
457
463
  feedback: AgentFeedback[];
458
464
  };
459
465
 
@@ -472,3 +478,31 @@ export interface AgentLongTermMemory {
472
478
  }
473
479
 
474
480
  export type Compute<A extends any> = { [K in keyof A]: A[K] } & unknown;
481
+
482
+ export type MaybePromise<T> = T | Promise<T>;
483
+
484
+ export type EventsFromAgent<T extends AnyAgent> = T extends Agent<
485
+ infer _,
486
+ infer __,
487
+ infer TEvents,
488
+ infer ___
489
+ >
490
+ ? TEvents
491
+ : never;
492
+
493
+ export type TypesFromAgent<T extends AnyAgent> = T extends Agent<
494
+ infer TContextSchema,
495
+ infer TEventSchema
496
+ >
497
+ ? {
498
+ context: ContextFromZodContextMapping<TContextSchema>;
499
+ events: EventsFromZodEventMapping<TEventSchema>;
500
+ }
501
+ : never;
502
+
503
+ export type ContextFromAgent<T extends AnyAgent> = T extends Agent<
504
+ infer TContextSchema,
505
+ infer _TEventSchema
506
+ >
507
+ ? ContextFromZodContextMapping<TContextSchema>
508
+ : never;
package/src/utils.ts CHANGED
@@ -59,6 +59,18 @@ export function wrapInXml(tagName: string, content: string): string {
59
59
  return `<${tagName}>${content}</${tagName}>`;
60
60
  }
61
61
 
62
+ export function convertToXml(obj: Record<string, any>): string {
63
+ return Object.entries(obj)
64
+ .map(([key, value]) => {
65
+ if (typeof value === 'object' && value !== null) {
66
+ return wrapInXml(key, convertToXml(value));
67
+ } else {
68
+ return wrapInXml(key, value);
69
+ }
70
+ })
71
+ .join('');
72
+ }
73
+
62
74
  export function randomId(prefix?: string): string {
63
75
  const timestamp = Date.now().toString(36);
64
76
  const random = Math.random().toString(36).substring(2, 9);
@@ -1,177 +0,0 @@
1
- import { generateObject } from 'ai';
2
- import {
3
- AgentPlan,
4
- AgentPlanInput,
5
- AgentPlanner,
6
- AgentStep,
7
- AnyAgent,
8
- CostFunction,
9
- ObservedState,
10
- } from '../types';
11
- import { getShortestPaths } from '@xstate/graph';
12
- import { z } from 'zod';
13
- import { zodToJsonSchema } from 'zod-to-json-schema';
14
- import Ajv from 'ajv';
15
- import { AnyMachineSnapshot } from 'xstate';
16
-
17
- const ajv = new Ajv();
18
-
19
- function observedStatesEqual(state1: ObservedState, state2: ObservedState) {
20
- // check state value && state context
21
- return (
22
- JSON.stringify(state1.value) === JSON.stringify(state2.value) &&
23
- JSON.stringify(state1.context) === JSON.stringify(state2.context)
24
- );
25
- }
26
-
27
- function trimSteps(steps: AgentStep<any>[], currentState: ObservedState) {
28
- const index = steps.findIndex(
29
- (step) => step.state && observedStatesEqual(step.state, currentState)
30
- );
31
-
32
- if (index === -1) {
33
- return undefined;
34
- }
35
-
36
- return steps.slice(index + 1, steps.length);
37
- }
38
-
39
- export function experimental_createShortestPathPlanner<
40
- T extends AnyAgent
41
- >(): AgentPlanner<T> {
42
- return async function shortestPathPlanner<T extends AnyAgent>(
43
- agent: T,
44
- input: AgentPlanInput<any>
45
- ): Promise<AgentPlan<any> | undefined> {
46
- const costFunction: CostFunction<any> =
47
- input.costFunction ?? ((path) => path.weight ?? Infinity);
48
- const existingPlan = agent
49
- .getPlans()
50
- .find((p) => p.planner === 'shortestPath' && p.goal === input.goal);
51
-
52
- let paths = existingPlan?.paths;
53
-
54
- if (existingPlan) {
55
- console.log('Existing plan found');
56
- }
57
-
58
- if (!input.machine && !existingPlan) {
59
- return;
60
- }
61
-
62
- if (input.machine && !existingPlan) {
63
- const contextSchema = zodToJsonSchema(z.object(agent.context));
64
- const result = await generateObject({
65
- model: agent.model,
66
- system: input.system ?? agent.description,
67
- prompt: `
68
- <goal>
69
- ${input.goal}
70
- </goal>
71
- <contextSchema>
72
- ${contextSchema}
73
- </contextSchema>
74
-
75
-
76
- Update the context JSON schema so that it validates the context to determine that it reaches the goal. Return the result as a diff.
77
-
78
- The contextSchema properties must not change. Do not add or remove properties, or modify the name of the properties.
79
- Use "const" for exact required values and define ranges/types for flexible conditions.
80
-
81
- Examples:
82
- 1. For "user is logged in with admin role":
83
- {
84
- "contextSchema": "{"type": "object", "properties": {"role": {"const": "admin"}, "lastLogin": {"type": "string"}}, "required": ["role"]}"
85
- }
86
-
87
- 2. For "score is above 100":
88
- {
89
- "contextSchema": "{"type": "object", "properties": {"score": {"type": "number", "minimum": 100}}, "required": ["score"]}"
90
- }
91
-
92
- 3. For "fruits contain apple, orange, banana":
93
- {
94
- "type": "array",
95
- "allOf": [
96
- { "contains": { "const": "apple" } },
97
- { "contains": { "const": "orange" } },
98
- { "contains": { "const": "banana" } }
99
- ]
100
- }
101
- `.trim(),
102
- schema: z.object({
103
- // valueSchema: z
104
- // .string()
105
- // .describe('The JSON Schema representing the goal state value'),
106
- contextSchema: z
107
- .object({
108
- type: z.literal('object'),
109
- properties: z.object(
110
- Object.keys((contextSchema as any).properties).reduce(
111
- (acc, key) => {
112
- acc[key] = z.any();
113
- return acc;
114
- },
115
- {} as any
116
- )
117
- ),
118
- required: z.array(z.string()).optional(),
119
- })
120
- .describe('The JSON Schema representing the goal state context'),
121
- }),
122
- });
123
-
124
- console.log(result.object);
125
- const validateContext = ajv.compile(result.object.contextSchema);
126
-
127
- const stateFilter = (state: AnyMachineSnapshot) => {
128
- return validateContext(state.context);
129
- };
130
-
131
- const resolvedState = input.machine.resolveState({
132
- ...input.state,
133
- context: input.state.context ?? {},
134
- });
135
-
136
- paths = getShortestPaths(input.machine, {
137
- fromState: resolvedState,
138
- toState: stateFilter,
139
- });
140
- }
141
-
142
- if (!paths) {
143
- return undefined;
144
- }
145
-
146
- const trimmedPaths = paths
147
- .map((path) => {
148
- const trimmedSteps = trimSteps(path.steps, input.state);
149
- if (!trimmedSteps) {
150
- return undefined;
151
- }
152
- return {
153
- ...path,
154
- steps: trimmedSteps,
155
- };
156
- })
157
- .filter((p): p is NonNullable<typeof p> => p !== undefined);
158
-
159
- // Sort paths from least weight to most weight
160
- const sortedPaths = trimmedPaths.sort(
161
- (a, b) => costFunction(a) - costFunction(b)
162
- );
163
-
164
- const leastWeightPath = sortedPaths[0];
165
- const nextStep = leastWeightPath?.steps[0];
166
-
167
- return {
168
- planner: 'shortestPath',
169
- episodeId: agent.episodeId,
170
- goal: input.goal,
171
- goalState: paths[0]?.state,
172
- nextEvent: nextStep?.event,
173
- paths,
174
- timestamp: Date.now(),
175
- };
176
- };
177
- }