@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/examples/wordGuesser.ts
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
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/src/adapter.test.ts
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { test, expect } from 'vitest';
|
|
2
|
-
import { createOpenAIAdapter, createTool } from './adapters/openai';
|
|
3
|
-
import OpenAI from 'openai';
|
|
4
|
-
import { createActor, toPromise } from 'xstate';
|
|
5
|
-
|
|
6
|
-
test('fromTool - weather or illustration', async () => {
|
|
7
|
-
const openAi = new OpenAI({
|
|
8
|
-
apiKey: process.env.OPENAI_API_KEY,
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
const adapter = createOpenAIAdapter(openAi, {
|
|
12
|
-
model: 'gpt-3.5-turbo',
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
const toolChoice = adapter.fromTool(() => 'Create an image of a donut', {
|
|
16
|
-
makeIllustration: {
|
|
17
|
-
description: 'Make an illustration',
|
|
18
|
-
run: async () => 'Illustration',
|
|
19
|
-
inputSchema: {
|
|
20
|
-
type: 'object',
|
|
21
|
-
properties: {
|
|
22
|
-
name: {
|
|
23
|
-
type: 'string',
|
|
24
|
-
description: 'The name of the illustration',
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
required: ['name'],
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
getWeather: {
|
|
31
|
-
description: 'Get the weather for a location',
|
|
32
|
-
run: async () => 'Weather',
|
|
33
|
-
inputSchema: {
|
|
34
|
-
type: 'object',
|
|
35
|
-
properties: {
|
|
36
|
-
location: {
|
|
37
|
-
type: 'object',
|
|
38
|
-
properties: {
|
|
39
|
-
city: {
|
|
40
|
-
type: 'string',
|
|
41
|
-
description: 'The name of the city',
|
|
42
|
-
},
|
|
43
|
-
state: {
|
|
44
|
-
type: 'string',
|
|
45
|
-
description: 'The name of the state',
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
required: ['city', 'state'],
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
required: ['location'],
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const actor = createActor(toolChoice);
|
|
57
|
-
|
|
58
|
-
actor.start();
|
|
59
|
-
|
|
60
|
-
const res = await toPromise(actor);
|
|
61
|
-
|
|
62
|
-
expect(res?.result).toBe('Illustration');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('fromTool - GitHub PR description inserter', async () => {
|
|
66
|
-
const openAi = new OpenAI({
|
|
67
|
-
apiKey: process.env.OPENAI_API_KEY,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const adapter = createOpenAIAdapter(openAi, {
|
|
71
|
-
model: 'gpt-3.5-turbo-16k-0613',
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const toolChoice = adapter.fromTool(
|
|
75
|
-
(input: string) =>
|
|
76
|
-
`Create a GitHub PR description for the following: ${input}`,
|
|
77
|
-
{
|
|
78
|
-
fetchGitHubPR: {
|
|
79
|
-
description: 'Fetch a GitHub PR',
|
|
80
|
-
run: async (input: string) => {
|
|
81
|
-
return {
|
|
82
|
-
title: 'Title',
|
|
83
|
-
body: input,
|
|
84
|
-
};
|
|
85
|
-
},
|
|
86
|
-
inputSchema: {
|
|
87
|
-
type: 'object',
|
|
88
|
-
properties: {
|
|
89
|
-
repo: {
|
|
90
|
-
type: 'string',
|
|
91
|
-
description: 'The name of the repo',
|
|
92
|
-
},
|
|
93
|
-
number: {
|
|
94
|
-
type: 'number',
|
|
95
|
-
description: 'The number of the PR',
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
required: ['repo', 'number'],
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
createPullRequestDescription: {
|
|
102
|
-
description: 'Create a GitHub PR description',
|
|
103
|
-
run: () => 'Description',
|
|
104
|
-
inputSchema: {
|
|
105
|
-
type: 'object',
|
|
106
|
-
properties: {
|
|
107
|
-
title: {
|
|
108
|
-
type: 'string',
|
|
109
|
-
description: 'The title of the PR',
|
|
110
|
-
},
|
|
111
|
-
body: {
|
|
112
|
-
type: 'string',
|
|
113
|
-
description: 'The body of the PR',
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
required: ['title', 'body'],
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
}
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const actor = createActor(toolChoice, {
|
|
123
|
-
input:
|
|
124
|
-
// 'Get the details from this: https://github.com/microsoft/TypeScript/pull/47198',
|
|
125
|
-
'Make a summary of this PR: (some code here)',
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
actor.start();
|
|
129
|
-
|
|
130
|
-
const res = await toPromise(actor);
|
|
131
|
-
|
|
132
|
-
expect(res?.tool).toEqual('createPullRequestDescription');
|
|
133
|
-
expect(res?.result).toEqual('Description');
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test('fromTool - joke creator or rater', async () => {
|
|
137
|
-
const openAi = new OpenAI({
|
|
138
|
-
apiKey: process.env.OPENAI_API_KEY,
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
const adapter = createOpenAIAdapter(openAi, {
|
|
142
|
-
model: 'gpt-4-1106-preview',
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
const rateJoke = createTool({
|
|
146
|
-
description: 'Rate a joke',
|
|
147
|
-
inputSchema: {
|
|
148
|
-
type: 'object',
|
|
149
|
-
properties: {
|
|
150
|
-
joke: {
|
|
151
|
-
type: 'string',
|
|
152
|
-
description: 'The joke to rate',
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
run: async ({ topic }: { topic: string }) => {
|
|
157
|
-
return `Here is a joke about ${topic}`;
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const createJoke = createTool({
|
|
162
|
-
description: 'Create a joke',
|
|
163
|
-
inputSchema: {
|
|
164
|
-
type: 'object',
|
|
165
|
-
properties: {
|
|
166
|
-
category: {
|
|
167
|
-
type: 'string',
|
|
168
|
-
description: 'The category of the joke',
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
required: ['category'],
|
|
172
|
-
},
|
|
173
|
-
run: async () => {
|
|
174
|
-
return 'Some joke';
|
|
175
|
-
},
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const toolChoice = adapter.fromTool(
|
|
179
|
-
(input: string) => `
|
|
180
|
-
The user provided this input:
|
|
181
|
-
|
|
182
|
-
<input>
|
|
183
|
-
${input}
|
|
184
|
-
</input>
|
|
185
|
-
|
|
186
|
-
Determine what to do:
|
|
187
|
-
- If the input is asking for a joke, create a joke,
|
|
188
|
-
- But if the input is providing a joke, then rate the joke.
|
|
189
|
-
`,
|
|
190
|
-
{
|
|
191
|
-
rateJoke,
|
|
192
|
-
createJoke,
|
|
193
|
-
}
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
const actor = createActor(toolChoice, {
|
|
197
|
-
// input: 'Why did the chicken cross the road? To get to the other side!',
|
|
198
|
-
input: 'Tell me a joke about chickens',
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
actor.start();
|
|
202
|
-
|
|
203
|
-
const res = await toPromise(actor);
|
|
204
|
-
|
|
205
|
-
expect(res?.tool).toEqual('createJoke');
|
|
206
|
-
expect(res?.result).toEqual('Some joke');
|
|
207
|
-
|
|
208
|
-
const actor2 = createActor(toolChoice, {
|
|
209
|
-
input:
|
|
210
|
-
'Check this joke out: Why did the chicken cross the road? To get to the other side!',
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
actor2.start();
|
|
214
|
-
|
|
215
|
-
const res2 = await toPromise(actor2);
|
|
216
|
-
expect(res2?.tool).toEqual('rateJoke');
|
|
217
|
-
});
|
package/src/adapters/openai.ts
DELETED
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
import type OpenAI from 'openai';
|
|
2
|
-
import {
|
|
3
|
-
AnyEventObject,
|
|
4
|
-
Observer,
|
|
5
|
-
fromObservable,
|
|
6
|
-
fromPromise,
|
|
7
|
-
isMachineSnapshot,
|
|
8
|
-
toObserver,
|
|
9
|
-
} from 'xstate';
|
|
10
|
-
import { getAllTransitions } from '../utils';
|
|
11
|
-
import { ChatCompletionCreateParamsNonStreaming } from 'openai/resources';
|
|
12
|
-
import { ChatCompletionCreateParamsBase } from 'openai/resources/chat/completions';
|
|
13
|
-
import { StatelyAgentAdapter, Tool } from '../types';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Creates [promise actor logic](https://stately.ai/docs/promise-actors) that uses the OpenAI API to generate a completion.
|
|
17
|
-
*
|
|
18
|
-
* @param openai The OpenAI instance.
|
|
19
|
-
* @param inputFn A function that maps arbitrary input to OpenAI chat completion input.
|
|
20
|
-
*
|
|
21
|
-
*/
|
|
22
|
-
export function fromChatCompletion<TInput>(
|
|
23
|
-
openai: OpenAI,
|
|
24
|
-
agentSettings: StatelyAgentAdapter,
|
|
25
|
-
inputFn: (
|
|
26
|
-
input: TInput
|
|
27
|
-
) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming
|
|
28
|
-
) {
|
|
29
|
-
return fromPromise<OpenAI.Chat.Completions.ChatCompletion, TInput>(
|
|
30
|
-
async ({ input }) => {
|
|
31
|
-
const openAiInput = inputFn(input);
|
|
32
|
-
const params: ChatCompletionCreateParamsNonStreaming =
|
|
33
|
-
typeof openAiInput === 'string'
|
|
34
|
-
? {
|
|
35
|
-
model: agentSettings.model,
|
|
36
|
-
messages: [
|
|
37
|
-
{
|
|
38
|
-
role: 'user',
|
|
39
|
-
content: openAiInput,
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
}
|
|
43
|
-
: openAiInput;
|
|
44
|
-
const response = await openai.chat.completions.create(params);
|
|
45
|
-
|
|
46
|
-
return response;
|
|
47
|
-
}
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Creates [observable actor logic](https://stately.ai/docs/observable-actors) that uses the OpenAI API to generate a completion stream.
|
|
53
|
-
*
|
|
54
|
-
* @param openai The OpenAI instance to use.
|
|
55
|
-
* @param inputFn A function that maps arbitrary input to OpenAI chat completion input.
|
|
56
|
-
*/
|
|
57
|
-
export function fromChatStream<TInput>(
|
|
58
|
-
openai: OpenAI,
|
|
59
|
-
agentSettings: StatelyAgentAdapter,
|
|
60
|
-
inputFn: (
|
|
61
|
-
input: TInput
|
|
62
|
-
) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming
|
|
63
|
-
) {
|
|
64
|
-
return fromObservable<OpenAI.Chat.Completions.ChatCompletionChunk, TInput>(
|
|
65
|
-
({ input }) => {
|
|
66
|
-
const observers = new Set<Observer<any>>();
|
|
67
|
-
|
|
68
|
-
(async () => {
|
|
69
|
-
const openAiInput = inputFn(input);
|
|
70
|
-
const resolvedParams: ChatCompletionCreateParamsBase =
|
|
71
|
-
typeof openAiInput === 'string'
|
|
72
|
-
? {
|
|
73
|
-
model: agentSettings.model,
|
|
74
|
-
messages: [
|
|
75
|
-
{
|
|
76
|
-
role: 'user',
|
|
77
|
-
content: openAiInput,
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
}
|
|
81
|
-
: openAiInput;
|
|
82
|
-
const stream = await openai.chat.completions.create({
|
|
83
|
-
...resolvedParams,
|
|
84
|
-
stream: true,
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
for await (const part of stream) {
|
|
88
|
-
observers.forEach((observer) => {
|
|
89
|
-
observer.next?.(part);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
})();
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
subscribe: (...args) => {
|
|
96
|
-
const observer = toObserver(...(args as any));
|
|
97
|
-
observers.add(observer);
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
unsubscribe: () => {
|
|
101
|
-
observers.delete(observer);
|
|
102
|
-
},
|
|
103
|
-
};
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Creates [promise actor logic](https://stately.ai/docs/promise-actors) that passes the next possible transitions as functions to [OpenAI tool calls](https://platform.openai.com/docs/guides/function-calling) and returns an array of potential next events.
|
|
112
|
-
*
|
|
113
|
-
* @param openai The OpenAI instance to use.
|
|
114
|
-
* @param inputFn A function that maps arbitrary input to OpenAI chat completion input.
|
|
115
|
-
*/
|
|
116
|
-
export function fromEvent<TInput>(
|
|
117
|
-
openai: OpenAI,
|
|
118
|
-
agentSettings: StatelyAgentAdapter,
|
|
119
|
-
inputFn: (
|
|
120
|
-
input: TInput
|
|
121
|
-
) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming
|
|
122
|
-
) {
|
|
123
|
-
return fromPromise<AnyEventObject[] | undefined, TInput>(
|
|
124
|
-
async ({ input, self, system }) => {
|
|
125
|
-
const parentSnapshot = self._parent?.getSnapshot();
|
|
126
|
-
|
|
127
|
-
if (!parentSnapshot || !isMachineSnapshot(parentSnapshot)) {
|
|
128
|
-
return undefined;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const schemas = parentSnapshot.machine.schemas as any;
|
|
132
|
-
const eventSchemaMap = schemas.events ?? {};
|
|
133
|
-
|
|
134
|
-
const transitions = getAllTransitions(self._parent!.getSnapshot());
|
|
135
|
-
const functionNameMapping: Record<string, string> = {};
|
|
136
|
-
const tools = transitions
|
|
137
|
-
.filter((t) => {
|
|
138
|
-
return !t.eventType.startsWith('xstate.');
|
|
139
|
-
})
|
|
140
|
-
.map((t) => {
|
|
141
|
-
const name = t.eventType.replace(/\./g, '_');
|
|
142
|
-
functionNameMapping[name] = t.eventType;
|
|
143
|
-
const eventSchema = eventSchemaMap[t.eventType];
|
|
144
|
-
const {
|
|
145
|
-
description,
|
|
146
|
-
properties: { type, ...properties },
|
|
147
|
-
} = eventSchema ?? {};
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
type: 'function',
|
|
151
|
-
function: {
|
|
152
|
-
name,
|
|
153
|
-
description: t.description ?? description,
|
|
154
|
-
parameters: {
|
|
155
|
-
type: 'object',
|
|
156
|
-
properties: properties ?? {},
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
} as const;
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
const openAiInput = inputFn(input);
|
|
163
|
-
const completionParams: ChatCompletionCreateParamsNonStreaming =
|
|
164
|
-
typeof openAiInput === 'string'
|
|
165
|
-
? {
|
|
166
|
-
model: agentSettings.model,
|
|
167
|
-
messages: [
|
|
168
|
-
{
|
|
169
|
-
role: 'user',
|
|
170
|
-
content: openAiInput,
|
|
171
|
-
},
|
|
172
|
-
],
|
|
173
|
-
}
|
|
174
|
-
: openAiInput;
|
|
175
|
-
const completion = await openai.chat.completions.create({
|
|
176
|
-
...completionParams,
|
|
177
|
-
tools,
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
const toolCalls = completion.choices[0]?.message.tool_calls;
|
|
181
|
-
|
|
182
|
-
if (toolCalls?.length) {
|
|
183
|
-
const events = toolCalls.map((tc) => {
|
|
184
|
-
return {
|
|
185
|
-
type: functionNameMapping[tc.function.name],
|
|
186
|
-
...JSON.parse(tc.function.arguments),
|
|
187
|
-
};
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
const event = events[0]!;
|
|
191
|
-
|
|
192
|
-
// @ts-ignore
|
|
193
|
-
system._relay(self, self._parent, event);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return undefined;
|
|
197
|
-
}
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function createTool<TInput, T>({
|
|
202
|
-
description,
|
|
203
|
-
inputSchema,
|
|
204
|
-
run,
|
|
205
|
-
}: Tool<TInput, T>): Tool<TInput, T> {
|
|
206
|
-
return {
|
|
207
|
-
description,
|
|
208
|
-
inputSchema,
|
|
209
|
-
run,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Creates [promise actor logic](https://stately.ai/docs/promise-actors) that passes the next possible transitions as functions to [OpenAI tool calls](https://platform.openai.com/docs/guides/function-calling) and returns an array of potential next events.
|
|
215
|
-
*
|
|
216
|
-
* @param openai The OpenAI instance to use.
|
|
217
|
-
* @param inputFn A function that maps arbitrary input to OpenAI chat completion input.
|
|
218
|
-
*/
|
|
219
|
-
export function fromTool<TInput>(
|
|
220
|
-
openai: OpenAI,
|
|
221
|
-
agentSettings: StatelyAgentAdapter,
|
|
222
|
-
tools: {
|
|
223
|
-
[key: string]: Tool<any, any>;
|
|
224
|
-
},
|
|
225
|
-
inputFn: (
|
|
226
|
-
input: TInput
|
|
227
|
-
) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming
|
|
228
|
-
) {
|
|
229
|
-
return fromPromise<
|
|
230
|
-
| {
|
|
231
|
-
result: any;
|
|
232
|
-
tool: string;
|
|
233
|
-
toolCall: OpenAI.Chat.Completions.ChatCompletionMessageToolCall;
|
|
234
|
-
}
|
|
235
|
-
| undefined,
|
|
236
|
-
TInput
|
|
237
|
-
>(async ({ input }) => {
|
|
238
|
-
const resolvedTools = Object.entries(tools).map(([key, value]) => {
|
|
239
|
-
return {
|
|
240
|
-
type: 'function',
|
|
241
|
-
function: {
|
|
242
|
-
name: key,
|
|
243
|
-
description: value.description,
|
|
244
|
-
parameters: value.inputSchema,
|
|
245
|
-
},
|
|
246
|
-
} as const;
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
const openAiInput = inputFn(input);
|
|
250
|
-
const completionParams: ChatCompletionCreateParamsNonStreaming =
|
|
251
|
-
typeof openAiInput === 'string'
|
|
252
|
-
? {
|
|
253
|
-
model: agentSettings.model,
|
|
254
|
-
messages: [
|
|
255
|
-
{
|
|
256
|
-
role: 'user',
|
|
257
|
-
content: openAiInput,
|
|
258
|
-
},
|
|
259
|
-
],
|
|
260
|
-
}
|
|
261
|
-
: openAiInput;
|
|
262
|
-
const completion = await openai.chat.completions.create({
|
|
263
|
-
...completionParams,
|
|
264
|
-
tools: resolvedTools,
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
const toolCalls = completion.choices[0]?.message.tool_calls;
|
|
268
|
-
|
|
269
|
-
if (toolCalls?.length) {
|
|
270
|
-
const toolCall = toolCalls[0]!;
|
|
271
|
-
const tool = tools[toolCall.function.name];
|
|
272
|
-
const args = JSON.parse(toolCall.function.arguments);
|
|
273
|
-
|
|
274
|
-
if (tool) {
|
|
275
|
-
const result = await tool.run(args);
|
|
276
|
-
|
|
277
|
-
return {
|
|
278
|
-
toolCall,
|
|
279
|
-
tool: toolCall.function.name,
|
|
280
|
-
result,
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
return undefined;
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export function createOpenAIAdapter<
|
|
290
|
-
T extends {
|
|
291
|
-
model: ChatCompletionCreateParamsBase['model'];
|
|
292
|
-
}
|
|
293
|
-
>(openai: OpenAI, settings: T): StatelyAgentAdapter {
|
|
294
|
-
const agentSettings: StatelyAgentAdapter = {
|
|
295
|
-
model: settings.model,
|
|
296
|
-
fromEvent: (input) => fromEvent(openai, agentSettings, input),
|
|
297
|
-
fromChat: (input) => fromChatCompletion(openai, agentSettings, input),
|
|
298
|
-
fromChatStream: (input) => fromChatStream(openai, agentSettings, input),
|
|
299
|
-
fromTool: (input, tools) => fromTool(openai, agentSettings, tools, input),
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
return agentSettings;
|
|
303
|
-
}
|