@statelyai/agent 0.0.7 → 0.1.0

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 (50) hide show
  1. package/.vscode/launch.json +12 -1
  2. package/CHANGELOG.md +47 -0
  3. package/dist/index.d.mts +3 -0
  4. package/dist/index.d.ts +282 -71
  5. package/dist/index.js +4389 -183
  6. package/dist/index.mjs +7 -0
  7. package/examples/chatbot.ts +79 -0
  8. package/examples/cot.ts +91 -0
  9. package/examples/email.ts +118 -0
  10. package/examples/example.ts +81 -0
  11. package/examples/goal.ts +94 -0
  12. package/examples/joke.ts +117 -110
  13. package/examples/multi.ts +103 -0
  14. package/examples/newspaper.ts +324 -0
  15. package/examples/number.ts +102 -0
  16. package/examples/raffle.ts +105 -0
  17. package/examples/simple.ts +39 -0
  18. package/examples/support.ts +147 -0
  19. package/examples/ticTacToe.ts +89 -124
  20. package/examples/todo.ts +132 -0
  21. package/examples/tutor.ts +100 -0
  22. package/examples/verify.ts +120 -0
  23. package/examples/weather.ts +65 -47
  24. package/examples/wiki.ts +30 -0
  25. package/examples/word.ts +168 -0
  26. package/package.json +18 -11
  27. package/readme.md +9 -38
  28. package/src/adapters/vercel.ts +7 -0
  29. package/src/agent-experimental.ts +221 -0
  30. package/src/agent.test.ts +187 -0
  31. package/src/agent.ts +260 -6
  32. package/src/decision.test.ts +179 -0
  33. package/src/decision.ts +83 -0
  34. package/src/index.ts +3 -2
  35. package/src/memory.ts +25 -0
  36. package/src/planners/shortestPathPlanner.ts +22 -0
  37. package/src/planners/simplePlanner.ts +126 -0
  38. package/src/schemas.ts +13 -38
  39. package/src/strategies/chain-of-note.ts +155 -0
  40. package/src/templates/defaultText.ts +18 -0
  41. package/src/templates/defaultToolCall.ts +10 -0
  42. package/src/text.ts +232 -0
  43. package/src/types.ts +363 -46
  44. package/src/utils.ts +13 -50
  45. package/tsconfig.json +1 -1
  46. package/examples/multiAgentCollaboration.ts +0 -0
  47. package/examples/numberGuesser.ts +0 -128
  48. package/examples/wordGuesser.ts +0 -156
  49. package/src/adapter.test.ts +0 -217
  50. package/src/adapters/openai.ts +0 -298
package/src/schemas.ts CHANGED
@@ -1,40 +1,15 @@
1
- import { Values } from 'xstate';
2
- import {
3
- ContextSchema,
4
- EventSchemas,
5
- ConvertToJSONSchemas,
6
- createEventSchemas,
7
- } from './utils';
8
- import { FromSchema } from 'json-schema-to-ts';
1
+ import { SomeZodObject } from 'zod';
2
+ import { AnyEventObject } from 'xstate';
3
+ import { ObservedState } from './types';
9
4
 
