@statelyai/agent 1.1.6 → 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 (62) hide show
  1. package/.changeset/cyan-carpets-perform.md +5 -0
  2. package/.changeset/fast-donkeys-argue.md +5 -0
  3. package/.changeset/light-hats-drive.md +9 -0
  4. package/.changeset/old-jobs-check.md +5 -0
  5. package/.changeset/pre.json +13 -0
  6. package/.vscode/launch.json +6 -0
  7. package/CHANGELOG.md +22 -0
  8. package/dist/index.d.mts +262 -171
  9. package/dist/index.d.ts +262 -171
  10. package/dist/index.js +383 -274
  11. package/dist/index.mjs +386 -272
  12. package/examples/chatbot-alt.ts +57 -0
  13. package/examples/chatbot.ts +12 -17
  14. package/examples/cot.ts +26 -23
  15. package/examples/customer-service-sim.ts +107 -0
  16. package/examples/email.ts +37 -41
  17. package/examples/example.ts +6 -6
  18. package/examples/executor.ts +66 -0
  19. package/examples/goal.ts +12 -12
  20. package/examples/helpers/helpers.ts +26 -14
  21. package/examples/joke.ts +79 -76
  22. package/examples/jugs.ts +125 -0
  23. package/examples/multi.ts +5 -5
  24. package/examples/newspaper.ts +98 -104
  25. package/examples/number.ts +6 -5
  26. package/examples/raffle.ts +11 -12
  27. package/examples/river-crossing.ts +140 -0
  28. package/examples/sandbox.ts +1 -1
  29. package/examples/simple.ts +5 -3
  30. package/examples/summary.ts +121 -0
  31. package/examples/support.ts +6 -6
  32. package/examples/ticTacToe.ts +86 -45
  33. package/examples/todo.ts +7 -7
  34. package/examples/tutor.ts +15 -15
  35. package/examples/verify.ts +3 -3
  36. package/examples/weather.ts +6 -9
  37. package/examples/wiki.ts +27 -8
  38. package/examples/word.ts +16 -11
  39. package/package.json +16 -11
  40. package/readme.md +1 -1
  41. package/src/agent-experimental.ts +1 -1
  42. package/src/agent.test.ts +243 -214
  43. package/src/agent.ts +286 -95
  44. package/src/decide.test.ts +276 -0
  45. package/src/decide.ts +163 -0
  46. package/src/index.ts +1 -1
  47. package/src/middleware.ts +91 -0
  48. package/src/mockModel.ts +47 -0
  49. package/src/planners/shortestPath.test.ts +94 -0
  50. package/src/planners/shortestPath.ts +177 -0
  51. package/src/planners/simple.ts +105 -0
  52. package/src/strategies/chain-of-note.ts +6 -55
  53. package/src/text.ts +51 -144
  54. package/src/types.ts +187 -212
  55. package/src/utils.ts +48 -4
  56. package/vitest.config.ts +9 -3
  57. package/src/adapters/vercel.ts +0 -7
  58. package/src/decision.test.ts +0 -179
  59. package/src/decision.ts +0 -84
  60. package/src/memory.ts +0 -25
  61. package/src/planners/shortestPathPlanner.ts +0 -22
  62. package/src/planners/simplePlanner.ts +0 -139
