@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.
Files changed (37) hide show
  1. package/.changeset/calm-beans-talk.md +5 -0
  2. package/.changeset/long-guests-explode.md +5 -0
  3. package/.changeset/nice-pants-rule.md +10 -0
  4. package/.changeset/odd-kiwis-compare.md +5 -0
  5. package/.changeset/pre.json +4 -0
  6. package/CHANGELOG.md +23 -0
  7. package/architecture.tldr +797 -0
  8. package/dist/index.d.mts +198 -140
  9. package/dist/index.d.ts +198 -140
  10. package/dist/index.js +4397 -148
  11. package/dist/index.mjs +4397 -149
  12. package/examples/chatbot.ts +9 -5
  13. package/examples/cot.ts +2 -4
  14. package/examples/jugs.ts +2 -2
  15. package/examples/learn-from-feedback.ts +7 -7
  16. package/examples/newspaper.ts +1 -1
  17. package/examples/rewoo.ts +62 -0
  18. package/examples/river-crossing.ts +2 -2
  19. package/examples/serverless.ts +71 -0
  20. package/examples/simple.ts +1 -1
  21. package/examples/ticTacToe.ts +6 -2
  22. package/examples/wiki.ts +2 -2
  23. package/package.json +14 -12
  24. package/readme.md +57 -0
  25. package/src/agent.test.ts +387 -30
  26. package/src/agent.ts +177 -64
  27. package/src/decide.test.ts +24 -2
  28. package/src/decide.ts +34 -78
  29. package/src/index.ts +1 -0
  30. package/src/{strategies/chainOfThought.ts → policies/chainOfThoughtPolicy.ts} +7 -9
  31. package/src/policies/index.ts +3 -0
  32. package/src/{strategies/shortestPath.test.ts → policies/shortestPathPolicy.test.ts} +2 -2
  33. package/src/{strategies/shortestPath.ts → policies/shortestPathPolicy.ts} +8 -8
  34. package/src/{strategies/simple.ts → policies/toolPolicy.ts} +27 -26
  35. package/src/text.ts +17 -22
  36. package/src/types.ts +162 -166
  37. package/src/agent-experimental.ts +0 -221
@@ -5,8 +5,8 @@ import {
5
5
  AgentDecision,
6
6
  PromptTemplate,
7
7
  } from '../types';
8
- import { getMessages } from '../text';
9
- import { simpleStrategy } from './simple';
8
+ import { combinePromptAndMessages } from '../text';
9
+ import { toolPolicy } from './toolPolicy';
10
10
  import { convertToXml } from '../utils';
11
11
 
