@statelyai/agent 0.0.6 → 0.0.8
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/CHANGELOG.md +35 -0
- package/dist/index.d.ts +13 -44
- package/dist/index.js +4761 -25
- package/examples/joke.ts +55 -62
- package/examples/numberGuesser.ts +10 -37
- package/examples/ticTacToe.ts +37 -72
- package/examples/weather.ts +48 -27
- package/examples/wordGuesser.ts +144 -0
- package/package.json +5 -3
- package/src/adapters/openai.ts +8 -3
- package/src/agent.ts +1 -1
- package/src/index.ts +1 -1
- package/src/schemas.ts +18 -32
- package/src/utils.ts +23 -1
package/examples/joke.ts
CHANGED
|
@@ -1,53 +1,37 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
2
|
import { assign, fromCallback, fromPromise, log, setup } from 'xstate';
|
|
3
|
-
import { createAgent, createOpenAIAdapter,
|
|
3
|
+
import { createAgent, createOpenAIAdapter, defineEvents } from '../src';
|
|
4
4
|
import { loadingAnimation } from './helpers/loader';
|
|
5
|
+
import { z } from 'zod';
|
|
5
6
|
|
|
6
7
|
const openai = new OpenAI({
|
|
7
8
|
apiKey: process.env.OPENAI_API_KEY,
|
|
8
9
|
});
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
},
|
|
24
|
-
required: ['topic', 'jokes', 'desire', 'lastRating'],
|
|
25
|
-
},
|
|
26
|
-
events: {
|
|
27
|
-
askForTopic: {
|
|
28
|
-
type: 'object',
|
|
29
|
-
properties: {
|
|
30
|
-
topic: {
|
|
31
|
-
type: 'string',
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
endJokes: {
|
|
36
|
-
type: 'object',
|
|
37
|
-
properties: {},
|
|
38
|
-
},
|
|
39
|
-
},
|
|
11
|
+
const events = defineEvents({
|
|
12
|
+
askForTopic: z.object({
|
|
13
|
+
topic: z.string().describe('The topic for the joke'),
|
|
14
|
+
}),
|
|
15
|
+
tellJoke: z.object({
|
|
16
|
+
joke: z.string().describe('The joke text'),
|
|
17
|
+
}),
|
|
18
|
+
endJokes: z.object({}).describe('End the jokes'),
|
|
19
|
+
|
|
20
|
+
rateJoke: z.object({
|
|
21
|
+
rating: z.number().min(1).max(10),
|
|
22
|
+
explanation: z.string(),
|
|
23
|
+
}),
|
|
40
24
|
});
|
|
41
25
|
|
|
42
26
|
const adapter = createOpenAIAdapter(openai, {
|
|
43
27
|
model: 'gpt-3.5-turbo-1106',
|
|
44
28
|
});
|
|
45
29
|
|
|
46
|
-
const getJokeCompletion = adapter.
|
|
30
|
+
const getJokeCompletion = adapter.fromEvent(
|
|
47
31
|
(topic: string) => `Tell me a joke about ${topic}.`
|
|
48
32
|
);
|
|
49
33
|
|
|
50
|
-
const rateJoke = adapter.
|
|
34
|
+
const rateJoke = adapter.fromEvent(
|
|
51
35
|
(joke: string) => `Rate this joke on a scale of 1 to 10: ${joke}`
|
|
52
36
|
);
|
|
53
37
|
|
|
@@ -66,7 +50,7 @@ const getTopic = fromPromise(async () => {
|
|
|
66
50
|
});
|
|
67
51
|
|
|
68
52
|
const decide = adapter.fromEvent(
|
|
69
|
-
(lastRating:
|
|
53
|
+
(lastRating: number) =>
|
|
70
54
|
`Choose what to do next, given the previous rating of the joke: ${lastRating}`
|
|
71
55
|
);
|
|
72
56
|
export function getRandomFunnyPhrase() {
|
|
@@ -109,8 +93,19 @@ const loader = fromCallback(({ input }: { input: string }) => {
|
|
|
109
93
|
});
|
|
110
94
|
|
|
111
95
|
const jokeMachine = setup({
|
|
112
|
-
schemas
|
|
113
|
-
|
|
96
|
+
schemas: {
|
|
97
|
+
events: events.schemas,
|
|
98
|
+
},
|
|
99
|
+
types: {
|
|
100
|
+
context: {} as {
|
|
101
|
+
topic: string;
|
|
102
|
+
jokes: string[];
|
|
103
|
+
desire: string | null;
|
|
104
|
+
lastRating: number | null;
|
|
105
|
+
loader: string | null;
|
|
106
|
+
},
|
|
107
|
+
events: events.types,
|
|
108
|
+
},
|
|
114
109
|
actors: {
|
|
115
110
|
getJokeCompletion,
|
|
116
111
|
getTopic,
|
|
@@ -119,6 +114,7 @@ const jokeMachine = setup({
|
|
|
119
114
|
loader,
|
|
120
115
|
},
|
|
121
116
|
}).createMachine({
|
|
117
|
+
id: 'joke',
|
|
122
118
|
context: () => ({
|
|
123
119
|
topic: '',
|
|
124
120
|
jokes: [],
|
|
@@ -144,54 +140,45 @@ const jokeMachine = setup({
|
|
|
144
140
|
{
|
|
145
141
|
src: 'getJokeCompletion',
|
|
146
142
|
input: ({ context }) => context.topic,
|
|
147
|
-
onDone: {
|
|
148
|
-
actions: [
|
|
149
|
-
assign({
|
|
150
|
-
jokes: ({ context, event }) =>
|
|
151
|
-
context.jokes.concat(
|
|
152
|
-
event.output.choices[0]!.message.content!
|
|
153
|
-
),
|
|
154
|
-
}),
|
|
155
|
-
log((x) => `\n` + x.context.jokes.at(-1)),
|
|
156
|
-
],
|
|
157
|
-
target: 'rateJoke',
|
|
158
|
-
},
|
|
159
143
|
},
|
|
160
144
|
{
|
|
161
145
|
src: 'loader',
|
|
162
146
|
input: getRandomFunnyPhrase,
|
|
163
147
|
},
|
|
164
148
|
],
|
|
149
|
+
on: {
|
|
150
|
+
tellJoke: {
|
|
151
|
+
actions: assign({
|
|
152
|
+
jokes: ({ context, event }) => [...context.jokes, event.joke],
|
|
153
|
+
}),
|
|
154
|
+
target: 'rateJoke',
|
|
155
|
+
},
|
|
156
|
+
},
|
|
165
157
|
},
|
|
166
158
|
rateJoke: {
|
|
167
159
|
invoke: [
|
|
168
160
|
{
|
|
169
161
|
src: 'rateJoke',
|
|
170
162
|
input: ({ context }) => context.jokes[context.jokes.length - 1]!,
|
|
171
|
-
onDone: {
|
|
172
|
-
actions: [
|
|
173
|
-
assign({
|
|
174
|
-
lastRating: ({ event }) =>
|
|
175
|
-
event.output.choices[0]!.message.content!,
|
|
176
|
-
}),
|
|
177
|
-
log(({ context }) => '\n' + context.lastRating),
|
|
178
|
-
],
|
|
179
|
-
target: 'decide',
|
|
180
|
-
},
|
|
181
163
|
},
|
|
182
164
|
{
|
|
183
165
|
src: 'loader',
|
|
184
166
|
input: getRandomRatingPhrase,
|
|
185
167
|
},
|
|
186
168
|
],
|
|
169
|
+
on: {
|
|
170
|
+
rateJoke: {
|
|
171
|
+
actions: assign({
|
|
172
|
+
lastRating: ({ event }) => event.rating,
|
|
173
|
+
}),
|
|
174
|
+
target: 'decide',
|
|
175
|
+
},
|
|
176
|
+
},
|
|
187
177
|
},
|
|
188
178
|
decide: {
|
|
189
179
|
invoke: {
|
|
190
180
|
src: 'decide',
|
|
191
181
|
input: ({ context }) => context.lastRating!,
|
|
192
|
-
onDone: {
|
|
193
|
-
actions: log(({ event }) => event),
|
|
194
|
-
},
|
|
195
182
|
},
|
|
196
183
|
on: {
|
|
197
184
|
askForTopic: {
|
|
@@ -216,5 +203,11 @@ const jokeMachine = setup({
|
|
|
216
203
|
},
|
|
217
204
|
});
|
|
218
205
|
|
|
219
|
-
const agent = createAgent(jokeMachine
|
|
206
|
+
const agent = createAgent(jokeMachine, {
|
|
207
|
+
inspect: (ev) => {
|
|
208
|
+
if (ev.type === '@xstate.event') {
|
|
209
|
+
console.log(`\n${ev.actorRef.id}`, ev.event);
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
});
|
|
220
213
|
agent.start();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import { createAgent, createOpenAIAdapter,
|
|
2
|
+
import { createAgent, createOpenAIAdapter, defineEvents } from '../src';
|
|
3
3
|
import { assign, setup } from 'xstate';
|
|
4
|
+
import { z } from 'zod';
|
|
4
5
|
const openai = new OpenAI({
|
|
5
6
|
apiKey: process.env.OPENAI_API_KEY,
|
|
6
7
|
});
|
|
@@ -23,40 +24,10 @@ const guessLogic = adapter.fromEvent(
|
|
|
23
24
|
`
|
|
24
25
|
);
|
|
25
26
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
lastGuess: {
|
|
31
|
-
type: ['number', 'null'],
|
|
32
|
-
description: 'The last guess',
|
|
33
|
-
},
|
|
34
|
-
previousGuesses: {
|
|
35
|
-
type: 'array',
|
|
36
|
-
items: {
|
|
37
|
-
type: 'number',
|
|
38
|
-
},
|
|
39
|
-
description: 'The previous guesses',
|
|
40
|
-
},
|
|
41
|
-
answer: {
|
|
42
|
-
type: 'number',
|
|
43
|
-
description: 'The answer',
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
events: {
|
|
48
|
-
guess: {
|
|
49
|
-
properties: {
|
|
50
|
-
number: {
|
|
51
|
-
// integer
|
|
52
|
-
type: 'number',
|
|
53
|
-
minimum: 1,
|
|
54
|
-
maximum: 10,
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
required: ['number'],
|
|
58
|
-
},
|
|
59
|
-
},
|
|
27
|
+
const events = defineEvents({
|
|
28
|
+
guess: z.object({
|
|
29
|
+
number: z.number().min(1).max(10).describe('The number guessed'),
|
|
30
|
+
}),
|
|
60
31
|
});
|
|
61
32
|
|
|
62
33
|
const machine = setup({
|
|
@@ -66,9 +37,11 @@ const machine = setup({
|
|
|
66
37
|
answer: number;
|
|
67
38
|
},
|
|
68
39
|
input: {} as { answer: number },
|
|
69
|
-
events:
|
|
40
|
+
events: events.types,
|
|
41
|
+
},
|
|
42
|
+
schemas: {
|
|
43
|
+
events: events.schemas,
|
|
70
44
|
},
|
|
71
|
-
schemas,
|
|
72
45
|
actors: {
|
|
73
46
|
guessLogic,
|
|
74
47
|
},
|
package/examples/ticTacToe.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { assign, setup, assertEvent } from 'xstate';
|
|
2
2
|
import OpenAI from 'openai';
|
|
3
|
-
import {
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
5
|
+
import { createOpenAIAdapter, defineEvents, createAgent } from '../src';
|
|
4
6
|
|
|
5
7
|
const openai = new OpenAI({
|
|
6
8
|
apiKey: process.env.OPENAI_API_KEY,
|
|
@@ -8,70 +10,32 @@ const openai = new OpenAI({
|
|
|
8
10
|
|
|
9
11
|
type Player = 'x' | 'o';
|
|
10
12
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
description: 'The number of moves that have been played',
|
|
28
|
-
},
|
|
29
|
-
player: {
|
|
30
|
-
type: 'string',
|
|
31
|
-
enum: ['x', 'o'],
|
|
32
|
-
description: 'The player whose turn it is',
|
|
33
|
-
},
|
|
34
|
-
gameReport: {
|
|
35
|
-
type: 'string',
|
|
36
|
-
description: 'The game report',
|
|
37
|
-
},
|
|
38
|
-
events: {
|
|
39
|
-
type: 'array',
|
|
40
|
-
items: {
|
|
41
|
-
type: 'string',
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
required: ['board', 'moves', 'player', 'gameReport', 'events'],
|
|
46
|
-
},
|
|
47
|
-
events: {
|
|
48
|
-
'x.play': {
|
|
49
|
-
properties: {
|
|
50
|
-
index: {
|
|
51
|
-
description: 'The index of the cell to play on',
|
|
52
|
-
type: 'number',
|
|
53
|
-
|
|
54
|
-
minimum: 0,
|
|
55
|
-
maximum: 8,
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
'o.play': {
|
|
60
|
-
properties: {
|
|
61
|
-
index: {
|
|
62
|
-
description: 'The index of the cell to play on',
|
|
63
|
-
type: 'number',
|
|
64
|
-
minimum: 0,
|
|
65
|
-
maximum: 8,
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
reset: {
|
|
70
|
-
properties: {},
|
|
71
|
-
},
|
|
72
|
-
},
|
|
13
|
+
const events = defineEvents({
|
|
14
|
+
'x.play': z.object({
|
|
15
|
+
index: z
|
|
16
|
+
.number()
|
|
17
|
+
.min(0)
|
|
18
|
+
.max(8)
|
|
19
|
+
.describe('The index of the cell to play on'),
|
|
20
|
+
}),
|
|
21
|
+
'o.play': z.object({
|
|
22
|
+
index: z
|
|
23
|
+
.number()
|
|
24
|
+
.min(0)
|
|
25
|
+
.max(8)
|
|
26
|
+
.describe('The index of the cell to play on'),
|
|
27
|
+
}),
|
|
28
|
+
reset: z.object({}).describe('Reset the game to the initial state'),
|
|
73
29
|
});
|
|
74
30
|
|
|
31
|
+
interface GameContext {
|
|
32
|
+
board: (Player | null)[];
|
|
33
|
+
moves: number;
|
|
34
|
+
player: Player;
|
|
35
|
+
gameReport: string;
|
|
36
|
+
events: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
75
39
|
const adapter = createOpenAIAdapter(openai, {
|
|
76
40
|
model: 'gpt-4-1106-preview',
|
|
77
41
|
});
|
|
@@ -82,10 +46,10 @@ const initialContext = {
|
|
|
82
46
|
player: 'x' as Player,
|
|
83
47
|
gameReport: '',
|
|
84
48
|
events: [],
|
|
85
|
-
} satisfies
|
|
49
|
+
} satisfies GameContext;
|
|
86
50
|
|
|
87
51
|
const bot = adapter.fromEvent(
|
|
88
|
-
({ context }: { context:
|
|
52
|
+
({ context }: { context: GameContext }) => `
|
|
89
53
|
You are playing a game of tic tac toe. This is the current game state. The 3x3 board is represented by a 9-element array. The first element is the top-left cell, the second element is the top-middle cell, the third element is the top-right cell, the fourth element is the middle-left cell, and so on. The value of each cell is either null, x, or o. The value of null means that the cell is empty. The value of x means that the cell is occupied by an x. The value of o means that the cell is occupied by an o.
|
|
90
54
|
|
|
91
55
|
${JSON.stringify(context, null, 2)}
|
|
@@ -94,11 +58,7 @@ Execute the single best next move to try to win the game. Do not play on an exis
|
|
|
94
58
|
);
|
|
95
59
|
|
|
96
60
|
const gameReporter = adapter.fromChatStream(
|
|
97
|
-
({
|
|
98
|
-
context,
|
|
99
|
-
}: {
|
|
100
|
-
context: typeof schemas.types.context;
|
|
101
|
-
}) => `Here is the game board:
|
|
61
|
+
({ context }: { context: GameContext }) => `Here is the game board:
|
|
102
62
|
|
|
103
63
|
${JSON.stringify(context.board, null, 2)}
|
|
104
64
|
|
|
@@ -131,8 +91,13 @@ function getWinner(board: typeof initialContext.board): Player | null {
|
|
|
131
91
|
}
|
|
132
92
|
|
|
133
93
|
export const ticTacToeMachine = setup({
|
|
134
|
-
schemas
|
|
135
|
-
|
|
94
|
+
schemas: {
|
|
95
|
+
events: events.schemas,
|
|
96
|
+
},
|
|
97
|
+
types: {
|
|
98
|
+
context: {} as GameContext,
|
|
99
|
+
events: events.types,
|
|
100
|
+
},
|
|
136
101
|
actors: {
|
|
137
102
|
bot,
|
|
138
103
|
gameReporter,
|
package/examples/weather.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import { createAgent, createOpenAIAdapter,
|
|
2
|
+
import { createAgent, createOpenAIAdapter, defineEvents } from '../src';
|
|
3
3
|
import { assign, fromPromise, log, setup } from 'xstate';
|
|
4
4
|
import { getFromTerminal } from './helpers/helpers';
|
|
5
|
+
import { z } from 'zod';
|
|
5
6
|
|
|
6
7
|
async function searchTavily(
|
|
7
8
|
input: string,
|
|
@@ -40,28 +41,21 @@ const openai = new OpenAI({
|
|
|
40
41
|
apiKey: process.env.OPENAI_API_KEY,
|
|
41
42
|
});
|
|
42
43
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
location:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
},
|
|
59
|
-
doSomethingElse: {
|
|
60
|
-
description:
|
|
61
|
-
'Do something else, because the user did not provide a location',
|
|
62
|
-
properties: {},
|
|
63
|
-
},
|
|
64
|
-
},
|
|
44
|
+
const events = defineEvents({
|
|
45
|
+
getWeather: z.object({
|
|
46
|
+
location: z.string().describe('The location to get the weather for'),
|
|
47
|
+
}),
|
|
48
|
+
reportWeather: z.object({
|
|
49
|
+
location: z
|
|
50
|
+
.string()
|
|
51
|
+
.describe('The location the weather is being reported for'),
|
|
52
|
+
highF: z.number().describe('The high temperature today in Fahrenheit'),
|
|
53
|
+
lowF: z.number().describe('The low temperature today in Fahrenheit'),
|
|
54
|
+
summary: z.string().describe('A summary of the weather conditions'),
|
|
55
|
+
}),
|
|
56
|
+
doSomethingElse: z
|
|
57
|
+
.object({})
|
|
58
|
+
.describe('Do something else, because the user did not provide a location'),
|
|
65
59
|
});
|
|
66
60
|
|
|
67
61
|
const adapter = createOpenAIAdapter(openai, {
|
|
@@ -79,11 +73,23 @@ const getWeather = fromPromise(async ({ input }: { input: string }) => {
|
|
|
79
73
|
return results;
|
|
80
74
|
});
|
|
81
75
|
|
|
76
|
+
const reportWeather = adapter.fromEvent(() => 'Report the weather');
|
|
77
|
+
|
|
82
78
|
const machine = setup({
|
|
83
|
-
schemas
|
|
84
|
-
|
|
79
|
+
schemas: {
|
|
80
|
+
events: events.schemas,
|
|
81
|
+
},
|
|
82
|
+
types: {
|
|
83
|
+
context: {} as {
|
|
84
|
+
location: string;
|
|
85
|
+
history: string[];
|
|
86
|
+
count: number;
|
|
87
|
+
},
|
|
88
|
+
events: events.types,
|
|
89
|
+
},
|
|
85
90
|
actors: {
|
|
86
91
|
getWeather,
|
|
92
|
+
reportWeather,
|
|
87
93
|
decide: adapter.fromEvent(
|
|
88
94
|
(input: string) =>
|
|
89
95
|
`Decide what to do based on the given input, which may or may not be a location: ${input}`
|
|
@@ -140,6 +146,17 @@ const machine = setup({
|
|
|
140
146
|
count: ({ context }) => context.count + 1,
|
|
141
147
|
}),
|
|
142
148
|
],
|
|
149
|
+
target: 'reportWeather',
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
reportWeather: {
|
|
154
|
+
invoke: {
|
|
155
|
+
src: 'reportWeather',
|
|
156
|
+
},
|
|
157
|
+
on: {
|
|
158
|
+
reportWeather: {
|
|
159
|
+
actions: log(({ event }) => event),
|
|
143
160
|
target: 'getLocation',
|
|
144
161
|
},
|
|
145
162
|
},
|
|
@@ -153,8 +170,12 @@ const machine = setup({
|
|
|
153
170
|
},
|
|
154
171
|
});
|
|
155
172
|
|
|
156
|
-
createAgent(machine, {
|
|
173
|
+
const actor = createAgent(machine, {
|
|
157
174
|
input: {
|
|
158
175
|
location: 'New York',
|
|
159
176
|
},
|
|
160
|
-
})
|
|
177
|
+
});
|
|
178
|
+
actor.subscribe((s) => {
|
|
179
|
+
console.log(s.value);
|
|
180
|
+
});
|
|
181
|
+
actor.start();
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { assign, log, setup } from 'xstate';
|
|
2
|
+
import { getFromTerminal } from './helpers/helpers';
|
|
3
|
+
import { createAgent, createOpenAIAdapter, defineEvents } from '../src';
|
|
4
|
+
import OpenAI from 'openai';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
const openAI = new OpenAI({
|
|
8
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const adapter = createOpenAIAdapter(openAI, {
|
|
12
|
+
model: 'gpt-4-1106-preview',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const events = defineEvents({
|
|
16
|
+
guessLetter: z.object({
|
|
17
|
+
letter: z.string().min(1).max(1).describe('The letter guessed'),
|
|
18
|
+
}),
|
|
19
|
+
|
|
20
|
+
guessWord: z.object({
|
|
21
|
+
word: z.string().describe('The word guessed'),
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const context = {
|
|
26
|
+
word: null as string | null,
|
|
27
|
+
guessedWord: null as string | null,
|
|
28
|
+
letters: [] as string[],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const wordGuesserMachine = setup({
|
|
32
|
+
types: {
|
|
33
|
+
context: {} as typeof context,
|
|
34
|
+
events: events.types,
|
|
35
|
+
},
|
|
36
|
+
actors: {
|
|
37
|
+
getFromTerminal,
|
|
38
|
+
guesser: adapter.fromEvent(
|
|
39
|
+
(input: typeof context) => `
|
|
40
|
+
You are trying to guess the word. The word has ${
|
|
41
|
+
input.word!.length
|
|
42
|
+
} letters. You have guessed the following letters so far: ${input.letters.join(
|
|
43
|
+
', '
|
|
44
|
+
)}. These letters matched: ${input
|
|
45
|
+
.word!.split('')
|
|
46
|
+
.map((letter) =>
|
|
47
|
+
input.letters.includes(letter.toUpperCase())
|
|
48
|
+
? letter.toUpperCase()
|
|
49
|
+
: '_'
|
|
50
|
+
)
|
|
51
|
+
.join('')}
|
|
52
|
+
Please make your next guess - type a letter or the full word. You can only make 10 total guesses.
|
|
53
|
+
`
|
|
54
|
+
),
|
|
55
|
+
},
|
|
56
|
+
schemas: {
|
|
57
|
+
events: events.schemas,
|
|
58
|
+
},
|
|
59
|
+
}).createMachine({
|
|
60
|
+
initial: 'providingWord',
|
|
61
|
+
context,
|
|
62
|
+
states: {
|
|
63
|
+
providingWord: {
|
|
64
|
+
invoke: {
|
|
65
|
+
src: 'getFromTerminal',
|
|
66
|
+
input: 'Enter a word',
|
|
67
|
+
onDone: {
|
|
68
|
+
actions: assign({
|
|
69
|
+
word: ({ event }) => event.output,
|
|
70
|
+
}),
|
|
71
|
+
target: 'guessing',
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
guessing: {
|
|
76
|
+
always: {
|
|
77
|
+
guard: ({ context }) => context.letters.length > 10,
|
|
78
|
+
target: 'finalGuess',
|
|
79
|
+
},
|
|
80
|
+
invoke: {
|
|
81
|
+
src: 'guesser',
|
|
82
|
+
input: ({ context }) => context,
|
|
83
|
+
},
|
|
84
|
+
on: {
|
|
85
|
+
guessLetter: {
|
|
86
|
+
actions: assign({
|
|
87
|
+
letters: ({ context, event }) => {
|
|
88
|
+
return [...context.letters, event.letter.toUpperCase()];
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
target: 'guessing',
|
|
92
|
+
reenter: true,
|
|
93
|
+
},
|
|
94
|
+
guessWord: {
|
|
95
|
+
actions: assign({
|
|
96
|
+
guessedWord: ({ event }) => event.word,
|
|
97
|
+
}),
|
|
98
|
+
target: 'gameOver',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
finalGuess: {
|
|
103
|
+
invoke: {
|
|
104
|
+
src: 'guesser',
|
|
105
|
+
input: ({ context }) => context,
|
|
106
|
+
},
|
|
107
|
+
on: {
|
|
108
|
+
guessWord: {
|
|
109
|
+
actions: assign({
|
|
110
|
+
guessedWord: ({ event }) => event.word,
|
|
111
|
+
}),
|
|
112
|
+
target: 'gameOver',
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
gameOver: {
|
|
117
|
+
entry: log(({ context }) => {
|
|
118
|
+
if (
|
|
119
|
+
context.guessedWord?.toUpperCase() === context.word?.toUpperCase()
|
|
120
|
+
) {
|
|
121
|
+
return 'You won!';
|
|
122
|
+
} else {
|
|
123
|
+
return 'You lost! The word was ' + context.word;
|
|
124
|
+
}
|
|
125
|
+
}),
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
exit: () => process.exit(),
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const actor = createAgent(wordGuesserMachine, {
|
|
132
|
+
inspect: (ev) => {
|
|
133
|
+
if (ev.type === '@xstate.event') {
|
|
134
|
+
console.log(ev.event);
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
actor.subscribe((s) => {
|
|
140
|
+
console.log(s.value);
|
|
141
|
+
console.log(s.context);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
actor.start();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
"ts-node": "^10.9.2",
|
|
19
19
|
"tsup": "^8.0.1",
|
|
20
20
|
"typescript": "^5.3.3",
|
|
21
|
-
"vitest": "^1.2.2"
|
|
21
|
+
"vitest": "^1.2.2",
|
|
22
|
+
"zod": "^3.22.4",
|
|
23
|
+
"zod-to-json-schema": "^3.22.4"
|
|
22
24
|
},
|
|
23
25
|
"publishConfig": {
|
|
24
26
|
"access": "public"
|
|
@@ -29,7 +31,7 @@
|
|
|
29
31
|
"packageManager": "pnpm@8.11.0",
|
|
30
32
|
"scripts": {
|
|
31
33
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
32
|
-
"lint": "tsc",
|
|
34
|
+
"lint": "tsc --noEmit",
|
|
33
35
|
"test": "vitest",
|
|
34
36
|
"example": "ts-node examples/helpers/runner.ts",
|
|
35
37
|
"changeset": "changeset",
|