@@ -1,179 +0,0 @@
1
- import { test, expect } from 'vitest';
2
- import { createAgent, fromDecision, type AIAdapter } from './';
3
- import { createActor, createMachine, waitFor } from 'xstate';
4
- import { z } from 'zod';
5
- import { GenerateTextResult } from 'ai';
6
-
7
- const mockToolDecision: AIAdapter['generateText'] = async (arg) => {
8
- const keys = Object.keys(arg.tools!);
9
-
10
- if (keys.length > 1) {
11
- throw new Error('Expected only 1 choice');
12
- }
13
-
14
- if (keys.length === 0) {
15
- return {
16
- toolResults: [],
17
- } as any as GenerateTextResult<any>;
18
- }
19
-
20
- return {
21
- toolResults: [
22
- {
23
- result: {
24
- type: keys[0],
25
- },
26
- },
27
- ],
28
- } as any as GenerateTextResult<any>;
29
- };
30
-
31
- test('fromDecision() makes a decision', async () => {
32
- const agent = createAgent({
33
- name: 'test',
34
- model: {} as any,
35
- events: {
36
- doFirst: z.object({}),
37
- doSecond: z.object({}),
38
- },
39
- adapter: {
40
- generateText: async (arg) => {
41
- const keys = Object.keys(arg.tools!);
42
-
43
- if (keys.length !== 1) {
44
- throw new Error('Expected only 1 choice');
45
- }
46
-
47
- return {
48
- toolResults: [
49
- {
50
- result: {
51
- type: keys[0],
52
- },
53
- },
54
- ],
55
- } as any as GenerateTextResult<any>;
56
- },
57
- streamText: {} as any,
58
- },
59
- });
60
-
61
- const machine = createMachine({
62
- initial: 'first',
63
- states: {
64
- first: {
65
- invoke: {
66
- src: fromDecision(agent),
67
- },
68
- on: {
69
- doFirst: 'second',
70
- },
71
- },
72
- second: {
73
- invoke: {
74
- src: fromDecision(agent),
75
- },
76
- on: {
77
- doSecond: 'third',
78
- },
79
- },
80
- third: {},
81
- },
82
- });
83
-
84
- const actor = createActor(machine);
85
-
86
- actor.start();
87
-
88
- await waitFor(actor, (s) => s.matches('third'));
89
-
90
- expect(actor.getSnapshot().value).toBe('third');
91
- });
92
-
93
- test('interacts with an actor', async () => {
94
- const agent = createAgent({
95
- name: 'test',
96
- model: {} as any,
97
- events: {
98
- doFirst: z.object({}),
99
- doSecond: z.object({}),
100
- },
101
- adapter: {
102
- generateText: mockToolDecision,
103
- streamText: {} as any,
104
- },
105
- });
106
-
107
- const machine = createMachine({
108
- initial: 'first',
109
- states: {
110
- first: {
111
- on: {
112
- doFirst: 'second',
113
- },
114
- },
115
- second: {
116
- on: {
117
- doSecond: 'third',
118
- },
119
- },
120
- third: {},
121
- },
122
- });
123
-
124
- const actor = createActor(machine);
125
-
126
- agent.interact(actor, () => ({
127
- goal: 'Some goal',
128
- }));
129
-
130
- actor.start();
131
-
132
- await waitFor(actor, (s) => s.matches('third'));
133
-
134
- expect(actor.getSnapshot().value).toBe('third');
135
- });
136
-
137
- test('interacts with an actor (late interaction)', async () => {
138
- const agent = createAgent({
139
- name: 'test',
140
- model: {} as any,
141
- events: {
142
- doFirst: z.object({}),
143
- doSecond: z.object({}),
144
- },
145
- adapter: {
146
- generateText: mockToolDecision,
147
- streamText: {} as any,
148
- },
149
- });
150
-
151
- const machine = createMachine({
152
- initial: 'first',
153
- states: {
154
- first: {
155
- on: {
156
- doFirst: 'second',
157
- },
158
- },
159
- second: {
160
- on: {
161
- doSecond: 'third',
162
- },
163
- },
164
- third: {},
165
- },
166
- });
167
-
168
- const actor = createActor(machine);
169
-
170
- actor.start();
171
-
172
- agent.interact(actor, () => ({
173
- goal: 'Some goal',
174
- }));
175
-
176
- await waitFor(actor, (s) => s.matches('third'));
177
-
178
- expect(actor.getSnapshot().value).toBe('third');
179
- });
package/src/decision.ts DELETED
@@ -1,84 +0,0 @@
1
- import { AnyActor, AnyMachineSnapshot, fromPromise } from 'xstate';
2
- import {
3
- AnyAgent,
4
- AgentDecideOptions,
5
- AgentDecisionLogic,
6
- AgentDecisionInput,
7
- AgentPlanner,
8
- AgentPlan,
9
- } from './types';
10
- import { simplePlanner } from './planners/simplePlanner';
11
-
12
- export async function agentDecide<T extends AnyAgent>(
13
- agent: T,
14
- options: AgentDecideOptions
15
- ): Promise<AgentPlan<any> | undefined> {
16
- const resolvedOptions = {
17
- ...agent.defaultOptions,
18
- ...options,
19
- };
20
- const {
21
- planner = simplePlanner as AgentPlanner<any>,
22
- goal,
23
- events = agent.events,
24
- state,
25
- machine,
26
- model = agent.model,
27
- ...otherPlanInput
28
- } = resolvedOptions;
29
-
30
- const plan = await planner(agent, {
31
- model,
32
- goal,
33
- events,
34
- state,
35
- machine,
36
- ...otherPlanInput,
37
- });
38
-
39
- if (plan?.nextEvent) {
40
- agent.addPlan(plan);
41
- await resolvedOptions.execute?.(plan.nextEvent);
42
- }
43
-
44
- return plan;
45
- }
46
-
47
- export function fromDecision(
48
- agent: AnyAgent,
49
- defaultInput?: AgentDecisionInput
50
- ): AgentDecisionLogic<any> {
51
- return fromPromise(async ({ input, self }) => {
52
- const parentRef = self._parent;
53
- if (!parentRef) {
54
- return;
55
- }
56
-
57
- const snapshot = parentRef.getSnapshot() as AnyMachineSnapshot;
58
- const inputObject = typeof input === 'string' ? { goal: input } : input;
59
- const resolvedInput = {
60
- ...defaultInput,
61
- ...inputObject,
62
- };
63
- const contextToInclude =
64
- resolvedInput.context === true
65
- ? // include entire context
66
- parentRef.getSnapshot().context
67
- : resolvedInput.context;
68
- const state = {
69
- value: snapshot.value,
70
- context: contextToInclude,
71
- };
72
-
73
- const plan = await agentDecide(agent, {
74
- machine: (parentRef as AnyActor).logic,
75
- state,
76
- execute: async (event) => {
77
- parentRef.send(event);
78
- },
79
- ...resolvedInput,
80
- });
81
-
82
- return plan;
83
- }) as AgentDecisionLogic<any>;
84
- }
package/src/memory.ts DELETED
@@ -1,25 +0,0 @@
1
- import { AgentMemory, AgentMemoryContext } from './types';
2
-
3
- export function createAgentMemory(): AgentMemory {
4
- const storage = {
5
- sessions: {} as Record<string, AgentMemoryContext>,
6
- };
7
-
8
- return {
9
- append: async (sessionId, key, item) => {
10
- storage.sessions[sessionId] =
11
- storage.sessions[sessionId] ||
12
- ({
13
- observations: [],
14
- messages: [],
15
- plans: [],
16
- feedback: [],
17
- } satisfies AgentMemoryContext);
18
-
19
- storage.sessions[sessionId]![key].push(item as any);
20
- },
21
- getAll: async (sessionId, key) => {
22
- return storage.sessions[sessionId]?.[key];
23
- },
24
- };
25
- }
@@ -1,22 +0,0 @@
1
- import { Agent, AgentPlan, AgentPlanInput, AnyAgent } from '../types';
2
- import { getShortestPaths } from '@xstate/graph';
3
-
4
- export async function simplePlanner<T extends AnyAgent>(
5
- agent: T,
6
- input: AgentPlanInput<any>
7
- ): Promise<AgentPlan<any> | undefined> {
8
- // 1. Determine goal state criteria
9
- // e.g. a state where the agent has won a game
10
- void 0;
11
-
12
- // 2. Determine possible events that can occur
13
- void 0;
14
-
15
- // 3. Get shortest paths from current state to
16
- // a state matching the criteria, using
17
- // possible events
18
- void 0;
19
-
20
- // 4. Return shortest path as a plan
21
- return null as any;
22
- }
@@ -1,139 +0,0 @@
1
- import { type CoreTool, tool } from 'ai';
2
- import {
3
- AgentPlan,
4
- AgentPlanInput,
5
- ObservedState,
6
- PromptTemplate,
7
- TransitionData,
8
- AnyAgent,
9
- } from '../types';
10
- import { getAllTransitions } from '../utils';
11
- import { AnyStateMachine } from 'xstate';
12
- import { defaultTextTemplate } from '../templates/defaultText';
13
- import { getMessages } from '../text';
14
-
15
- function getTransitions(
16
- state: ObservedState,
17
- machine: AnyStateMachine
18
- ): TransitionData[] {
19
- if (!machine) {
20
- return [];
21
- }
22
-
23
- const resolvedState = machine.resolveState(state);
24
- return getAllTransitions(resolvedState);
25
- }
26
-
27
- const simplePlannerPromptTemplate: PromptTemplate<any> = (data) => {
28
- return `
29
- ${defaultTextTemplate(data)}
30
-
31
- 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.
32
- `.trim();
33
- };
34
-
35
- export async function simplePlanner<T extends AnyAgent>(
36
- agent: T,
37
- input: AgentPlanInput<any>
38
- ): Promise<AgentPlan<any> | undefined> {
39
- // Get all of the possible next transitions
40
- const transitions: TransitionData[] = input.machine
41
- ? getTransitions(input.state, input.machine)
42
- : Object.entries(input.events).map(([eventType, { description }]) => ({
43
- eventType,
44
- description,
45
- }));
46
-
47
- // Only keep the transitions that match the event types that are in the event mapping
48
- // TODO: allow for custom filters
49
- const filter = (eventType: string) =>
50
- Object.keys(input.events).includes(eventType);
51
-
52
- // Mapping of each event type (e.g. "mouse.click")
53
- // to a valid function name (e.g. "mouse_click")
54
- const functionNameMapping: Record<string, string> = {};
55
-
56
- const toolTransitions = transitions
57
- .filter((t) => {
58
- return filter(t.eventType);
59
- })
60
- .map((t) => {
61
- const name = t.eventType.replace(/\./g, '_');
62
- functionNameMapping[name] = t.eventType;
63
-
64
- return {
65
- type: 'function',
66
- eventType: t.eventType,
67
- description: t.description,
68
- name,
69
- } as const;
70
- });
71
-
72
- // Convert the transition data to a tool map that the
73
- // Vercel AI SDK can use
74
- const toolMap: Record<string, CoreTool<any, any>> = {};
75
- for (const toolTransitionData of toolTransitions) {
76
- const toolZodType = input.events?.[toolTransitionData.eventType];
77
-
78
- if (!toolZodType) {
79
- continue;
80
- }
81
-
82
- toolMap[toolTransitionData.name] = tool({
83
- description: toolZodType?.description ?? toolTransitionData.description,
84
- parameters: toolZodType,
85
- execute: async (params: Record<string, any>) => {
86
- const event = {
87
- type: toolTransitionData.eventType,
88
- ...params,
89
- };
90
-
91
- return event;
92
- },
93
- });
94
- }
95
-
96
- if (!Object.keys(toolMap).length) {
97
- // No valid transitions for the specified tools
98
- return undefined;
99
- }
100
-
101
- // Create a prompt with the given context and goal.
102
- // The template is used to ensure that a single tool call at most is made.
103
- const prompt = simplePlannerPromptTemplate({
104
- context: input.state.context,
105
- goal: input.goal,
106
- });
107
-
108
- const messages = await getMessages(agent, prompt, input);
109
-
110
- const result = await agent.generateText({
111
- toolChoice: 'required',
112
- ...input,
113
- prompt,
114
- messages,
115
- tools: toolMap,
116
- });
117
-
118
- const singleResult = result.toolResults[0];
119
-
120
- if (!singleResult) {
121
- // TODO: retries?
122
- console.warn('No tool call results returned');
123
- return undefined;
124
- }
125
-
126
- return {
127
- goal: input.goal,
128
- state: input.state,
129
- execute: async (state) => {
130
- if (JSON.stringify(state) === JSON.stringify(input.state)) {
131
- return singleResult.result;
132
- }
133
- return undefined;
134
- },
135
- nextEvent: singleResult.result,
136
- sessionId: agent.sessionId,
137
- timestamp: Date.now(),
138
- };
139
- }