10
- export function createSchemas<
11
- const TContextSchema extends ContextSchema,
12
- const TEventSchemas extends EventSchemas
13
- >({
14
- context,
15
- events,
16
- }: {
17
- /**
18
- * The JSON schema for the context object.
19
- *
20
- * Must be of `{ type: 'object' }`.
21
- */
22
- context?: TContextSchema;
23
- /**
24
- * An object mapping event types to each event object's JSON Schema.
25
- */
26
- events: TEventSchemas;
27
- }): {
28
- context: TContextSchema | undefined;
29
- events: ConvertToJSONSchemas<TEventSchemas>;
30
- types: {
31
- context: FromSchema<TContextSchema>;
32
- events: FromSchema<Values<ConvertToJSONSchemas<TEventSchemas>>>;
33
- };
34
- } {
35
- return {
36
- context,
37
- events: createEventSchemas(events),
38
- types: {} as any,
5
+ export type ZodEventMapping = {
6
+ // map event types to Zod types
7
+ [eventType: string]: SomeZodObject;
8
+ };
9
+
10
+ export type ZodActionMapping = {
11
+ [eventType: string]: {
12
+ schema: SomeZodObject;
13
+ action: (state: ObservedState, event: AnyEventObject) => Promise<void>;
39
14
  };
40
- }
15
+ };
@@ -0,0 +1,155 @@
1
+ import { GenerateTextResult, LanguageModel } from 'ai';
2
+ import wiki, { wikiSearchResult, wikiSummary } from 'wikipedia';
3
+ import { assign, fromPromise, setup } from 'xstate';
4
+ import { Agent } from '../types';
5
+
6
+ const searchWiki = fromPromise(
7
+ async ({
8
+ input,
9
+ }: {
10
+ input: {
11
+ query: string;
12
+ limit?: number;
13
+ };
14
+ }) => {
15
+ const passages = await wiki.search(input.query, {
16
+ limit: input.limit ?? 5,
17
+ });
18
+ return passages;
19
+ }
20
+ );
21
+
22
+ const extractSummaries = fromPromise(
23
+ async ({
24
+ input,
25
+ }: {
26
+ input: {
27
+ searchResult: wikiSearchResult;
28
+ };
29
+ }) => {
30
+ const summaries = await Promise.all(
31
+ input.searchResult.results.map(async (result) => {
32
+ const summary = await wiki.summary(result.title);
33
+ return {
34
+ title: result.title,
35
+ summary,
36
+ };
37
+ })
38
+ );
39
+ return summaries;
40
+ }
41
+ );
42
+
43
+ export const chainOfNote = setup({
44
+ types: {
45
+ input: {} as {
46
+ model: LanguageModel;
47
+ agent: Agent<any>;
48
+ prompt: string;
49
+ },
50
+ context: {} as {
51
+ searchResults: wikiSearchResult | null;
52
+ summaries:
53
+ | {
54
+ title: any;
55
+ summary: wikiSummary;
56
+ }[]
57
+ | null;
58
+ model: LanguageModel;
59
+ agent: Agent<any>;
60
+ prompt: string;
61
+ },
62
+ output: {} as GenerateTextResult<any>,
63
+ },
64
+ actors: {
65
+ searchWiki,
66
+ extractSummaries,
67
+ },
68
+ }).createMachine({
69
+ initial: 'searching',
70
+ context: (x) => ({
71
+ ...x.input,
72
+ searchResults: null,
73
+ summaries: null,
74
+ }),
75
+ states: {
76
+ searching: {
77
+ invoke: {
78
+ src: 'searchWiki',
79
+ input: (x) => ({
80
+ query: x.context.prompt,
81
+ }),
82
+ onDone: {
83
+ actions: assign({
84
+ searchResults: ({ event }) => event.output,
85
+ }),
86
+ target: 'extracting',
87
+ },
88
+ },
89
+ },
90
+ extracting: {
91
+ invoke: {
92
+ src: 'extractSummaries',
93
+ input: (x) => ({
94
+ searchResult: x.context.searchResults!,
95
+ }),
96
+ onDone: {
97
+ actions: assign({
98
+ summaries: ({ event }) => event.output,
99
+ }),
100
+ target: 'generating',
101
+ },
102
+ },
103
+ },
104
+ generating: {},
105
+ },
106
+ });
107
+
108
+ // export function chainOfNote() {
109
+ // return {
110
+ // generateText: async (x) => {
111
+ // const passages = await wiki.search(x.prompt!, {
112
+ // limit: 5,
113
+ // });
114
+
115
+ // const extracts = await Promise.all(
116
+ // passages.results.map(async (p) => {
117
+ // const summary = await wiki.summary(p.title);
118
+ // return summary.extract;
119
+ // })
120
+ // );
121
+ // x.agent?.addMessage({
122
+ // content: x.prompt!,
123
+ // id: Date.now() + '',
124
+ // role: 'user',
125
+ // timestamp: Date.now(),
126
+ // });
127
+ // const result = await generateText({
128
+ // model: x.model,
129
+ // system: `Task Description:
130
+
131
+ // 1. Read the given question and five Wikipedia passages to gather relevant information.
132
+
133
+ // 2. Write reading notes summarizing the key points from these passages.
134
+
135
+ // 3. Discuss the relevance of the given question and Wikipedia passages.
136
+
137
+ // 4. If some passages are relevant to the given question, provide a brief answer based on the passages.
138
+
139
+ // 5. If no passage is relevant, direcly provide answer without considering the passages.
140
+
141
+ // Passages: \n${extracts.join('\n')}`,
142
+ // prompt: `${x.prompt!}`,
143
+ // });
144
+
145
+ // x.agent?.addMessage({
146
+ // content: result.text,
147
+ // id: Date.now() + '',
148
+ // role: 'user',
149
+ // timestamp: Date.now(),
150
+ // });
151
+
152
+ // return result;
153
+ // },
154
+ // } satisfies AgentStrategy;
155
+ // }
@@ -0,0 +1,18 @@
1
+ import { PromptTemplate } from '../types';
2
+ import { wrapInXml } from '../utils';
3
+
4
+ export const defaultTextTemplate: PromptTemplate<any> = (data) => {
5
+ const preamble = [
6
+ data.context
7
+ ? wrapInXml('context', JSON.stringify(data.context))
8
+ : undefined,
9
+ ]
10
+ .filter(Boolean)
11
+ .join('\n');
12
+
13
+ return `
14
+ ${preamble}
15
+
16
+ ${data.goal}
17
+ `.trim();
18
+ };
@@ -0,0 +1,10 @@
1
+ import { PromptTemplate } from '../types';
2
+ import { defaultTextTemplate } from './defaultText';
3
+
4
+ export const defaultToolCallTemplate: PromptTemplate<any> = (data) => {
5
+ return `
6
+ ${defaultTextTemplate(data)}
7
+
8
+ Only make a single tool call to achieve the above goal.
9
+ `.trim();
10
+ };
package/src/text.ts ADDED
@@ -0,0 +1,232 @@
1
+ import type {
2
+ CoreMessage,
3
+ CoreTool,
4
+ GenerateTextResult,
5
+ StreamTextResult,
6
+ } from 'ai';
7
+ import {
8
+ Agent,
9
+ AgentGenerateTextOptions,
10
+ AgentStreamTextOptions,
11
+ } from './types';
12
+ import { randomUUID } from 'crypto';
13
+ import { defaultTextTemplate } from './templates/defaultText';
14
+ import {
15
+ AnyMachineSnapshot,
16
+ ObservableActorLogic,
17
+ Observer,
18
+ PromiseActorLogic,
19
+ fromObservable,
20
+ fromPromise,
21
+ toObserver,
22
+ } from 'xstate';
23
+ import { vercelAdapter } from './adapters/vercel';
24
+
25
+ /**
26
+ * Gets an array of messages from the given prompt, based on the agent and options.
27
+ *
28
+ * @param agent
29
+ * @param prompt
30
+ * @param options
31
+ * @returns
32
+ */
33
+ async function getMessages(
34
+ agent: Agent<any>,
35
+ prompt: string,
36
+ options: AgentStreamTextOptions
37
+ ): Promise<CoreMessage[]> {
38
+ let messages: CoreMessage[] = [];
39
+ if (options.messages === true) {
40
+ messages = agent.select((s) => s.messages);
41
+ } else if (typeof options.messages === 'function') {
42
+ messages = await options.messages(agent);
43
+ } else if (options.messages) {
44
+ messages = options.messages;
45
+ }
46
+
47
+ messages = messages.concat({
48
+ role: 'user',
49
+ content: prompt,
50
+ });
51
+
52
+ return messages;
53
+ }
54
+
55
+ export async function agentGenerateText<T extends Agent<any>>(
56
+ agent: T,
57
+ options: AgentGenerateTextOptions
58
+ ) {
59
+ const resolvedOptions = {
60
+ ...agent.defaultOptions,
61
+ ...options,
62
+ };
63
+ const template = resolvedOptions.template ?? defaultTextTemplate;
64
+ // TODO: check if messages was provided instead
65
+ const id = randomUUID();
66
+ const goal =
67
+ typeof resolvedOptions.prompt === 'string'
68
+ ? resolvedOptions.prompt
69
+ : await resolvedOptions.prompt(agent);
70
+
71
+ const promptWithContext = template({
72
+ goal,
73
+ context: resolvedOptions.context,
74
+ });
75
+
76
+ const messages = await getMessages(agent, promptWithContext, resolvedOptions);
77
+
78
+ agent.addMessage({
79
+ id,
80
+ role: 'user',
81
+ content: promptWithContext,
82
+ timestamp: Date.now(),
83
+ });
84
+
85
+ const result = await agent.adapter.generateText({
86
+ ...resolvedOptions,
87
+ prompt: undefined,
88
+ messages,
89
+ });
90
+
91
+ agent.addMessage({
92
+ content: result.text,
93
+ id,
94
+ role: 'assistant',
95
+ timestamp: Date.now(),
96
+ responseId: id,
97
+ result,
98
+ });
99
+
100
+ return result;
101
+ }
102
+
103
+ export async function agentStreamText(
104
+ agent: Agent<any>,
105
+ options: AgentStreamTextOptions
106
+ ): Promise<StreamTextResult<any>> {
107
+ const resolvedOptions = {
108
+ ...agent.defaultOptions,
109
+ ...options,
110
+ };
111
+ const template = resolvedOptions.template ?? defaultTextTemplate;
112
+
113
+ const id = randomUUID();
114
+ const goal =
115
+ typeof resolvedOptions.prompt === 'string'
116
+ ? resolvedOptions.prompt
117
+ : await resolvedOptions.prompt(agent);
118
+
119
+ const promptWithContext = template({
120
+ goal,
121
+ context: resolvedOptions.context,
122
+ });
123
+
124
+ const messages = await getMessages(agent, promptWithContext, resolvedOptions);
125
+
126
+ agent.addMessage({
127
+ role: 'user',
128
+ content: promptWithContext,
129
+ id,
130
+ timestamp: Date.now(),
131
+ });
132
+
133
+ const result = await agent.adapter.streamText({
134
+ ...resolvedOptions,
135
+ prompt: undefined,
136
+ messages,
137
+ onFinish: async (res) => {
138
+ agent.addMessage({
139
+ role: 'assistant',
140
+ result: {
141
+ text: res.text,
142
+ finishReason: res.finishReason,
143
+ logprobs: undefined,
144
+ responseMessages: [],
145
+ toolCalls: [],
146
+ toolResults: [],
147
+ usage: res.usage,
148
+ warnings: res.warnings,
149
+ rawResponse: res.rawResponse,
150
+ },
151
+ content: res.text,
152
+ id: randomUUID(),
153
+ timestamp: Date.now(),
154
+ responseId: id,
155
+ });
156
+ },
157
+ });
158
+
159
+ return result;
160
+ }
161
+
162
+ export function fromTextStream<T extends Agent<any>>(
163
+ agent: T,
164
+ defaultOptions?: AgentStreamTextOptions
165
+ ): ObservableActorLogic<
166
+ { textDelta: string },
167
+ Omit<AgentStreamTextOptions, 'context'> & {
168
+ context?: AgentStreamTextOptions['context'] | boolean;
169
+ }
170
+ > {
171
+ return fromObservable(({ input, self }) => {
172
+ const context =
173
+ input.context === true
174
+ ? (self._parent?.getSnapshot() as AnyMachineSnapshot).context
175
+ : input.context;
176
+
177
+ const observers = new Set<Observer<{ textDelta: string }>>();
178
+
179
+ // TODO: check if messages was provided instead
180
+
181
+ (async () => {
182
+ const result = await agentStreamText(agent, {
183
+ ...defaultOptions,
184
+ ...input,
185
+ context,
186
+ });
187
+
188
+ for await (const part of result.fullStream) {
189
+ if (part.type === 'text-delta') {
190
+ observers.forEach((observer) => {
191
+ observer.next?.(part);
192
+ });
193
+ }
194
+ }
195
+ })();
196
+
197
+ return {
198
+ subscribe: (...args: any[]) => {
199
+ const observer = toObserver(...args);
200
+ observers.add(observer);
201
+
202
+ return {
203
+ unsubscribe: () => {
204
+ observers.delete(observer);
205
+ },
206
+ };
207
+ },
208
+ };
209
+ });
210
+ }
211
+
212
+ export function fromText<T extends Agent<any>>(
213
+ agent: T,
214
+ defaultOptions?: AgentGenerateTextOptions
215
+ ): PromiseActorLogic<
216
+ GenerateTextResult<Record<string, CoreTool<any, any>>>,
217
+ Omit<AgentGenerateTextOptions, 'context'> & {
218
+ context?: AgentGenerateTextOptions['context'] | boolean;
219
+ }
220
+ > {
221
+ return fromPromise(async ({ input, self }) => {
222
+ const context =
223
+ input.context === true
224
+ ? (self._parent?.getSnapshot() as AnyMachineSnapshot).context
225
+ : input.context;
226
+ return await agentGenerateText(agent, {
227
+ ...input,
228
+ ...defaultOptions,
229
+ context,
230
+ });
231
+ });
232
+ }