@statelyai/agent 0.0.3 → 0.0.4
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 +8 -0
- package/dist/index.d.ts +39 -47
- package/dist/index.js +48 -33
- package/examples/helpers/helpers.ts +1 -1
- package/examples/joke.ts +32 -30
- package/examples/multiAgentCollaboration.ts +0 -0
- package/examples/ticTacToe.ts +17 -19
- package/examples/weather.ts +30 -17
- package/package.json +2 -2
- package/src/{openai.ts → adapters/openai.ts} +47 -64
- package/src/agent.ts +8 -0
- package/src/index.ts +3 -6
- package/src/schemas.ts +38 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @statelyai/agent
|
|
2
2
|
|
|
3
|
+
## 0.0.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#5](https://github.com/statelyai/agent/pull/5) [`ae473d7`](https://github.com/statelyai/agent/commit/ae473d73399a15ac3199d77d00eb44a0ea5626db) Thanks [@davidkpiano](https://github.com/davidkpiano)! - Simplify API (WIP)
|
|
8
|
+
|
|
9
|
+
- [#5](https://github.com/statelyai/agent/pull/5) [`687bed8`](https://github.com/statelyai/agent/commit/687bed87f29bd1d13447cc53b5154da0fe6fdcab) Thanks [@davidkpiano](https://github.com/davidkpiano)! - Add `createSchemas`, `createOpenAIAdapter`, and change `createAgent`
|
|
10
|
+
|
|
3
11
|
## 0.0.3
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Prop,
|
|
1
|
+
import * as xstate from 'xstate';
|
|
2
|
+
import { Prop, Values, AnyStateMachine, createActor, PromiseActorLogic, AnyEventObject, ObservableActorLogic } from 'xstate';
|
|
3
3
|
import { JSONSchema7 } from 'json-schema-to-ts/lib/types/definitions';
|
|
4
4
|
import { FromSchema } from 'json-schema-to-ts';
|
|
5
|
+
import OpenAI from 'openai';
|
|
5
6
|
import { ChatCompletionCreateParamsNonStreaming } from 'openai/resources';
|
|
6
7
|
import { ChatCompletionCreateParamsBase, ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions';
|
|
7
8
|
|
|
@@ -35,54 +36,45 @@ type ConvertContextToJSONSchema<T extends ContextSchema> = {
|
|
|
35
36
|
additionalProperties: false;
|
|
36
37
|
};
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 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.
|
|
55
|
-
*
|
|
56
|
-
* @param openai The OpenAI instance to use.
|
|
57
|
-
* @param inputFn A function that maps arbitrary input to OpenAI chat completion input.
|
|
58
|
-
*/
|
|
59
|
-
declare function fromEventChoice<TInput>(openai: OpenAI, agentSettings: CreateAgentOutput<any>, inputFn: (input: TInput) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming, options?: {
|
|
60
|
-
/**
|
|
61
|
-
* Immediately execute sending the event to the parent actor.
|
|
62
|
-
* @default false
|
|
63
|
-
*/
|
|
64
|
-
execute?: boolean;
|
|
65
|
-
}): PromiseActorLogic<AnyEventObject[] | undefined, TInput>;
|
|
66
|
-
interface CreateAgentOutput<T extends {
|
|
39
|
+
declare function createSchemas<TContextSchema extends ContextSchema, TEventSchemas extends EventSchemas>({ context, events, }: {
|
|
40
|
+
context: TContextSchema;
|
|
41
|
+
events: TEventSchemas;
|
|
42
|
+
}): {
|
|
43
|
+
context: ConvertContextToJSONSchema<TContextSchema>;
|
|
44
|
+
events: ConvertToJSONSchemas<TEventSchemas>;
|
|
45
|
+
types: {
|
|
46
|
+
context: FromSchema<ConvertContextToJSONSchema<TContextSchema>>;
|
|
47
|
+
events: FromSchema<Values<ConvertToJSONSchemas<TEventSchemas>>>;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
declare function createAgent<T extends AnyStateMachine>(...args: Parameters<typeof createActor<T>>): xstate.Actor<T>;
|
|
52
|
+
|
|
53
|
+
interface OpenAIAdapterOutput<T extends {
|
|
67
54
|
model: ChatCompletionCreateParamsBase['model'];
|
|
68
|
-
context: ContextSchema;
|
|
69
|
-
events: EventSchemas;
|
|
70
55
|
}> {
|
|
71
56
|
model: T['model'];
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Determines which event to send to the parent state machine actor based on the prompt.
|
|
59
|
+
*/
|
|
60
|
+
fromEventChoice: <TInput>(inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming, options?: {
|
|
61
|
+
/**
|
|
62
|
+
* Immediately execute sending the event to the parent actor.
|
|
63
|
+
* @default true
|
|
64
|
+
*/
|
|
65
|
+
execute?: boolean;
|
|
66
|
+
}) => PromiseActorLogic<AnyEventObject[] | undefined, TInput>;
|
|
67
|
+
/**
|
|
68
|
+
* Creates promise actor logic that resolves with a chat completion.
|
|
69
|
+
*/
|
|
70
|
+
fromChat: <TInput>(inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming) => PromiseActorLogic<OpenAI.Chat.Completions.ChatCompletion, TInput>;
|
|
71
|
+
/**
|
|
72
|
+
* Creates observable actor logic that emits a chat completion stream.
|
|
73
|
+
*/
|
|
74
|
+
fromChatStream: <TInput>(inputFn: (input: TInput) => string | ChatCompletionCreateParamsStreaming) => ObservableActorLogic<OpenAI.Chat.Completions.ChatCompletionChunk, TInput>;
|
|
81
75
|
}
|
|
82
|
-
declare function
|
|
76
|
+
declare function createOpenAIAdapter<T extends {
|
|
83
77
|
model: ChatCompletionCreateParamsBase['model'];
|
|
84
|
-
|
|
85
|
-
events: EventSchemas;
|
|
86
|
-
}>(openai: OpenAI, settings: T): CreateAgentOutput<T>;
|
|
78
|
+
}>(openai: OpenAI, settings: T): OpenAIAdapterOutput<T>;
|
|
87
79
|
|
|
88
|
-
export { createAgent,
|
|
80
|
+
export { createAgent, createOpenAIAdapter, createSchemas };
|
package/dist/index.js
CHANGED
|
@@ -21,15 +21,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
createAgent: () => createAgent,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
fromEventChoice: () => fromEventChoice
|
|
24
|
+
createOpenAIAdapter: () => createOpenAIAdapter,
|
|
25
|
+
createSchemas: () => createSchemas
|
|
27
26
|
});
|
|
28
27
|
module.exports = __toCommonJS(src_exports);
|
|
29
28
|
|
|
30
|
-
// src/openai.ts
|
|
31
|
-
var import_xstate = require("xstate");
|
|
32
|
-
|
|
33
29
|
// src/utils.ts
|
|
34
30
|
function getAllTransitions(state) {
|
|
35
31
|
const nodes = state._nodes;
|
|
@@ -55,9 +51,34 @@ function createEventSchemas(eventSchemaMap) {
|
|
|
55
51
|
return resolvedEventSchemaMap;
|
|
56
52
|
}
|
|
57
53
|
|
|
58
|
-
// src/
|
|
54
|
+
// src/schemas.ts
|
|
55
|
+
function createSchemas({
|
|
56
|
+
context,
|
|
57
|
+
events
|
|
58
|
+
}) {
|
|
59
|
+
return {
|
|
60
|
+
context: {
|
|
61
|
+
type: "object",
|
|
62
|
+
properties: context,
|
|
63
|
+
additionalProperties: false,
|
|
64
|
+
required: Object.keys(context)
|
|
65
|
+
},
|
|
66
|
+
events: createEventSchemas(events),
|
|
67
|
+
types: {}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/agent.ts
|
|
72
|
+
var import_xstate = require("xstate");
|
|
73
|
+
function createAgent(...args) {
|
|
74
|
+
const [machine, options] = args;
|
|
75
|
+
return (0, import_xstate.createActor)(machine, options);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/adapters/openai.ts
|
|
79
|
+
var import_xstate2 = require("xstate");
|
|
59
80
|
function fromChatCompletion(openai, agentSettings, inputFn) {
|
|
60
|
-
return (0,
|
|
81
|
+
return (0, import_xstate2.fromPromise)(
|
|
61
82
|
async ({ input }) => {
|
|
62
83
|
const openAiInput = inputFn(input);
|
|
63
84
|
const params = typeof openAiInput === "string" ? {
|
|
@@ -74,8 +95,8 @@ function fromChatCompletion(openai, agentSettings, inputFn) {
|
|
|
74
95
|
}
|
|
75
96
|
);
|
|
76
97
|
}
|
|
77
|
-
function
|
|
78
|
-
return (0,
|
|
98
|
+
function fromChatStream(openai, agentSettings, inputFn) {
|
|
99
|
+
return (0, import_xstate2.fromObservable)(
|
|
79
100
|
({ input }) => {
|
|
80
101
|
const observers = /* @__PURE__ */ new Set();
|
|
81
102
|
(async () => {
|
|
@@ -101,7 +122,7 @@ function fromChatCompletionStream(openai, agentSettings, inputFn) {
|
|
|
101
122
|
})();
|
|
102
123
|
return {
|
|
103
124
|
subscribe: (...args) => {
|
|
104
|
-
const observer = (0,
|
|
125
|
+
const observer = (0, import_xstate2.toObserver)(...args);
|
|
105
126
|
observers.add(observer);
|
|
106
127
|
return {
|
|
107
128
|
unsubscribe: () => {
|
|
@@ -114,8 +135,14 @@ function fromChatCompletionStream(openai, agentSettings, inputFn) {
|
|
|
114
135
|
);
|
|
115
136
|
}
|
|
116
137
|
function fromEventChoice(openai, agentSettings, inputFn, options) {
|
|
117
|
-
return (0,
|
|
138
|
+
return (0, import_xstate2.fromPromise)(
|
|
118
139
|
async ({ input, self, system }) => {
|
|
140
|
+
const parentSnapshot = self._parent?.getSnapshot();
|
|
141
|
+
if (!parentSnapshot || !(0, import_xstate2.isMachineSnapshot)(parentSnapshot)) {
|
|
142
|
+
return void 0;
|
|
143
|
+
}
|
|
144
|
+
const schemas = parentSnapshot.machine.schemas;
|
|
145
|
+
const eventSchemaMap = schemas.events ?? {};
|
|
119
146
|
const transitions = getAllTransitions(self._parent.getSnapshot());
|
|
120
147
|
const functionNameMapping = {};
|
|
121
148
|
const tools = transitions.filter((t) => {
|
|
@@ -127,10 +154,10 @@ function fromEventChoice(openai, agentSettings, inputFn, options) {
|
|
|
127
154
|
type: "function",
|
|
128
155
|
function: {
|
|
129
156
|
name,
|
|
130
|
-
description: t.description ??
|
|
157
|
+
description: t.description ?? eventSchemaMap[t.eventType]?.description,
|
|
131
158
|
parameters: {
|
|
132
159
|
type: "object",
|
|
133
|
-
properties:
|
|
160
|
+
properties: eventSchemaMap[t.eventType]?.properties ?? {}
|
|
134
161
|
}
|
|
135
162
|
}
|
|
136
163
|
};
|
|
@@ -167,33 +194,21 @@ function fromEventChoice(openai, agentSettings, inputFn, options) {
|
|
|
167
194
|
}
|
|
168
195
|
);
|
|
169
196
|
}
|
|
170
|
-
function
|
|
197
|
+
function createOpenAIAdapter(openai, settings) {
|
|
171
198
|
const agentSettings = {
|
|
172
199
|
model: settings.model,
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
type: "object",
|
|
176
|
-
properties: settings.context,
|
|
177
|
-
additionalProperties: false
|
|
178
|
-
},
|
|
179
|
-
events: createEventSchemas(settings.events)
|
|
180
|
-
},
|
|
181
|
-
types: {},
|
|
182
|
-
fromEvent: (input) => (
|
|
183
|
-
// @ts-ignore
|
|
200
|
+
fromEventChoice: (input) => (
|
|
201
|
+
// @ts-ignore infinitely deep
|
|
184
202
|
fromEventChoice(openai, agentSettings, input, { execute: true })
|
|
185
203
|
),
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
fromChatCompletion: (input) => fromChatCompletion(openai, agentSettings, input),
|
|
189
|
-
fromChatCompletionStream: (input) => fromChatCompletionStream(openai, agentSettings, input)
|
|
204
|
+
fromChat: (input) => fromChatCompletion(openai, agentSettings, input),
|
|
205
|
+
fromChatStream: (input) => fromChatStream(openai, agentSettings, input)
|
|
190
206
|
};
|
|
191
207
|
return agentSettings;
|
|
192
208
|
}
|
|
193
209
|
// Annotate the CommonJS export names for ESM import in node:
|
|
194
210
|
0 && (module.exports = {
|
|
195
211
|
createAgent,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
fromEventChoice
|
|
212
|
+
createOpenAIAdapter,
|
|
213
|
+
createSchemas
|
|
199
214
|
});
|
|
@@ -3,7 +3,7 @@ import { fromPromise } from 'xstate';
|
|
|
3
3
|
export const getFromTerminal = fromPromise<string, string>(
|
|
4
4
|
async ({ input }) => {
|
|
5
5
|
const topic = await new Promise<string>((res) => {
|
|
6
|
-
console.log(input);
|
|
6
|
+
console.log(input + '\n');
|
|
7
7
|
const listener = (data: Buffer) => {
|
|
8
8
|
const result = data.toString().trim();
|
|
9
9
|
process.stdin.off('data', listener);
|
package/examples/joke.ts
CHANGED
|
@@ -1,21 +1,13 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
createActor,
|
|
5
|
-
fromCallback,
|
|
6
|
-
fromPromise,
|
|
7
|
-
log,
|
|
8
|
-
setup,
|
|
9
|
-
} from 'xstate';
|
|
10
|
-
import { createAgent } from '../src';
|
|
2
|
+
import { assign, fromCallback, fromPromise, log, setup } from 'xstate';
|
|
3
|
+
import { createAgent, createOpenAIAdapter, createSchemas } from '../src';
|
|
11
4
|
import { loadingAnimation } from './helpers/loader';
|
|
12
5
|
|
|
13
6
|
const openai = new OpenAI({
|
|
14
7
|
apiKey: process.env.OPENAI_API_KEY,
|
|
15
8
|
});
|
|
16
9
|
|
|
17
|
-
const
|
|
18
|
-
model: 'gpt-3.5-turbo-1106',
|
|
10
|
+
const schemas = createSchemas({
|
|
19
11
|
context: {
|
|
20
12
|
topic: { type: 'string' },
|
|
21
13
|
jokes: {
|
|
@@ -23,18 +15,35 @@ const agent = createAgent(openai, {
|
|
|
23
15
|
items: {
|
|
24
16
|
type: 'string',
|
|
25
17
|
},
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
},
|
|
19
|
+
desire: { type: ['string', 'null'] as const },
|
|
20
|
+
lastRating: { type: ['string', 'null'] as const },
|
|
21
|
+
},
|
|
22
|
+
events: {
|
|
23
|
+
askForTopic: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
topic: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
endJokes: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {},
|
|
28
34
|
},
|
|
29
35
|
},
|
|
30
|
-
events: {},
|
|
31
36
|
});
|
|
32
37
|
|
|
33
|
-
const
|
|
38
|
+
const adapter = createOpenAIAdapter(openai, {
|
|
39
|
+
model: 'gpt-3.5-turbo-1106',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const getJokeCompletion = adapter.fromChat(
|
|
34
43
|
(topic: string) => `Tell me a joke about ${topic}.`
|
|
35
44
|
);
|
|
36
45
|
|
|
37
|
-
const rateJoke =
|
|
46
|
+
const rateJoke = adapter.fromChat(
|
|
38
47
|
(joke: string) => `Rate this joke on a scale of 1 to 10: ${joke}`
|
|
39
48
|
);
|
|
40
49
|
|
|
@@ -52,7 +61,7 @@ const getTopic = fromPromise(async () => {
|
|
|
52
61
|
return topic;
|
|
53
62
|
});
|
|
54
63
|
|
|
55
|
-
const decide =
|
|
64
|
+
const decide = adapter.fromEventChoice(
|
|
56
65
|
(lastRating: string) =>
|
|
57
66
|
`Choose what to do next, given the previous rating of the joke: ${lastRating}`
|
|
58
67
|
);
|
|
@@ -96,15 +105,8 @@ const loader = fromCallback(({ input }: { input: string }) => {
|
|
|
96
105
|
});
|
|
97
106
|
|
|
98
107
|
const jokeMachine = setup({
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
topic: string;
|
|
102
|
-
jokes: string[];
|
|
103
|
-
desire: string | null;
|
|
104
|
-
lastRating: string | null;
|
|
105
|
-
},
|
|
106
|
-
input: {} as { topic: string },
|
|
107
|
-
},
|
|
108
|
+
schemas,
|
|
109
|
+
types: schemas.types,
|
|
108
110
|
actors: {
|
|
109
111
|
getJokeCompletion,
|
|
110
112
|
getTopic,
|
|
@@ -146,7 +148,7 @@ const jokeMachine = setup({
|
|
|
146
148
|
event.output.choices[0]!.message.content!
|
|
147
149
|
),
|
|
148
150
|
}),
|
|
149
|
-
log((x) => x.context.jokes.at(-1)),
|
|
151
|
+
log((x) => `\n` + x.context.jokes.at(-1)),
|
|
150
152
|
],
|
|
151
153
|
target: 'rateJoke',
|
|
152
154
|
},
|
|
@@ -168,7 +170,7 @@ const jokeMachine = setup({
|
|
|
168
170
|
lastRating: ({ event }) =>
|
|
169
171
|
event.output.choices[0]!.message.content!,
|
|
170
172
|
}),
|
|
171
|
-
log(({ context }) => context.lastRating),
|
|
173
|
+
log(({ context }) => '\n' + context.lastRating),
|
|
172
174
|
],
|
|
173
175
|
target: 'decide',
|
|
174
176
|
},
|
|
@@ -210,5 +212,5 @@ const jokeMachine = setup({
|
|
|
210
212
|
},
|
|
211
213
|
});
|
|
212
214
|
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
+
const agent = createAgent(jokeMachine);
|
|
216
|
+
agent.start();
|
|
File without changes
|
package/examples/ticTacToe.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { assign, setup, assertEvent
|
|
1
|
+
import { assign, setup, assertEvent } from 'xstate';
|
|
2
2
|
import OpenAI from 'openai';
|
|
3
|
-
import { createAgent } from '../src
|
|
3
|
+
import { createOpenAIAdapter, createSchemas, createAgent } from '../src';
|
|
4
4
|
|
|
5
5
|
const openai = new OpenAI({
|
|
6
6
|
apiKey: process.env.OPENAI_API_KEY,
|
|
@@ -8,8 +8,7 @@ const openai = new OpenAI({
|
|
|
8
8
|
|
|
9
9
|
type Player = 'x' | 'o';
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
model: 'gpt-4-1106-preview',
|
|
11
|
+
const schemas = createSchemas({
|
|
13
12
|
context: {
|
|
14
13
|
board: {
|
|
15
14
|
type: 'array',
|
|
@@ -69,16 +68,20 @@ const agent = createAgent(openai, {
|
|
|
69
68
|
},
|
|
70
69
|
});
|
|
71
70
|
|
|
71
|
+
const adapter = createOpenAIAdapter(openai, {
|
|
72
|
+
model: 'gpt-4-1106-preview',
|
|
73
|
+
});
|
|
74
|
+
|
|
72
75
|
const initialContext = {
|
|
73
76
|
board: Array(9).fill(null) as Array<Player | null>,
|
|
74
77
|
moves: 0,
|
|
75
78
|
player: 'x' as Player,
|
|
76
79
|
gameReport: '',
|
|
77
80
|
events: [],
|
|
78
|
-
} satisfies typeof
|
|
81
|
+
} satisfies typeof schemas.types.context;
|
|
79
82
|
|
|
80
|
-
const bot =
|
|
81
|
-
({ context }: { context: typeof
|
|
83
|
+
const bot = adapter.fromEventChoice(
|
|
84
|
+
({ context }: { context: typeof schemas.types.context }) => `
|
|
82
85
|
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.
|
|
83
86
|
|
|
84
87
|
${JSON.stringify(context, null, 2)}
|
|
@@ -86,11 +89,11 @@ ${JSON.stringify(context, null, 2)}
|
|
|
86
89
|
Execute the single best next move to try to win the game. Do not play on an existing cell.`
|
|
87
90
|
);
|
|
88
91
|
|
|
89
|
-
const gameReporter =
|
|
92
|
+
const gameReporter = adapter.fromChatStream(
|
|
90
93
|
({
|
|
91
94
|
context,
|
|
92
95
|
}: {
|
|
93
|
-
context: typeof
|
|
96
|
+
context: typeof schemas.types.context;
|
|
94
97
|
}) => `Here is the game board:
|
|
95
98
|
|
|
96
99
|
${JSON.stringify(context.board, null, 2)}
|
|
@@ -124,7 +127,8 @@ function getWinner(board: typeof initialContext.board): Player | null {
|
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
export const ticTacToeMachine = setup({
|
|
127
|
-
|
|
130
|
+
schemas,
|
|
131
|
+
types: schemas.types,
|
|
128
132
|
actors: {
|
|
129
133
|
bot,
|
|
130
134
|
gameReporter,
|
|
@@ -248,14 +252,8 @@ export const ticTacToeMachine = setup({
|
|
|
248
252
|
},
|
|
249
253
|
});
|
|
250
254
|
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
if (e.type === '@xstate.event') {
|
|
254
|
-
console.log(e.event);
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
});
|
|
258
|
-
actor.subscribe((s) => {
|
|
255
|
+
const agent = createAgent(ticTacToeMachine);
|
|
256
|
+
agent.subscribe((s) => {
|
|
259
257
|
console.log(s.value, s.context);
|
|
260
258
|
});
|
|
261
|
-
|
|
259
|
+
agent.start();
|
package/examples/weather.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import { createAgent,
|
|
3
|
-
import { assign,
|
|
2
|
+
import { createAgent, createOpenAIAdapter, createSchemas } from '../src';
|
|
3
|
+
import { assign, fromPromise, log, setup } from 'xstate';
|
|
4
4
|
import { getFromTerminal } from './helpers/helpers';
|
|
5
5
|
|
|
6
6
|
async function searchTavily(
|
|
@@ -23,6 +23,7 @@ async function searchTavily(
|
|
|
23
23
|
},
|
|
24
24
|
body: JSON.stringify(body),
|
|
25
25
|
});
|
|
26
|
+
|
|
26
27
|
const json = await response.json();
|
|
27
28
|
if (!response.ok) {
|
|
28
29
|
throw new Error(
|
|
@@ -39,8 +40,7 @@ const openai = new OpenAI({
|
|
|
39
40
|
apiKey: process.env.OPENAI_API_KEY,
|
|
40
41
|
});
|
|
41
42
|
|
|
42
|
-
const
|
|
43
|
-
model: 'gpt-4-1106-preview',
|
|
43
|
+
const schemas = createSchemas({
|
|
44
44
|
context: {
|
|
45
45
|
location: { type: 'string' },
|
|
46
46
|
history: { type: 'array', items: { type: 'string' } },
|
|
@@ -64,17 +64,27 @@ const agent = createAgent(openai, {
|
|
|
64
64
|
},
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
+
const adapter = createOpenAIAdapter(openai, {
|
|
68
|
+
model: 'gpt-4-1106-preview',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const getWeather = fromPromise(async ({ input }: { input: string }) => {
|
|
72
|
+
const results = await searchTavily(
|
|
73
|
+
`Get the weather for this location: ${input}`,
|
|
74
|
+
{
|
|
75
|
+
maxResults: 5,
|
|
76
|
+
apiKey: process.env.TAVILY_API_KEY!,
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
return results;
|
|
80
|
+
});
|
|
81
|
+
|
|
67
82
|
const machine = setup({
|
|
68
|
-
|
|
83
|
+
schemas,
|
|
84
|
+
types: schemas.types,
|
|
69
85
|
actors: {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
maxResults: 5,
|
|
73
|
-
apiKey: process.env.TAVILY_API_KEY!,
|
|
74
|
-
});
|
|
75
|
-
return results;
|
|
76
|
-
}),
|
|
77
|
-
decide: agent.fromEvent(
|
|
86
|
+
getWeather,
|
|
87
|
+
decide: adapter.fromEventChoice(
|
|
78
88
|
(input: string) =>
|
|
79
89
|
`Decide what to do based on the given input, which may or may not be a location: ${input}`
|
|
80
90
|
),
|
|
@@ -121,9 +131,8 @@ const machine = setup({
|
|
|
121
131
|
gettingWeather: {
|
|
122
132
|
entry: log('Getting weather...'),
|
|
123
133
|
invoke: {
|
|
124
|
-
src: '
|
|
125
|
-
input: ({ context }) =>
|
|
126
|
-
`Get the weather for this location: ${context.location}`,
|
|
134
|
+
src: 'getWeather',
|
|
135
|
+
input: ({ context }) => context.location,
|
|
127
136
|
onDone: {
|
|
128
137
|
actions: [
|
|
129
138
|
log(({ event }) => event.output),
|
|
@@ -144,4 +153,8 @@ const machine = setup({
|
|
|
144
153
|
},
|
|
145
154
|
});
|
|
146
155
|
|
|
147
|
-
|
|
156
|
+
createAgent(machine, {
|
|
157
|
+
input: {
|
|
158
|
+
location: 'New York',
|
|
159
|
+
},
|
|
160
|
+
}).start();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"xstate": "^5.
|
|
26
|
+
"xstate": "^5.6.0"
|
|
27
27
|
},
|
|
28
28
|
"packageManager": "pnpm@8.11.0",
|
|
29
29
|
"scripts": {
|
|
@@ -4,21 +4,12 @@ import {
|
|
|
4
4
|
ObservableActorLogic,
|
|
5
5
|
Observer,
|
|
6
6
|
PromiseActorLogic,
|
|
7
|
-
Values,
|
|
8
7
|
fromObservable,
|
|
9
8
|
fromPromise,
|
|
10
|
-
|
|
9
|
+
isMachineSnapshot,
|
|
11
10
|
toObserver,
|
|
12
11
|
} from 'xstate';
|
|
13
|
-
import { getAllTransitions } from '
|
|
14
|
-
import {
|
|
15
|
-
ContextSchema,
|
|
16
|
-
EventSchemas,
|
|
17
|
-
ConvertContextToJSONSchema,
|
|
18
|
-
ConvertToJSONSchemas,
|
|
19
|
-
createEventSchemas,
|
|
20
|
-
} from './utils';
|
|
21
|
-
import { FromSchema } from 'json-schema-to-ts';
|
|
12
|
+
import { getAllTransitions } from '../utils';
|
|
22
13
|
import { ChatCompletionCreateParamsNonStreaming } from 'openai/resources';
|
|
23
14
|
import {
|
|
24
15
|
ChatCompletionCreateParamsBase,
|
|
@@ -34,7 +25,7 @@ import {
|
|
|
34
25
|
*/
|
|
35
26
|
export function fromChatCompletion<TInput>(
|
|
36
27
|
openai: OpenAI,
|
|
37
|
-
agentSettings:
|
|
28
|
+
agentSettings: OpenAIAdapterOutput<any>,
|
|
38
29
|
inputFn: (
|
|
39
30
|
input: TInput
|
|
40
31
|
) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming
|
|
@@ -67,9 +58,9 @@ export function fromChatCompletion<TInput>(
|
|
|
67
58
|
* @param openai The OpenAI instance to use.
|
|
68
59
|
* @param inputFn A function that maps arbitrary input to OpenAI chat completion input.
|
|
69
60
|
*/
|
|
70
|
-
export function
|
|
61
|
+
export function fromChatStream<TInput>(
|
|
71
62
|
openai: OpenAI,
|
|
72
|
-
agentSettings:
|
|
63
|
+
agentSettings: OpenAIAdapterOutput<any>,
|
|
73
64
|
inputFn: (
|
|
74
65
|
input: TInput
|
|
75
66
|
) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming
|
|
@@ -128,7 +119,7 @@ export function fromChatCompletionStream<TInput>(
|
|
|
128
119
|
*/
|
|
129
120
|
export function fromEventChoice<TInput>(
|
|
130
121
|
openai: OpenAI,
|
|
131
|
-
agentSettings:
|
|
122
|
+
agentSettings: OpenAIAdapterOutput<any>,
|
|
132
123
|
inputFn: (
|
|
133
124
|
input: TInput
|
|
134
125
|
) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming,
|
|
@@ -142,6 +133,15 @@ export function fromEventChoice<TInput>(
|
|
|
142
133
|
) {
|
|
143
134
|
return fromPromise<AnyEventObject[] | undefined, TInput>(
|
|
144
135
|
async ({ input, self, system }) => {
|
|
136
|
+
const parentSnapshot = self._parent?.getSnapshot();
|
|
137
|
+
|
|
138
|
+
if (!parentSnapshot || !isMachineSnapshot(parentSnapshot)) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const schemas = parentSnapshot.machine.schemas as any;
|
|
143
|
+
const eventSchemaMap = schemas.events ?? {};
|
|
144
|
+
|
|
145
145
|
const transitions = getAllTransitions(self._parent!.getSnapshot());
|
|
146
146
|
const functionNameMapping: Record<string, string> = {};
|
|
147
147
|
const tools = transitions
|
|
@@ -156,12 +156,10 @@ export function fromEventChoice<TInput>(
|
|
|
156
156
|
function: {
|
|
157
157
|
name,
|
|
158
158
|
description:
|
|
159
|
-
t.description ??
|
|
160
|
-
agentSettings.schemas.events[t.eventType]?.description,
|
|
159
|
+
t.description ?? eventSchemaMap[t.eventType]?.description,
|
|
161
160
|
parameters: {
|
|
162
161
|
type: 'object',
|
|
163
|
-
properties:
|
|
164
|
-
agentSettings.schemas.events[t.eventType]?.properties ?? {},
|
|
162
|
+
properties: eventSchemaMap[t.eventType]?.properties ?? {},
|
|
165
163
|
},
|
|
166
164
|
},
|
|
167
165
|
} as const;
|
|
@@ -208,35 +206,35 @@ export function fromEventChoice<TInput>(
|
|
|
208
206
|
);
|
|
209
207
|
}
|
|
210
208
|
|
|
211
|
-
interface
|
|
209
|
+
interface OpenAIAdapterOutput<
|
|
212
210
|
T extends {
|
|
213
211
|
model: ChatCompletionCreateParamsBase['model'];
|
|
214
|
-
context: ContextSchema;
|
|
215
|
-
events: EventSchemas;
|
|
216
212
|
}
|
|
217
213
|
> {
|
|
218
214
|
model: T['model'];
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
events: FromSchema<Values<ConvertToJSONSchemas<T['events']>>>;
|
|
223
|
-
};
|
|
224
|
-
fromEvent: <TInput>(
|
|
225
|
-
inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming
|
|
226
|
-
) => PromiseActorLogic<
|
|
227
|
-
FromSchema<Values<ConvertToJSONSchemas<T['events']>>>[] | undefined,
|
|
228
|
-
TInput
|
|
229
|
-
>;
|
|
215
|
+
/**
|
|
216
|
+
* Determines which event to send to the parent state machine actor based on the prompt.
|
|
217
|
+
*/
|
|
230
218
|
fromEventChoice: <TInput>(
|
|
231
|
-
inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
219
|
+
inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming,
|
|
220
|
+
options?: {
|
|
221
|
+
/**
|
|
222
|
+
* Immediately execute sending the event to the parent actor.
|
|
223
|
+
* @default true
|
|
224
|
+
*/
|
|
225
|
+
execute?: boolean;
|
|
226
|
+
}
|
|
227
|
+
) => PromiseActorLogic<AnyEventObject[] | undefined, TInput>;
|
|
228
|
+
/**
|
|
229
|
+
* Creates promise actor logic that resolves with a chat completion.
|
|
230
|
+
*/
|
|
231
|
+
fromChat: <TInput>(
|
|
237
232
|
inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming
|
|
238
233
|
) => PromiseActorLogic<OpenAI.Chat.Completions.ChatCompletion, TInput>;
|
|
239
|
-
|
|
234
|
+
/**
|
|
235
|
+
* Creates observable actor logic that emits a chat completion stream.
|
|
236
|
+
*/
|
|
237
|
+
fromChatStream: <TInput>(
|
|
240
238
|
inputFn: (input: TInput) => string | ChatCompletionCreateParamsStreaming
|
|
241
239
|
) => ObservableActorLogic<
|
|
242
240
|
OpenAI.Chat.Completions.ChatCompletionChunk,
|
|
@@ -244,34 +242,19 @@ interface CreateAgentOutput<
|
|
|
244
242
|
>;
|
|
245
243
|
}
|
|
246
244
|
|
|
247
|
-
export function
|
|
245
|
+
export function createOpenAIAdapter<
|
|
248
246
|
T extends {
|
|
249
247
|
model: ChatCompletionCreateParamsBase['model'];
|
|
250
|
-
context: ContextSchema;
|
|
251
|
-
events: EventSchemas;
|
|
252
248
|
}
|
|
253
|
-
>(openai: OpenAI, settings: T):
|
|
254
|
-
const agentSettings:
|
|
249
|
+
>(openai: OpenAI, settings: T): OpenAIAdapterOutput<T> {
|
|
250
|
+
const agentSettings: OpenAIAdapterOutput<T> = {
|
|
255
251
|
model: settings.model,
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
},
|
|
262
|
-
events: createEventSchemas(settings.events),
|
|
263
|
-
} as any,
|
|
264
|
-
types: {} as any,
|
|
265
|
-
fromEvent: (input) =>
|
|
266
|
-
// @ts-ignore
|
|
267
|
-
fromEventChoice(openai, agentSettings, input, { execute: true }),
|
|
268
|
-
// @ts-ignore infinitely deep
|
|
269
|
-
fromEventChoice: (input) => fromEventChoice(openai, agentSettings, input),
|
|
270
|
-
fromChatCompletion: (input) =>
|
|
271
|
-
fromChatCompletion(openai, agentSettings, input),
|
|
272
|
-
fromChatCompletionStream: (input) =>
|
|
273
|
-
fromChatCompletionStream(openai, agentSettings, input),
|
|
252
|
+
fromEventChoice: (input) =>
|
|
253
|
+
// @ts-ignore infinitely deep
|
|
254
|
+
fromEventChoice(openai, agentSettings, input, { execute: true }) as any,
|
|
255
|
+
fromChat: (input) => fromChatCompletion(openai, agentSettings, input),
|
|
256
|
+
fromChatStream: (input) => fromChatStream(openai, agentSettings, input),
|
|
274
257
|
};
|
|
275
258
|
|
|
276
|
-
return agentSettings
|
|
259
|
+
return agentSettings;
|
|
277
260
|
}
|
package/src/agent.ts
ADDED
package/src/index.ts
CHANGED
package/src/schemas.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Values } from 'xstate';
|
|
2
|
+
import {
|
|
3
|
+
ContextSchema,
|
|
4
|
+
EventSchemas,
|
|
5
|
+
ConvertContextToJSONSchema,
|
|
6
|
+
ConvertToJSONSchemas,
|
|
7
|
+
createEventSchemas,
|
|
8
|
+
} from './utils';
|
|
9
|
+
import { FromSchema } from 'json-schema-to-ts';
|
|
10
|
+
|
|
11
|
+
export function createSchemas<
|
|
12
|
+
TContextSchema extends ContextSchema,
|
|
13
|
+
TEventSchemas extends EventSchemas
|
|
14
|
+
>({
|
|
15
|
+
context,
|
|
16
|
+
events,
|
|
17
|
+
}: {
|
|
18
|
+
context: TContextSchema;
|
|
19
|
+
events: TEventSchemas;
|
|
20
|
+
}): {
|
|
21
|
+
context: ConvertContextToJSONSchema<TContextSchema>;
|
|
22
|
+
events: ConvertToJSONSchemas<TEventSchemas>;
|
|
23
|
+
types: {
|
|
24
|
+
context: FromSchema<ConvertContextToJSONSchema<TContextSchema>>;
|
|
25
|
+
events: FromSchema<Values<ConvertToJSONSchemas<TEventSchemas>>>;
|
|
26
|
+
};
|
|
27
|
+
} {
|
|
28
|
+
return {
|
|
29
|
+
context: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: context,
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
required: Object.keys(context),
|
|
34
|
+
},
|
|
35
|
+
events: createEventSchemas(events),
|
|
36
|
+
types: {} as any,
|
|
37
|
+
};
|
|
38
|
+
}
|