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

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 (45) hide show
  1. package/.changeset/cyan-carpets-perform.md +5 -0
  2. package/.changeset/fast-donkeys-argue.md +5 -0
  3. package/.changeset/old-jobs-check.md +5 -0
  4. package/.changeset/pre.json +4 -1
  5. package/CHANGELOG.md +12 -0
  6. package/dist/index.d.mts +19 -25
  7. package/dist/index.d.ts +19 -25
  8. package/dist/index.js +43 -39
  9. package/dist/index.mjs +43 -39
  10. package/examples/chatbot-alt.ts +1 -1
  11. package/examples/chatbot.ts +1 -1
  12. package/examples/cot.ts +1 -1
  13. package/examples/customer-service-sim.ts +4 -4
  14. package/examples/email.ts +29 -33
  15. package/examples/example.ts +1 -1
  16. package/examples/goal.ts +1 -1
  17. package/examples/joke.ts +1 -1
  18. package/examples/jugs.ts +3 -3
  19. package/examples/multi.ts +1 -1
  20. package/examples/number.ts +1 -1
  21. package/examples/raffle.ts +1 -1
  22. package/examples/river-crossing.ts +3 -3
  23. package/examples/simple.ts +1 -1
  24. package/examples/summary.ts +1 -1
  25. package/examples/support.ts +1 -1
  26. package/examples/ticTacToe.ts +2 -2
  27. package/examples/todo.ts +1 -1
  28. package/examples/tutor.ts +2 -2
  29. package/examples/verify.ts +1 -1
  30. package/examples/weather.ts +1 -1
  31. package/examples/wiki.ts +1 -1
  32. package/examples/word.ts +1 -1
  33. package/package.json +5 -3
  34. package/src/agent.test.ts +150 -10
  35. package/src/agent.ts +20 -217
  36. package/src/decide.test.ts +124 -3
  37. package/src/decide.ts +24 -14
  38. package/src/middleware.ts +2 -14
  39. package/src/planners/shortestPath.test.ts +94 -0
  40. package/src/planners/shortestPath.ts +177 -0
  41. package/src/planners/{simplePlanner.ts → simple.ts} +6 -12
  42. package/src/types.ts +15 -8
  43. package/src/utils.ts +11 -0
  44. package/vitest.config.ts +9 -3
  45. package/src/planners/shortestPathPlanner.ts +0 -160
package/src/decide.ts CHANGED
@@ -10,13 +10,13 @@ import {
10
10
  AgentPlanInput,
11
11
  TransitionData,
12
12
  } from './types';
13
- import { simplePlanner } from './planners/simplePlanner';
13
+ import { simplePlanner } from './planners/simple';
14
14
  import { getTransitions } from './utils';
15
- import { CoreTool, tool } from 'ai';
15
+ import { CoreMessage, CoreTool, tool } from 'ai';
16
16
 