12
12
  const chainOfThoughtPromptTemplate: PromptTemplate<any> = ({
@@ -14,14 +14,12 @@ const chainOfThoughtPromptTemplate: PromptTemplate<any> = ({
14
14
  context,
15
15
  goal,
16
16
  }) => {
17
- return `
18
- ${convertToXml({ stateValue, context, goal })}
17
+ return `${convertToXml({ stateValue, context, goal })}
19
18
 
20
- How would you achieve the goal? Think step-by-step.
21
- `.trim();
19
+ How would you achieve the goal? Think step-by-step.`;
22
20
  };
23
21
 
24
- export async function chainOfThoughtStrategy<T extends AnyAgent>(
22
+ export async function chainOfThoughtPolicy<T extends AnyAgent>(
25
23
  agent: T,
26
24
  input: AgentDecideInput<any>
27
25
  ): Promise<AgentDecision<any> | undefined> {
@@ -31,7 +29,7 @@ export async function chainOfThoughtStrategy<T extends AnyAgent>(
31
29
  goal: input.goal,
32
30
  });
33
31
 
34
- const messages = await getMessages(agent, prompt, input);
32
+ const messages = combinePromptAndMessages(prompt, input.messages);
35
33
 
36
34
  const model = input.model ? agent.wrap(input.model) : agent.model;
37
35
 
@@ -41,7 +39,7 @@ export async function chainOfThoughtStrategy<T extends AnyAgent>(
41
39
  messages,
42
40
  });
43
41
 
44
- const decision = await simpleStrategy(agent, {
42
+ const decision = await toolPolicy(agent, {
45
43
  ...input,
46
44
  messages: messages.concat(result.response.messages),
47
45
  });
@@ -0,0 +1,3 @@
1
+ export * from './chainOfThoughtPolicy';
2
+ export * from './shortestPathPolicy';
3
+ export * from './toolPolicy';
@@ -1,7 +1,7 @@
1
1
  import { createAgent, TypesFromAgent } from '..';
2
2
  import { assign, createActor, setup } from 'xstate';
3
3
  import { z } from 'zod';
4
- import { experimental_shortestPathStrategy } from './shortestPath';
4
+ import { experimental_shortestPathPolicy } from './shortestPathPolicy';
5
5
  import { test, expect } from 'vitest';
6
6
  import { dummyResponseValues, MockLanguageModelV1 } from '../mockModel';
7
7
 
@@ -84,7 +84,7 @@ test.skip('should find shortest path to goal', async () => {
84
84
  }),
85
85
  goal: 'Get the counter to exactly 3',
86
86
  state: counterActor.getSnapshot(),
87
- strategy: experimental_shortestPathStrategy,
87
+ policy: experimental_shortestPathPolicy,
88
88
  });
89
89
 
90
90
  expect(decision?.nextEvent?.type).toBe('increment');
@@ -2,7 +2,6 @@ import { generateObject } from 'ai';
2
2
  import {
3
3
  AgentDecision,
4
4
  AgentDecideInput,
5
- AgentStrategy,
6
5
  AgentStep,
7
6
  AnyAgent,
8
7
  CostFunction,
@@ -40,15 +39,15 @@ function trimSteps(steps: AgentStep<any>[], currentState: ObservedState<any>) {
40
39
  return steps.slice(index + 1, steps.length);
41
40
  }
42
41
 
43
- export async function experimental_shortestPathStrategy<T extends AnyAgent>(
42
+ export async function experimental_shortestPathPolicy<T extends AnyAgent>(
44
43
  agent: T,
45
44
  input: AgentDecideInput<any>
46
45
  ): Promise<AgentDecision<any> | undefined> {
47
46
  const costFunction: CostFunction<any> =
48
47
  input.costFunction ?? ((path) => path.weight ?? Infinity);
49
- const existingDecision = agent
50
- .getDecisions()
51
- .find((p) => p.strategy === 'shortestPath' && p.goal === input.goal);
48
+ const existingDecision = input.decisions?.find(
49
+ (p) => p.policy === 'shortestPath' && p.goal === input.goal
50
+ );
52
51
 
53
52
  let paths = existingDecision?.paths;
54
53
 
@@ -167,11 +166,12 @@ Examples:
167
166
 
168
167
  return {
169
168
  id: randomId(),
170
- strategy: 'shortestPath',
169
+ decisionId: input.decisionId ?? null,
170
+ policy: 'shortestPath',
171
171
  episodeId: agent.episodeId,
172
172
  goal: input.goal,
173
- goalState: paths[0]?.state,
174
- nextEvent: nextStep?.event,
173
+ goalState: paths[0]?.state ?? null,
174
+ nextEvent: nextStep?.event ?? null,
175
175
  paths,
176
176
  timestamp: Date.now(),
177
177
  };
@@ -1,4 +1,4 @@
1
- import { CoreMessage, generateText } from 'ai';
1
+ import { CoreToolResult, generateText } from 'ai';
2
2
  import {
3
3
  AgentDecision,
4
4
  AgentDecideInput,
@@ -6,11 +6,11 @@ import {
6
6
  AnyAgent,
7
7
  } from '../types';
8
8
  import { convertToXml, randomId } from '../utils';
9
- import { getNextSnapshot } from 'xstate';
10
- import { getMessages } from '../text';
9
+ import { transition } from 'xstate';
10
+ import { combinePromptAndMessages } from '../text';
11
11
  import { getToolMap } from '../decide';
12
12
 
13
- const simpleStrategyPromptTemplate: PromptTemplate<any> = (data) => {
13
+ const toolPolicyPromptTemplate: PromptTemplate<any> = (data) => {
14
14
  return `
15
15
  ${convertToXml(data)}
16
16
 
@@ -18,10 +18,10 @@ Make at most one tool call to achieve the above goal. If the goal cannot be achi
18
18
  `.trim();
19
19
  };
20
20
 
21
- export async function simpleStrategy<T extends AnyAgent>(
22
- agent: T,
23
- input: AgentDecideInput<any>
24
- ): Promise<AgentDecision<any> | undefined> {
21
+ export async function toolPolicy<TAgent extends AnyAgent>(
22
+ agent: TAgent,
23
+ input: AgentDecideInput<TAgent>
24
+ ): Promise<AgentDecision<TAgent> | undefined> {
25
25
  const toolMap = getToolMap(agent, input);
26
26
 
27
27
  if (!toolMap) {
@@ -31,13 +31,13 @@ export async function simpleStrategy<T extends AnyAgent>(
31
31
 
32
32
  // Create a prompt with the given context and goal.
33
33
  // The template is used to ensure that a single tool call at most is made.
34
- const prompt = simpleStrategyPromptTemplate({
34
+ const prompt = toolPolicyPromptTemplate({
35
35
  stateValue: input.state.value,
36
36
  context: input.context ?? input.state.context,
37
37
  goal: input.goal,
38
38
  });
39
39
 
40
- const messages = await getMessages(agent, prompt, input);
40
+ const messages = combinePromptAndMessages(prompt, input.messages);
41
41
 
42
42
  const model = input.model ? agent.wrap(input.model) : agent.model;
43
43
 
@@ -56,21 +56,19 @@ export async function simpleStrategy<T extends AnyAgent>(
56
56
  system: input.system ?? agent.description,
57
57
  model,
58
58
  messages,
59
- tools: toolMap as any,
59
+ tools: toolMap,
60
60
  toolChoice: input.toolChoice ?? 'required',
61
61
  });
62
62
 
63
63
  result.response.messages.forEach((m) => {
64
- const message: CoreMessage = m;
65
-
66
- agent.addMessage({
67
- ...message,
68
- id: randomId(),
69
- timestamp: Date.now(),
70
- });
64
+ agent.addMessage(m);
71
65
  });
72
66
 
73
- const singleResult = result.toolResults[0];
67
+ const singleResult = result.toolResults[0] as unknown as CoreToolResult<
68
+ any,
69
+ any,
70
+ any
71
+ >;
74
72
 
75
73
  if (!singleResult) {
76
74
  // TODO: retries?
@@ -78,24 +76,27 @@ export async function simpleStrategy<T extends AnyAgent>(
78
76
  return undefined;
79
77
  }
80
78
 
79
+ const nextEvent = singleResult.result;
80
+
81
81
  return {
82
82
  id: randomId(),
83
- strategy: 'simple',
83
+ decisionId: input.decisionId ?? null,
84
+ policy: 'simple',
84
85
  goal: input.goal,
85
86
  goalState: input.state,
86
- nextEvent: singleResult.result,
87
- episodeId: agent.episodeId,
87
+ nextEvent,
88
+ episodeId: input.episodeId ?? agent.episodeId,
88
89
  timestamp: Date.now(),
89
90
  paths: [
90
91
  {
91
- state: undefined,
92
+ state: null,
92
93
  steps: [
93
94
  {
94
- event: singleResult.result,
95
+ event: nextEvent,
95
96
  state:
96
97
  machine && machineState
97
- ? getNextSnapshot(machine, machineState, singleResult.result)
98
- : undefined,
98
+ ? transition(machine, machineState, nextEvent)[0]
99
+ : null,
99
100
  },
100
101
  ],
101
102
  },
package/src/text.ts CHANGED
@@ -28,32 +28,22 @@ import {
28
28
  * @param options
29
29
  * @returns
30
30
  */
31
- export async function getMessages<TAgent extends AnyAgent>(
32
- agent: TAgent,
31
+ export function combinePromptAndMessages(
33
32
  prompt: string,
34
- options: Omit<AgentGenerateTextOptions<TAgent>, 'prompt'>
35
- ): Promise<CoreMessage[]> {
36
- let messages: CoreMessage[] = [];
37
- if (typeof options.messages === 'function') {
38
- messages = await options.messages(agent);
39
- } else if (options.messages) {
40
- messages = options.messages;
41
- }
42
-
43
- messages = messages.concat({
33
+ messages?: CoreMessage[]
34
+ ): CoreMessage[] {
35
+ return (messages ?? []).concat({
44
36
  role: 'user',
45
37
  content: prompt,
46
38
  });
47
-
48
- return messages;
49
39
  }
50
40
 
51
41
  export function fromTextStream<TAgent extends AnyAgent>(
52
42
  agent: TAgent,
53
- options?: AgentStreamTextOptions<TAgent>
43
+ options?: AgentStreamTextOptions
54
44
  ): ObservableActorLogic<
55
45
  { textDelta: string },
56
- Omit<AgentStreamTextOptions<TAgent>, 'context'> & {
46
+ Omit<AgentStreamTextOptions, 'context'> & {
57
47
  context?: Record<string, any>;
58
48
  }
59
49
  > {
@@ -73,7 +63,10 @@ export function fromTextStream<TAgent extends AnyAgent>(
73
63
  goal,
74
64
  context: input.context,
75
65
  });
76
- const messages = await getMessages(agent, promptWithContext, input);
66
+ const messages = combinePromptAndMessages(
67
+ promptWithContext,
68
+ input.messages
69
+ );
77
70
  const result = await streamText({
78
71
  ...options,
79
72
  ...input,
@@ -108,15 +101,14 @@ export function fromTextStream<TAgent extends AnyAgent>(
108
101
 
109
102
  export function fromText<TAgent extends AnyAgent>(
110
103
  agent: TAgent,
111
- options?: AgentGenerateTextOptions<TAgent>
104
+ options?: AgentGenerateTextOptions
112
105
  ): PromiseActorLogic<
113
- GenerateTextResult<Record<string, CoreTool<any, any>>>,
114
- Omit<AgentGenerateTextOptions<TAgent>, 'context'> & {
106
+ GenerateTextResult<Record<string, CoreTool<any, any>>, any>,
107
+ Omit<AgentGenerateTextOptions, 'context'> & {
115
108
  context?: Record<string, any>;
116
109
  }
117
110
  > {
118
111
  const resolvedOptions = {
119
- ...agent.defaultOptions,
120
112
  ...options,
121
113
  };
122
114
 
@@ -133,7 +125,10 @@ export function fromText<TAgent extends AnyAgent>(
133
125
  context: input.context,
134
126
  });
135
127
 
136
- const messages = await getMessages(agent, promptWithContext, input);
128
+ const messages = combinePromptAndMessages(
129
+ promptWithContext,
130
+ input.messages
131
+ );
137
132
 
138
133
  const model = input.model ? agent.wrap(input.model) : agent.model;
139
134