@statelyai/agent 0.0.8 → 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.
- package/.vscode/launch.json +12 -1
- package/CHANGELOG.md +18 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +286 -44
- package/dist/index.js +695 -1225
- package/dist/index.mjs +7 -0
- package/examples/chatbot.ts +79 -0
- package/examples/cot.ts +91 -0
- package/examples/email.ts +118 -0
- package/examples/example.ts +81 -0
- package/examples/goal.ts +94 -0
- package/examples/joke.ts +98 -84
- package/examples/multi.ts +103 -0
- package/examples/newspaper.ts +324 -0
- package/examples/number.ts +102 -0
- package/examples/raffle.ts +105 -0
- package/examples/simple.ts +39 -0
- package/examples/support.ts +147 -0
- package/examples/ticTacToe.ts +77 -77
- package/examples/todo.ts +132 -0
- package/examples/tutor.ts +100 -0
- package/examples/verify.ts +120 -0
- package/examples/weather.ts +42 -45
- package/examples/wiki.ts +30 -0
- package/examples/word.ts +168 -0
- package/package.json +17 -12
- package/readme.md +9 -38
- package/src/adapters/vercel.ts +7 -0
- package/src/agent-experimental.ts +221 -0
- package/src/agent.test.ts +187 -0
- package/src/agent.ts +260 -6
- package/src/decision.test.ts +179 -0
- package/src/decision.ts +83 -0
- package/src/index.ts +3 -2
- package/src/memory.ts +25 -0
- package/src/planners/shortestPathPlanner.ts +22 -0
- package/src/planners/simplePlanner.ts +126 -0
- package/src/schemas.ts +9 -20
- package/src/strategies/chain-of-note.ts +155 -0
- package/src/templates/defaultText.ts +18 -0
- package/src/templates/defaultToolCall.ts +10 -0
- package/src/text.ts +232 -0
- package/src/types.ts +363 -46
- package/src/utils.ts +13 -72
- package/tsconfig.json +1 -1
- package/examples/multiAgentCollaboration.ts +0 -0
- package/examples/numberGuesser.ts +0 -101
- package/examples/wordGuesser.ts +0 -144
- package/src/adapter.test.ts +0 -217
- package/src/adapters/openai.ts +0 -303
package/src/schemas.ts
CHANGED
|
@@ -1,26 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { SomeZodObject } from 'zod';
|
|
2
|
+
import { AnyEventObject } from 'xstate';
|
|
3
|
+
import { ObservedState } from './types';
|
|
4
4
|
|
|
5
|
-
export type
|
|
5
|
+
export type ZodEventMapping = {
|
|
6
6
|
// map event types to Zod types
|
|
7
7
|
[eventType: string]: SomeZodObject;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
[K in keyof TEventSchemas]: {
|
|
15
|
-
type: K;
|
|
16
|
-
} & TypeOf<TEventSchemas[K]>;
|
|
17
|
-
}>;
|
|
18
|
-
schemas: {
|
|
19
|
-
[K in keyof TEventSchemas]: unknown;
|
|
10
|
+
export type ZodActionMapping = {
|
|
11
|
+
[eventType: string]: {
|
|
12
|
+
schema: SomeZodObject;
|
|
13
|
+
action: (state: ObservedState, event: AnyEventObject) => Promise<void>;
|
|
20
14
|
};
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
types: {} as any,
|
|
24
|
-
schemas: createZodEventSchemas(events),
|
|
25
|
-
};
|
|
26
|
-
}
|
|
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
|
+
}
|