17
17
  export async function agentDecide<T extends AnyAgent>(
18
18
  agent: T,
19
- options: AgentDecideOptions
19
+ options: AgentDecideOptions<T>
20
20
  ): Promise<AgentPlan<EventsFromZodEventMapping<T['events']>> | undefined> {
21
21
  const resolvedOptions = {
22
22
  ...agent.defaultOptions,
@@ -29,21 +29,31 @@ export async function agentDecide<T extends AnyAgent>(
29
29
  state,
30
30
  machine,
31
31
  model = agent.model,
32
+ messages,
32
33
  ...otherPlanInput
33
34
  } = resolvedOptions;
34
35
 
35
- const plan = await planner(agent, {
36
- model,
37
- goal,
38
- events,
39
- state,
40
- machine,
41
- ...otherPlanInput,
42
- });
36
+ let attempts = 0;
37
+
38
+ const maxAttempts = resolvedOptions.maxAttempts ?? 2;
39
+
40
+ let plan;
43
41
 
44
- if (plan?.nextEvent) {
45
- agent.addPlan(plan);
46
- await resolvedOptions.execute?.(plan.nextEvent);
42
+ while (attempts++ < maxAttempts) {
43
+ plan = await planner(agent, {
44
+ model,
45
+ goal,
46
+ events,
47
+ state,
48
+ machine,
49
+ messages: messages as CoreMessage[], // TODO: fix UIMessage thing
50
+ ...otherPlanInput,
51
+ });
52
+
53
+ if (plan?.nextEvent) {
54
+ agent.addPlan(plan);
55
+ await resolvedOptions.execute?.(plan.nextEvent);
56
+ }
47
57
  }
48
58
 
49
59
  return plan;
package/src/middleware.ts CHANGED
@@ -17,15 +17,11 @@ export function createAgentMiddleware(agent: AnyAgent) {
17
17
  wrapGenerate: async ({ doGenerate, params }) => {
18
18
  const id = randomId();
19
19
 
20
- params.prompt.forEach((p) => {
20
+ params.prompt.forEach((message) => {
21
21
  agent.addMessage({
22
22
  id,
23
- ...p,
23
+ ...message,
24
24
  timestamp: Date.now(),
25
- correlationId: params.providerMetadata
26
- ?.correlationId as unknown as string,
27
- parentCorrelationId: params.providerMetadata
28
- ?.parentCorrelationId as unknown as string,
29
25
  });
30
26
  });
31
27
 
@@ -43,10 +39,6 @@ export function createAgentMiddleware(agent: AnyAgent) {
43
39
  id,
44
40
  ...message,
45
41
  timestamp: Date.now(),
46
- correlationId: params.providerMetadata
47
- ?.correlationId as unknown as string,
48
- parentCorrelationId: params.providerMetadata
49
- ?.parentCorrelationId as unknown as string,
50
42
  });
51
43
  });
52
44
 
@@ -85,10 +77,6 @@ export function createAgentMiddleware(agent: AnyAgent) {
85
77
  role: 'assistant',
86
78
  content,
87
79
  responseId: id,
88
- correlationId: params.providerMetadata
89
- ?.correlationId as unknown as string,
90
- parentCorrelationId: params.providerMetadata
91
- ?.parentCorrelationId as unknown as string,
92
80
  });
93
81
  },
94
82
  });
@@ -0,0 +1,94 @@
1
+ import { createAgent } from '../';
2
+ import { assign, createActor, setup } from 'xstate';
3
+ import { z } from 'zod';
4
+ import { experimental_createShortestPathPlanner } from './shortestPath';
5
+ import { test, expect } from 'vitest';
6
+ import { dummyResponseValues, MockLanguageModelV1 } from '../mockModel';
7
+
8
+ test.skip('should find shortest path to goal', async () => {
9
+ const agent = createAgent({
10
+ id: 'counter',
11
+ model: new MockLanguageModelV1({
12
+ doGenerate: async () => {
13
+ return {
14
+ ...dummyResponseValues,
15
+ text: JSON.stringify({
16
+ type: 'object',
17
+ properties: {
18
+ count: {
19
+ type: 'number',
20
+ const: 3,
21
+ },
22
+ },
23
+ required: ['count'],
24
+ }),
25
+ };
26
+ },
27
+ }),
28
+ events: {
29
+ increment: z.object({}).describe('Increment the counter by 1'),
30
+ decrement: z.object({}).describe('Decrement the counter by 1'),
31
+ },
32
+ context: {
33
+ count: z.number().int().describe('Current count value'),
34
+ },
35
+ });
36
+
37
+ const counterMachine = setup({
38
+ types: {
39
+ context: agent.types.context,
40
+ events: agent.types.events,
41
+ },
42
+ }).createMachine({
43
+ initial: 'counting',
44
+ context: { count: 0 },
45
+ states: {
46
+ counting: {
47
+ always: {
48
+ guard: ({ context }) => context.count === 3,
49
+ target: 'success',
50
+ },
51
+ on: {
52
+ increment: {
53
+ actions: assign({ count: ({ context }) => context.count + 1 }),
54
+ },
55
+ decrement: {
56
+ actions: assign({ count: ({ context }) => context.count - 1 }),
57
+ },
58
+ },
59
+ },
60
+ success: {
61
+ type: 'final',
62
+ },
63
+ },
64
+ });
65
+
66
+ const counterActor = createActor(counterMachine).start();
67
+
68
+ const decision = await agent.decide({
69
+ machine: counterMachine,
70
+ model: new MockLanguageModelV1({
71
+ defaultObjectGenerationMode: 'tool',
72
+ doGenerate: async () => {
73
+ return {
74
+ ...dummyResponseValues,
75
+ text: JSON.stringify({
76
+ type: 'object',
77
+ properties: {
78
+ count: {
79
+ type: 'number',
80
+ const: 3,
81
+ },
82
+ },
83
+ required: ['count'],
84
+ }),
85
+ };
86
+ },
87
+ }),
88
+ goal: 'Get the counter to exactly 3',
89
+ state: counterActor.getSnapshot(),
90
+ planner: experimental_createShortestPathPlanner(),
91
+ });
92
+
93
+ expect(decision?.nextEvent?.type).toBe('increment');
94
+ });
@@ -0,0 +1,177 @@
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
+ }
@@ -1,14 +1,7 @@
1
- import { CoreMessage, type CoreTool, generateText, tool } from 'ai';
2
- import {
3
- AgentPlan,
4
- AgentPlanInput,
5
- ObservedState,
6
- PromptTemplate,
7
- TransitionData,
8
- AnyAgent,
9
- } from '../types';
10
- import { getAllTransitions, randomId } from '../utils';
11
- import { AnyStateMachine, getNextSnapshot } from 'xstate';
1
+ import { CoreMessage, generateText } from 'ai';
2
+ import { AgentPlan, AgentPlanInput, PromptTemplate, AnyAgent } from '../types';
3
+ import { randomId } from '../utils';
4
+ import { getNextSnapshot } from 'xstate';
12
5
  import { defaultTextTemplate } from '../templates/defaultText';
