@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.
- package/.changeset/cyan-carpets-perform.md +5 -0
- package/.changeset/fast-donkeys-argue.md +5 -0
- package/.changeset/light-hats-drive.md +9 -0
- package/.changeset/old-jobs-check.md +5 -0
- package/.changeset/pre.json +13 -0
- package/.vscode/launch.json +6 -0
- package/CHANGELOG.md +22 -0
- package/dist/index.d.mts +262 -171
- package/dist/index.d.ts +262 -171
- package/dist/index.js +383 -274
- package/dist/index.mjs +386 -272
- package/examples/chatbot-alt.ts +57 -0
- package/examples/chatbot.ts +12 -17
- package/examples/cot.ts +26 -23
- package/examples/customer-service-sim.ts +107 -0
- package/examples/email.ts +37 -41
- package/examples/example.ts +6 -6
- package/examples/executor.ts +66 -0
- package/examples/goal.ts +12 -12
- package/examples/helpers/helpers.ts +26 -14
- package/examples/joke.ts +79 -76
- package/examples/jugs.ts +125 -0
- package/examples/multi.ts +5 -5
- package/examples/newspaper.ts +98 -104
- package/examples/number.ts +6 -5
- package/examples/raffle.ts +11 -12
- package/examples/river-crossing.ts +140 -0
- package/examples/sandbox.ts +1 -1
- package/examples/simple.ts +5 -3
- package/examples/summary.ts +121 -0
- package/examples/support.ts +6 -6
- package/examples/ticTacToe.ts +86 -45
- package/examples/todo.ts +7 -7
- package/examples/tutor.ts +15 -15
- package/examples/verify.ts +3 -3
- package/examples/weather.ts +6 -9
- package/examples/wiki.ts +27 -8
- package/examples/word.ts +16 -11
- package/package.json +16 -11
- package/readme.md +1 -1
- package/src/agent-experimental.ts +1 -1
- package/src/agent.test.ts +243 -214
- package/src/agent.ts +286 -95
- package/src/decide.test.ts +276 -0
- package/src/decide.ts +163 -0
- package/src/index.ts +1 -1
- package/src/middleware.ts +91 -0
- package/src/mockModel.ts +47 -0
- package/src/planners/shortestPath.test.ts +94 -0
- package/src/planners/shortestPath.ts +177 -0
- package/src/planners/simple.ts +105 -0
- package/src/strategies/chain-of-note.ts +6 -55
- package/src/text.ts +51 -144
- package/src/types.ts +187 -212
- package/src/utils.ts +48 -4
- package/vitest.config.ts +9 -3
- package/src/adapters/vercel.ts +0 -7
- package/src/decision.test.ts +0 -179
- package/src/decision.ts +0 -84
- package/src/memory.ts +0 -25
- package/src/planners/shortestPathPlanner.ts +0 -22
- package/src/planners/simplePlanner.ts +0 -139
package/examples/joke.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { createAgent, fromDecision } from '../src';
|
|
|
3
3
|
import { loadingAnimation } from './helpers/loader';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { openai } from '@ai-sdk/openai';
|
|
6
|
-
import {
|
|
6
|
+
import { fromTerminal } from './helpers/helpers';
|
|
7
7
|
|
|
8
8
|
export function getRandomFunnyPhrase() {
|
|
9
9
|
const funnyPhrases = [
|
|
@@ -45,27 +45,31 @@ const loader = fromCallback(({ input }: { input: string }) => {
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
const agent = createAgent({
|
|
48
|
-
|
|
49
|
-
model: openai('gpt-
|
|
48
|
+
id: 'joke-teller',
|
|
49
|
+
model: openai('gpt-4o-mini'),
|
|
50
50
|
events: {
|
|
51
|
-
askForTopic: z
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
askForTopic: z
|
|
52
|
+
.object({
|
|
53
|
+
topic: z.string().describe('The topic for the joke'),
|
|
54
|
+
})
|
|
55
|
+
.describe('Ask for a new topic, because the last joke rated 6 or lower'),
|
|
54
56
|
'agent.tellJoke': z.object({
|
|
55
57
|
joke: z.string().describe('The joke text'),
|
|
56
58
|
}),
|
|
57
|
-
'agent.endJokes': z
|
|
59
|
+
'agent.endJokes': z
|
|
60
|
+
.object({})
|
|
61
|
+
.describe('End the jokes, since the last joke rated 7 or higher'),
|
|
58
62
|
'agent.rateJoke': z.object({
|
|
59
63
|
rating: z.number().min(1).max(10),
|
|
60
64
|
explanation: z.string(),
|
|
61
65
|
}),
|
|
62
66
|
'agent.continue': z.object({}).describe('Continue'),
|
|
63
|
-
'agent.
|
|
64
|
-
.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
'agent.markRelevancy': z.object({
|
|
68
|
+
relevant: z.boolean().describe('Whether the joke was relevant'),
|
|
69
|
+
explanation: z
|
|
70
|
+
.string()
|
|
71
|
+
.describe('The explanation for why the joke was relevant or not'),
|
|
72
|
+
}),
|
|
69
73
|
},
|
|
70
74
|
context: {
|
|
71
75
|
topic: z.string().describe('The topic for the joke'),
|
|
@@ -81,7 +85,7 @@ const jokeMachine = setup({
|
|
|
81
85
|
actors: {
|
|
82
86
|
agent: fromDecision(agent),
|
|
83
87
|
loader,
|
|
84
|
-
getFromTerminal,
|
|
88
|
+
getFromTerminal: fromTerminal,
|
|
85
89
|
},
|
|
86
90
|
}).createMachine({
|
|
87
91
|
id: 'joke',
|
|
@@ -107,72 +111,44 @@ const jokeMachine = setup({
|
|
|
107
111
|
},
|
|
108
112
|
},
|
|
109
113
|
tellingJoke: {
|
|
110
|
-
invoke:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
topic: context.topic,
|
|
116
|
-
},
|
|
117
|
-
goal: `Tell me a joke about the topic. Do not make any joke that is not relevant to the topic.`,
|
|
118
|
-
}),
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
src: 'loader',
|
|
122
|
-
input: getRandomFunnyPhrase,
|
|
123
|
-
},
|
|
124
|
-
],
|
|
114
|
+
invoke: {
|
|
115
|
+
src: 'loader',
|
|
116
|
+
input: getRandomFunnyPhrase,
|
|
117
|
+
},
|
|
118
|
+
|
|
125
119
|
on: {
|
|
126
120
|
'agent.tellJoke': {
|
|
127
121
|
actions: [
|
|
128
122
|
assign({
|
|
129
123
|
jokes: ({ context, event }) => [...context.jokes, event.joke],
|
|
130
124
|
}),
|
|
131
|
-
log((
|
|
125
|
+
log(({ event }) => event.joke),
|
|
132
126
|
],
|
|
133
127
|
target: 'relevance',
|
|
134
128
|
},
|
|
135
129
|
},
|
|
136
130
|
},
|
|
137
131
|
relevance: {
|
|
138
|
-
invoke: {
|
|
139
|
-
src: 'agent',
|
|
140
|
-
input: (x) => ({
|
|
141
|
-
context: {
|
|
142
|
-
topic: x.context.topic,
|
|
143
|
-
lastJoke: x.context.jokes[x.context.jokes.length - 1],
|
|
144
|
-
},
|
|
145
|
-
goal: 'An irrelevant joke has no reference to the topic. If the last joke is completely irrelevant to the topic, ask for a new joke topic. Otherwise, continue.',
|
|
146
|
-
}),
|
|
147
|
-
},
|
|
148
132
|
on: {
|
|
149
|
-
'agent.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
133
|
+
'agent.markRelevancy': [
|
|
134
|
+
{
|
|
135
|
+
guard: ({ event }) => !event.relevant,
|
|
136
|
+
actions: log(
|
|
137
|
+
({ event }) => 'Irrelevant joke: ' + event.explanation
|
|
138
|
+
),
|
|
139
|
+
target: 'waitingForTopic',
|
|
140
|
+
description: 'Continue',
|
|
141
|
+
},
|
|
142
|
+
{ target: 'rateJoke' },
|
|
143
|
+
],
|
|
158
144
|
},
|
|
159
145
|
},
|
|
160
146
|
rateJoke: {
|
|
161
|
-
invoke:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
jokes: context.jokes,
|
|
167
|
-
},
|
|
168
|
-
goal: `Rate the last joke on a scale of 1 to 10.`,
|
|
169
|
-
}),
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
src: 'loader',
|
|
173
|
-
input: getRandomRatingPhrase,
|
|
174
|
-
},
|
|
175
|
-
],
|
|
147
|
+
invoke: {
|
|
148
|
+
src: 'loader',
|
|
149
|
+
input: getRandomRatingPhrase,
|
|
150
|
+
},
|
|
151
|
+
|
|
176
152
|
on: {
|
|
177
153
|
'agent.rateJoke': {
|
|
178
154
|
actions: [
|
|
@@ -188,26 +164,14 @@ const jokeMachine = setup({
|
|
|
188
164
|
},
|
|
189
165
|
},
|
|
190
166
|
decide: {
|
|
191
|
-
invoke: {
|
|
192
|
-
src: 'agent',
|
|
193
|
-
input: ({ context }) => ({
|
|
194
|
-
context: {
|
|
195
|
-
lastRating: context.lastRating,
|
|
196
|
-
},
|
|
197
|
-
goal: `Choose what to do next, given the previous rating of the joke.`,
|
|
198
|
-
}),
|
|
199
|
-
},
|
|
200
167
|
on: {
|
|
201
168
|
askForTopic: {
|
|
202
169
|
target: 'waitingForTopic',
|
|
203
170
|
actions: log("That joke wasn't good enough. Let's try again."),
|
|
204
|
-
description:
|
|
205
|
-
'Ask for a new topic, because the last joke rated 6 or lower',
|
|
206
171
|
},
|
|
207
172
|
'agent.endJokes': {
|
|
208
173
|
target: 'end',
|
|
209
174
|
actions: log('That joke was good enough. Goodbye!'),
|
|
210
|
-
description: 'End the jokes, since the last joke rated 7 or higher',
|
|
211
175
|
},
|
|
212
176
|
},
|
|
213
177
|
},
|
|
@@ -222,4 +186,43 @@ const jokeMachine = setup({
|
|
|
222
186
|
|
|
223
187
|
const actor = createActor(jokeMachine);
|
|
224
188
|
|
|
189
|
+
agent.interact(actor, (observed) => {
|
|
190
|
+
if (observed.state.matches('tellingJoke')) {
|
|
191
|
+
return {
|
|
192
|
+
goal: 'Tell me a joke about the topic. Do not make any joke that is not relevant to the topic.',
|
|
193
|
+
context: {
|
|
194
|
+
topic: observed.state.context.topic,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (observed.state.matches('relevance')) {
|
|
200
|
+
return {
|
|
201
|
+
goal: 'An irrelevant joke has no reference to the topic. If the last joke is completely irrelevant to the topic, ask for a new joke topic. Otherwise, continue.',
|
|
202
|
+
context: {
|
|
203
|
+
topic: observed.state.context.topic,
|
|
204
|
+
lastJoke: observed.state.context.jokes.at(-1),
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (observed.state.matches('rateJoke')) {
|
|
210
|
+
return {
|
|
211
|
+
goal: 'Rate the last joke on a scale of 1 to 10.',
|
|
212
|
+
context: {
|
|
213
|
+
lastJoke: observed.state.context.jokes.at(-1),
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (observed.state.matches('decide')) {
|
|
219
|
+
return {
|
|
220
|
+
goal: 'Choose what to do next, given the previous rating of the joke.',
|
|
221
|
+
context: {
|
|
222
|
+
lastRating: observed.state.context.lastRating,
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
225
228
|
actor.start();
|
package/examples/jugs.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { createAgent } from '../src';
|
|
2
|
+
import { assign, createActor, setup } from 'xstate';
|
|
3
|
+
import { openai } from '@ai-sdk/openai';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { experimental_createShortestPathPlanner } from '../src/planners/shortestPath';
|
|
6
|
+
|
|
7
|
+
const agent = createAgent({
|
|
8
|
+
id: 'die-hard-solver',
|
|
9
|
+
model: openai('gpt-4o'),
|
|
10
|
+
events: {
|
|
11
|
+
fill3: z.object({}).describe('Fill the 3-gallon jug'),
|
|
12
|
+
fill5: z.object({}).describe('Fill the 5-gallon jug'),
|
|
13
|
+
empty3: z.object({}).describe('Empty the 3-gallon jug'),
|
|
14
|
+
empty5: z.object({}).describe('Empty the 5-gallon jug'),
|
|
15
|
+
pour3to5: z
|
|
16
|
+
.object({
|
|
17
|
+
reasoning: z
|
|
18
|
+
.string()
|
|
19
|
+
.describe(
|
|
20
|
+
'Very brief reasoning for pouring 3-gallon jug into 5-gallon jug'
|
|
21
|
+
),
|
|
22
|
+
})
|
|
23
|
+
.describe('Pour the 3-gallon jug into the 5-gallon jug'),
|
|
24
|
+
pour5to3: z
|
|
25
|
+
.object({
|
|
26
|
+
reasoning: z
|
|
27
|
+
.string()
|
|
28
|
+
.describe(
|
|
29
|
+
'Very brief reasoning for pouring 3-gallon jug into 5-gallon jug'
|
|
30
|
+
),
|
|
31
|
+
})
|
|
32
|
+
.describe('Pour the 5-gallon jug into the 3-gallon jug'),
|
|
33
|
+
},
|
|
34
|
+
context: {
|
|
35
|
+
jug3: z.number().int().describe('Gallons of water in the 3-gallon jug'),
|
|
36
|
+
jug5: z.number().int().describe('Gallons of water in the 5-gallon jug'),
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const waterJugMachine = setup({
|
|
41
|
+
types: {
|
|
42
|
+
context: agent.types.context,
|
|
43
|
+
events: agent.types.events,
|
|
44
|
+
},
|
|
45
|
+
}).createMachine({
|
|
46
|
+
initial: 'solving',
|
|
47
|
+
context: { jug3: 0, jug5: 0 },
|
|
48
|
+
states: {
|
|
49
|
+
solving: {
|
|
50
|
+
always: {
|
|
51
|
+
guard: ({ context }) => context.jug5 === 4,
|
|
52
|
+
target: 'success',
|
|
53
|
+
},
|
|
54
|
+
on: {
|
|
55
|
+
fill3: {
|
|
56
|
+
actions: assign({ jug3: 3 }),
|
|
57
|
+
},
|
|
58
|
+
fill5: {
|
|
59
|
+
actions: assign({ jug5: 5 }),
|
|
60
|
+
},
|
|
61
|
+
empty3: {
|
|
62
|
+
actions: assign({ jug3: 0 }),
|
|
63
|
+
},
|
|
64
|
+
empty5: {
|
|
65
|
+
actions: assign({ jug5: 0 }),
|
|
66
|
+
},
|
|
67
|
+
pour3to5: {
|
|
68
|
+
actions: assign(({ context }) => {
|
|
69
|
+
const total = context.jug3 + context.jug5;
|
|
70
|
+
const newJug5 = Math.min(5, total);
|
|
71
|
+
return {
|
|
72
|
+
jug5: newJug5,
|
|
73
|
+
jug3: total - newJug5,
|
|
74
|
+
};
|
|
75
|
+
}),
|
|
76
|
+
},
|
|
77
|
+
pour5to3: {
|
|
78
|
+
actions: assign(({ context }) => {
|
|
79
|
+
const total = context.jug3 + context.jug5;
|
|
80
|
+
const newJug3 = Math.min(3, total);
|
|
81
|
+
return {
|
|
82
|
+
jug3: newJug3,
|
|
83
|
+
jug5: total - newJug3,
|
|
84
|
+
};
|
|
85
|
+
}),
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
success: {
|
|
90
|
+
type: 'final',
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
let maxTries = 0;
|
|
96
|
+
async function main() {
|
|
97
|
+
const waterJugActor = createActor(waterJugMachine).start();
|
|
98
|
+
|
|
99
|
+
while (waterJugActor.getSnapshot().value !== 'success') {
|
|
100
|
+
maxTries++;
|
|
101
|
+
if (maxTries > 20) {
|
|
102
|
+
console.log('Max tries reached');
|
|
103
|
+
throw new Error('Max tries reached');
|
|
104
|
+
}
|
|
105
|
+
const decision = await agent.decide({
|
|
106
|
+
machine: waterJugMachine,
|
|
107
|
+
goal: 'Get exactly 4 gallons of water in the 5-gallon jug',
|
|
108
|
+
state: waterJugActor.getSnapshot(),
|
|
109
|
+
planner: experimental_createShortestPathPlanner(),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
console.log(decision?.nextEvent);
|
|
113
|
+
|
|
114
|
+
if (decision?.nextEvent) {
|
|
115
|
+
waterJugActor.send(decision.nextEvent);
|
|
116
|
+
console.log(waterJugActor.getSnapshot().context);
|
|
117
|
+
} else {
|
|
118
|
+
console.log('No decision made');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log('Done');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
main();
|
package/examples/multi.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createAgent, fromDecision } from '../src';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { assign, createActor, log, setup } from 'xstate';
|
|
4
|
-
import {
|
|
4
|
+
import { fromTerminal } from './helpers/helpers';
|
|
5
5
|
import { openai } from '@ai-sdk/openai';
|
|
6
6
|
|
|
7
7
|
const agent = createAgent({
|
|
8
|
-
|
|
9
|
-
model: openai('gpt-
|
|
8
|
+
id: 'multi',
|
|
9
|
+
model: openai('gpt-4o-mini'),
|
|
10
10
|
events: {
|
|
11
11
|
'agent.respond': z.object({
|
|
12
12
|
response: z.string().describe('The response from the agent'),
|
|
@@ -22,7 +22,7 @@ const machine = setup({
|
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
24
|
actors: {
|
|
25
|
-
getFromTerminal,
|
|
25
|
+
getFromTerminal: fromTerminal,
|
|
26
26
|
agent: fromDecision(agent),
|
|
27
27
|
},
|
|
28
28
|
}).createMachine({
|
|
@@ -69,7 +69,7 @@ const machine = setup({
|
|
|
69
69
|
invoke: {
|
|
70
70
|
src: 'agent',
|
|
71
71
|
input: ({ context }) => ({
|
|
72
|
-
model: openai('gpt-
|
|
72
|
+
model: openai('gpt-4-turbo'),
|
|
73
73
|
context,
|
|
74
74
|
goal: 'Debate the topic, and take the negative position. Respond directly to the last message of the discourse. Keep it short.',
|
|
75
75
|
}),
|
package/examples/newspaper.ts
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
// https://github.com/assafelovic/gpt-newspaper
|
|
3
3
|
// https://gist.github.com/TheGreatBonnie/58dc21ebbeeb8cbb08df665db762738c
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
|
5
|
+
import { tavily } from '@tavily/core';
|
|
6
|
+
|
|
8
7
|
import { assign, createActor, fromPromise, setup } from 'xstate';
|
|
8
|
+
import { createAgent } from '../src';
|
|
9
|
+
import { openai } from '@ai-sdk/openai';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { generateObject, generateText } from 'ai';
|
|
9
12
|
|
|
10
13
|
interface AgentState {
|
|
11
14
|
topic: string;
|
|
@@ -15,58 +18,41 @@ interface AgentState {
|
|
|
15
18
|
revisionCount: number;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
openAIApiKey: process.env.OPENAI_API_KEY,
|
|
23
|
-
});
|
|
24
|
-
}
|
|
21
|
+
const agent = createAgent({
|
|
22
|
+
model: openai('gpt-4o-mini'),
|
|
23
|
+
events: {},
|
|
24
|
+
});
|
|
25
25
|
|
|
26
|
-
async function search({
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
async function search({
|
|
27
|
+
topic,
|
|
28
|
+
}: Pick<AgentState, 'topic'>): Promise<string | undefined> {
|
|
29
|
+
const tvly = tavily({
|
|
29
30
|
apiKey: process.env.TAVILY_API_KEY,
|
|
30
31
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
topic = 'topic: ' + topic;
|
|
35
|
-
}
|
|
36
|
-
const docs = await retriever.invoke(topic);
|
|
37
|
-
return JSON.stringify(docs);
|
|
32
|
+
const response = await tvly.search(topic, {});
|
|
33
|
+
|
|
34
|
+
return response.answer;
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
async function curate(
|
|
41
38
|
input: Pick<AgentState, 'topic' | 'searchResults'>
|
|
42
39
|
): Promise<string> {
|
|
43
|
-
const response = await
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
${input.searchResults}`.replace(/\s+/g, ' ')
|
|
60
|
-
),
|
|
61
|
-
],
|
|
62
|
-
{
|
|
63
|
-
response_format: {
|
|
64
|
-
type: 'json_object',
|
|
65
|
-
},
|
|
66
|
-
}
|
|
67
|
-
);
|
|
68
|
-
const urls = JSON.parse(response.content as string).urls;
|
|
69
|
-
const searchResults = JSON.parse(input.searchResults!);
|
|
40
|
+
const response = await generateObject({
|
|
41
|
+
model: agent.model,
|
|
42
|
+
system: `
|
|
43
|
+
You are a personal newspaper editor.
|
|
44
|
+
Your sole task is to return a list of URLs of the 5 most relevant articles for the provided topic or query as a JSON list of strings.`.trim(),
|
|
45
|
+
prompt: `Today's date is ${new Date().toLocaleDateString('en-GB')}
|
|
46
|
+
Topic or Query: ${input.topic}
|
|
47
|
+
|
|
48
|
+
Here is a list of articles:
|
|
49
|
+
${input.searchResults}`.trim(),
|
|
50
|
+
schema: z.object({
|
|
51
|
+
urls: z.array(z.string()).describe('The URLs of the articles'),
|
|
52
|
+
}),
|
|
53
|
+
});
|
|
54
|
+
const urls = response.object.urls;
|
|
55
|
+
const searchResults = JSON.parse(input.searchResults ?? '[]');
|
|
70
56
|
const newSearchResults = searchResults.filter((result: any) => {
|
|
71
57
|
return urls.includes(result.metadata.source);
|
|
72
58
|
});
|
|
@@ -78,29 +64,36 @@ async function critique(
|
|
|
78
64
|
): Promise<string | undefined> {
|
|
79
65
|
let feedbackInstructions = '';
|
|
80
66
|
if (input.critique) {
|
|
81
|
-
feedbackInstructions =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
67
|
+
feedbackInstructions = `
|
|
68
|
+
The writer has revised the article based on your previous critique: ${input.critique}
|
|
69
|
+
The writer might have left feedback for you encoded between <FEEDBACK> tags.
|
|
70
|
+
The feedback is only for you to see and will be removed from the final article.
|
|
71
|
+
`.trim();
|
|
86
72
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
73
|
+
|
|
74
|
+
const response = await generateObject({
|
|
75
|
+
model: agent.model,
|
|
76
|
+
system: `
|
|
77
|
+
You are a personal newspaper writing critique.
|
|
78
|
+
Your sole purpose is to provide short feedback on a written article so the writer will know what to fix.
|
|
79
|
+
Today's date is ${new Date().toLocaleDateString('en-GB')}
|
|
80
|
+
Your task is to provide a really short feedback on the article only if necessary.
|
|
81
|
+
If you think the article is good, please return [DONE].
|
|
82
|
+
You can provide feedback on the revised article or just return [DONE] if you think the article is good.
|
|
83
|
+
Please return a string of your critique or [DONE].`.trim(),
|
|
84
|
+
prompt: `
|
|
85
|
+
${feedbackInstructions}
|
|
86
|
+
This is the article: ${input.article}`.trim(),
|
|
87
|
+
schema: z.object({
|
|
88
|
+
critique: z
|
|
89
|
+
.string()
|
|
90
|
+
.describe(
|
|
91
|
+
'The critique of the article or [DONE] if no changes are needed'
|
|
92
|
+
),
|
|
93
|
+
}),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const content = response.object.critique;
|
|
104
97
|
console.log('critique:', content);
|
|
105
98
|
return content.includes('[DONE]') ? undefined : content;
|
|
106
99
|
}
|
|
@@ -108,48 +101,55 @@ async function critique(
|
|
|
108
101
|
async function write(
|
|
109
102
|
input: Pick<AgentState, 'searchResults' | 'topic'>
|
|
110
103
|
): Promise<string> {
|
|
111
|
-
const response = await
|
|
112
|
-
|
|
104
|
+
const response = await generateObject({
|
|
105
|
+
model: agent.model,
|
|
106
|
+
system:
|
|
113
107
|
`You are a personal newspaper writer. Your sole purpose is to write a well-written article about a
|
|
114
|
-
|
|
115
|
-
/\s+/g,
|
|
116
|
-
' '
|
|
117
|
-
)
|
|
118
|
-
),
|
|
119
|
-
new HumanMessage(
|
|
120
|
-
`Today's date is ${new Date().toLocaleDateString('en-GB')}.
|
|
121
|
-
Your task is to write a critically acclaimed article for me about the provided query or
|
|
122
|
-
topic based on the sources.
|
|
123
|
-
Here is a list of articles: ${input.searchResults}
|
|
124
|
-
This is the topic: ${input.topic}
|
|
125
|
-
Please return a well-written article based on the provided information.`.replace(
|
|
108
|
+
topic using a list of articles. Write 5 paragraphs in markdown.`.replace(
|
|
126
109
|
/\s+/g,
|
|
127
110
|
' '
|
|
128
|
-
)
|
|
111
|
+
),
|
|
112
|
+
prompt: `Today's date is ${new Date().toLocaleDateString('en-GB')}.
|
|
113
|
+
Your task is to write a critically acclaimed article for me about the provided query or
|
|
114
|
+
topic based on the sources.
|
|
115
|
+
Here is a list of articles: ${input.searchResults}
|
|
116
|
+
This is the topic: ${input.topic}
|
|
117
|
+
Please return a well-written article based on the provided information.`.replace(
|
|
118
|
+
/\s+/g,
|
|
119
|
+
' '
|
|
129
120
|
),
|
|
130
|
-
|
|
131
|
-
|
|
121
|
+
schema: z.object({
|
|
122
|
+
article: z
|
|
123
|
+
.string()
|
|
124
|
+
.describe('The well-written article based on the provided information'),
|
|
125
|
+
}),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const content = response.object.article;
|
|
132
129
|
return content;
|
|
133
130
|
}
|
|
134
|
-
|
|
135
131
|
async function revise(
|
|
136
132
|
input: Pick<AgentState, 'article' | 'critique'>
|
|
137
133
|
): Promise<string> {
|
|
138
|
-
const response = await
|
|
139
|
-
|
|
134
|
+
const response = await generateObject({
|
|
135
|
+
model: agent.model,
|
|
136
|
+
system:
|
|
140
137
|
`You are a personal newspaper editor. Your sole purpose is to edit a well-written article about a
|
|
141
|
-
topic based on given critique.`.replace(/\s+/g, ' ')
|
|
142
|
-
|
|
143
|
-
new HumanMessage(
|
|
144
|
-
`Your task is to edit the article based on the critique given.
|
|
138
|
+
topic based on given critique.`.replace(/\s+/g, ' '),
|
|
139
|
+
prompt: `Your task is to edit the article based on the critique given.
|
|
145
140
|
This is the article: ${input.article}
|
|
146
141
|
This is the critique: ${input.critique}
|
|
147
142
|
Please return the edited article based on the critique given.
|
|
148
143
|
You may leave feedback about the critique encoded between <FEEDBACK> tags like this:
|
|
149
|
-
<FEEDBACK> here goes the feedback ...</FEEDBACK>`.replace(/\s+/g, ' ')
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
144
|
+
<FEEDBACK> here goes the feedback ...</FEEDBACK>`.replace(/\s+/g, ' '),
|
|
145
|
+
schema: z.object({
|
|
146
|
+
article: z
|
|
147
|
+
.string()
|
|
148
|
+
.describe('The edited article based on the critique given'),
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const content = response.object.article;
|
|
153
153
|
return content;
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -284,13 +284,7 @@ const machine = setup({
|
|
|
284
284
|
output: ({ context }) => context.article,
|
|
285
285
|
});
|
|
286
286
|
|
|
287
|
-
const actor = createActor(machine
|
|
288
|
-
// inspect: (inspEv) => {
|
|
289
|
-
// if (inspEv.type === '@xstate.event') {
|
|
290
|
-
// console.log(JSON.stringify(inspEv.event, null, 2));
|
|
291
|
-
// }
|
|
292
|
-
// },
|
|
293
|
-
});
|
|
287
|
+
const actor = createActor(machine);
|
|
294
288
|
|
|
295
289
|
actor.subscribe({
|
|
296
290
|
next: (s) => {
|