13
6
  import { getMessages } from '../text';
14
7
  import { getToolMap } from '../decide';
@@ -62,13 +55,14 @@ export async function simplePlanner<T extends AnyAgent>(
62
55
 
63
56
  const result = await generateText({
64
57
  ...rest,
58
+ system: input.system ?? agent.description,
65
59
  model,
66
60
  messages,
67
61
  tools: toolMap as any,
68
62
  toolChoice: input.toolChoice ?? 'required',
69
63
  });
70
64
 
71
- result.responseMessages.forEach((m) => {
65
+ result.response.messages.forEach((m) => {
72
66
  const message: CoreMessage = m;
73
67
 
74
68
  agent.addMessage({
package/src/types.ts CHANGED
@@ -32,7 +32,7 @@ export type CostFunction<TEvent extends EventObject> = (
32
32
  ) => number;
33
33
 
34
34
  export type AgentPlanInput<TEvent extends EventObject> = Omit<
35
- GenerateTextOptions,
35
+ AgentGenerateTextOptions,
36
36
  'prompt' | 'tools'
37
37
  > & {
38
38
  /**
@@ -63,6 +63,12 @@ export type AgentPlanInput<TEvent extends EventObject> = Omit<
63
63
  * The total cost of the path to the goal state.
64
64
  */
65
65
  costFunction?: CostFunction<TEvent>;
66
+
67
+ /**
68
+ * The maximum number of attempts to generate a plan.
69
+ * Defaults to 2.
70
+ */
71
+ maxAttempts?: number;
66
72
  };
67
73
 
68
74
  export type AgentStep<TEvent extends EventObject> = {
@@ -146,14 +152,19 @@ export type AgentPlanner<T extends AnyAgent> = (
146
152
  input: AgentPlanInput<T['types']['events']>
147
153
  ) => Promise<AgentPlan<T['types']['events']> | undefined>;
148
154
 
149
- export type AgentDecideOptions = {
155
+ export type AgentDecideOptions<T extends AnyAgent> = {
150
156
  goal: string;
151
157
  model?: LanguageModel;
152
158
  state: ObservedState;
153
159
  machine?: AnyStateMachine;
154
160
  execute?: (event: AnyEventObject) => Promise<void>;
155
- planner?: AgentPlanner<any>;
161
+ planner?: AgentPlanner<T>;
156
162
  events?: ZodEventMapping;
163
+ /**
164
+ * The maximum number of times the agent will attempt to make a decision.
165
+ * Defaults to 2.
166
+ */
167
+ maxAttempts?: number;
157
168
  } & Omit<Parameters<typeof generateText>[0], 'model' | 'tools' | 'prompt'>;
158
169
 
159
170
  export interface AgentFeedback {
@@ -162,7 +173,6 @@ export interface AgentFeedback {
162
173
  /**
163
174
  * The message correlation that the feedback is relevant for
164
175
  */
165
- correlationId?: string;
166
176
  attributes: Record<string, any>;
167
177
  reward: number;
168
178
  timestamp: number;
@@ -172,7 +182,6 @@ export interface AgentFeedback {
172
182
  export interface AgentFeedbackInput {
173
183
  goal?: string;
174
184
  observationId?: string;
175
- correlationId?: string;
176
185
  attributes?: Record<string, any>;
177
186
  timestamp?: number;
178
187
  reward?: number;
@@ -313,8 +322,6 @@ export type AgentMessageInput = CoreMessage & {
313
322
  * which message this message is responding to, if any.
314
323
  */
315
324
  responseId?: string;
316
- correlationId?: string;
317
- parentCorrelationId?: string;
318
325
  result?: GenerateTextResult<any>;
319
326
  };
320
327
 
@@ -402,7 +409,7 @@ export type ContextFromZodContextMapping<
402
409
  [K in keyof TContextSchema & string]: TypeOf<TContextSchema[K]>;
403
410
  };
404
411
 
405
- export type AnyAgent = Agent<any, any>;
412
+ export type AnyAgent = Agent<any, any, any, any>;
406
413
 
407
414
  export type FromAgent<T> = T | ((agent: AnyAgent) => T | Promise<T>);
408
415
 
package/src/utils.ts CHANGED
@@ -103,3 +103,14 @@ export function getTransitions(
103
103
  });
104
104
  return getAllTransitions(resolvedState);
105
105
  }
106
+
107
+ export function isMachineActor(
108
+ actor: ActorRefLike
109
+ ): actor is typeof actor & { src: AnyStateMachine } {
110
+ return (
111
+ 'src' in actor &&
112
+ typeof actor.src === 'object' &&
113
+ actor.src !== null &&
114
+ 'definition' in actor.src
115
+ );
116
+ }
package/vitest.config.ts CHANGED
@@ -1,9 +1,15 @@
1
- // vitest.config.ts
1
+ import { defineConfig } from 'vitest/config';
2
2
  import dotenv from 'dotenv';
3
3
  dotenv.config();
4
4
 
5
- export default {
5
+ export default defineConfig({
6
6
  test: {
7
7
  testTimeout: 10000, // Global timeout of 10000ms for all tests
8
+ coverage: {
9
+ provider: 'v8',
10
+ reporter: ['text', 'json', 'html'],
11
+ exclude: ['**.test.ts'],
12
+ include: ['src'],
13
+ },
8
14
  },
9
- };
15
+ });
@@ -1,160 +0,0 @@
1
- import { generateObject } from 'ai';
2
- import { getToolMap } from '../decide';
3
- import {
4
- AgentPlan,
5
- AgentPlanInput,
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
-
16
- const ajv = new Ajv();
17
-
18
- function observedStatesEqual(state1: ObservedState, state2: ObservedState) {
19
- // check state value && state context
20
- return (
21
- JSON.stringify(state1.value) === JSON.stringify(state2.value) &&
22
- JSON.stringify(state1.context) === JSON.stringify(state2.context)
23
- );
24
- }
25
-
26
- function trimSteps(steps: AgentStep<any>[], currentState: ObservedState) {
27
- const index = steps.findIndex(
28
- (step) => step.state && observedStatesEqual(step.state, currentState)
29
- );
30
-
31
- if (index === -1) {
32
- return undefined;
33
- }
34
-
35
- return steps.slice(index + 1, steps.length);
36
- }
37
-
38
- export async function shortestPathPlanner<T extends AnyAgent>(
39
- agent: T,
40
- input: AgentPlanInput<any>
41
- ): Promise<AgentPlan<any> | undefined> {
42
- const costFunction: CostFunction<any> =
43
- input.costFunction ?? ((path) => path.weight ?? Infinity);
44
- const existingPlan = agent
45
- .getPlans()
46
- .find((p) => p.planner === 'shortestPath' && p.goal === input.goal);
47
-
48
- let paths = existingPlan?.paths;
49
-
50
- if (existingPlan) {
51
- console.log('Existing plan found');
52
- }
53
-
54
- if (!input.machine && !existingPlan) {
55
- return;
56
- }
57
-
58
- if (input.machine && !existingPlan) {
59
- const contextSchema = zodToJsonSchema(z.object(agent.context));
60
- const result = await generateObject({
61
- model: agent.model,
62
- prompt: `
63
- <goal>
64
- ${input.goal}
65
- </goal>
66
- <contextSchema>
67
- ${contextSchema}
68
- </contextSchema>
69
-
70
-
71
- Update the context JSON schema so that it validates the context to determine that it reaches the goal. Return the result as a diff.
72
-
73
- The contextSchema properties must not change. Do not add or remove properties, or modify the name of the properties.
74
- Use "const" for exact required values and define ranges/types for flexible conditions.
75
-
76
- Examples:
77
- 1. For "user is logged in with admin role":
78
- {
79
- "contextSchema": "{"type": "object", "properties": {"role": {"const": "admin"}, "lastLogin": {"type": "string"}}, "required": ["role"]}"
80
- }
81
-
82
- 2. For "score is above 100":
83
- {
84
- "contextSchema": "{"type": "object", "properties": {"score": {"type": "number", "minimum": 100}}, "required": ["score"]}"
85
- }
86
- `.trim(),
87
- schema: z.object({
88
- // valueSchema: z
89
- // .string()
90
- // .describe('The JSON Schema representing the goal state value'),
91
- contextSchema: z
92
- .object({
93
- type: z.literal('object'),
94
- properties: z.object(
95
- Object.keys((contextSchema as any).properties).reduce(
96
- (acc, key) => {
97
- acc[key] = z.any();
98
- return acc;
99
- },
100
- {} as any
101
- )
102
- ),
103
- required: z.array(z.string()).optional(),
104
- })
105
- .describe('The JSON Schema representing the goal state context'),
106
- }),
107
- });
108
-
109
- console.log(result.object);
110
- const validateContext = ajv.compile(result.object.contextSchema);
111
-
112
- const resolvedState = input.machine.resolveState({
113
- ...input.state,
114
- context: input.state.context ?? {},
115
- });
116
-
117
- paths = getShortestPaths(input.machine, {
118
- fromState: resolvedState,
119
- toState: (state) => {
120
- const v = validateContext(state.context);
121
- return v;
122
- },
123
- });
124
- }
125
-
126
- if (!paths) {
127
- return undefined;
128
- }
129
-
130
- const trimmedPaths = paths
131
- .map((path) => {
132
- const trimmedSteps = trimSteps(path.steps, input.state);
133
- if (!trimmedSteps) {
134
- return undefined;
135
- }
136
- return {
137
- ...path,
138
- steps: trimmedSteps,
139
- };
140
- })
141
- .filter((p): p is NonNullable<typeof p> => p !== undefined);
142
-
143
- // Sort paths from least weight to most weight
144
- const sortedPaths = trimmedPaths.sort(
145
- (a, b) => costFunction(a) - costFunction(b)
146
- );
147
-
148
- const leastWeightPath = sortedPaths[0];
149
- const nextStep = leastWeightPath?.steps[0];
150
-
151
- return {
152
- planner: 'shortestPath',
153
- episodeId: agent.episodeId,
154
- goal: input.goal,
155
- goalState: paths[0]?.state,
156
- nextEvent: nextStep?.event,
157
- paths,
158
- timestamp: Date.now(),
159
- };
160
- }