@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 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 OpenAI from 'openai';
2
- import { Prop, PromiseActorLogic, ObservableActorLogic, AnyEventObject, Values } from 'xstate';
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
- * Creates [promise actor logic](https://stately.ai/docs/promise-actors) that uses the OpenAI API to generate a completion.
40
- *
41
- * @param openai The OpenAI instance.
42
- * @param inputFn A function that maps arbitrary input to OpenAI chat completion input.
43
- *
44
- */
45
- declare function fromChatCompletion<TInput>(openai: OpenAI, agentSettings: CreateAgentOutput<any>, inputFn: (input: TInput) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming): PromiseActorLogic<OpenAI.Chat.Completions.ChatCompletion, TInput>;
46
- /**
47
- * Creates [observable actor logic](https://stately.ai/docs/observable-actors) that uses the OpenAI API to generate a completion stream.
48
- *
49
- * @param openai The OpenAI instance to use.
50
- * @param inputFn A function that maps arbitrary input to OpenAI chat completion input.
51
- */
52
- declare function fromChatCompletionStream<TInput>(openai: OpenAI, agentSettings: CreateAgentOutput<any>, inputFn: (input: TInput) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming): ObservableActorLogic<OpenAI.Chat.Completions.ChatCompletionChunk, TInput>;
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
- schemas: T;
73
- types: {
74
- context: FromSchema<ConvertContextToJSONSchema<T['context']>>;
75
- events: FromSchema<Values<ConvertToJSONSchemas<T['events']>>>;
76
- };
77
- fromEvent: <TInput>(inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming) => PromiseActorLogic<FromSchema<Values<ConvertToJSONSchemas<T['events']>>>[] | undefined, TInput>;
78
- fromEventChoice: <TInput>(inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming) => PromiseActorLogic<FromSchema<Values<ConvertToJSONSchemas<T['events']>>>[] | undefined, TInput>;
79
- fromChatCompletion: <TInput>(inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming) => PromiseActorLogic<OpenAI.Chat.Completions.ChatCompletion, TInput>;
80
- fromChatCompletionStream: <TInput>(inputFn: (input: TInput) => string | ChatCompletionCreateParamsStreaming) => ObservableActorLogic<OpenAI.Chat.Completions.ChatCompletionChunk, TInput>;
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 createAgent<T extends {
76
+ declare function createOpenAIAdapter<T extends {
83
77
  model: ChatCompletionCreateParamsBase['model'];
84
- context: ContextSchema;
85
- events: EventSchemas;
86
- }>(openai: OpenAI, settings: T): CreateAgentOutput<T>;
78
+ }>(openai: OpenAI, settings: T): OpenAIAdapterOutput<T>;
87
79
 
88
- export { createAgent, fromChatCompletion, fromChatCompletionStream, fromEventChoice };
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
- fromChatCompletion: () => fromChatCompletion,
25
- fromChatCompletionStream: () => fromChatCompletionStream,
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/openai.ts
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, import_xstate.fromPromise)(
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 fromChatCompletionStream(openai, agentSettings, inputFn) {
78
- return (0, import_xstate.fromObservable)(
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, import_xstate.toObserver)(...args);
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, import_xstate.fromPromise)(
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 ?? agentSettings.schemas.events[t.eventType]?.description,
157
+ description: t.description ?? eventSchemaMap[t.eventType]?.description,
131
158
  parameters: {
132
159
  type: "object",
133
- properties: agentSettings.schemas.events[t.eventType]?.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 createAgent(openai, settings) {
197
+ function createOpenAIAdapter(openai, settings) {
171
198
  const agentSettings = {
172
199
  model: settings.model,
173
- schemas: {
174
- context: {
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
- // @ts-ignore infinitely deep
187
- fromEventChoice: (input) => fromEventChoice(openai, agentSettings, input),
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
- fromChatCompletion,
197
- fromChatCompletionStream,
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
- assign,
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 agent = createAgent(openai, {
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
- desire: { type: ['string', 'null'] },
27
- lastRating: { type: ['string', 'null'] },
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 getJokeCompletion = agent.fromChatCompletion(
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 = agent.fromChatCompletion(
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 = agent.fromEvent(
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
- types: {
100
- context: {} as {
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 actor = createActor(jokeMachine);
214
- actor.start();
215
+ const agent = createAgent(jokeMachine);
216
+ agent.start();
File without changes
@@ -1,6 +1,6 @@
1
- import { assign, setup, assertEvent, createActor } from 'xstate';
1
+ import { assign, setup, assertEvent } from 'xstate';
2
2
  import OpenAI from 'openai';
3
- import { createAgent } from '../src/openai';
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 agent = createAgent(openai, {
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 agent.types.context;
81
+ } satisfies typeof schemas.types.context;
79
82
 
80
- const bot = agent.fromEvent(
81
- ({ context }: { context: typeof agent.types.context }) => `
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 = agent.fromChatCompletionStream(
92
+ const gameReporter = adapter.fromChatStream(
90
93
  ({
91
94
  context,
92
95
  }: {
93
- context: typeof agent.types.context;
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
- types: agent.types,
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 actor = createActor(ticTacToeMachine, {
252
- inspect: (e) => {
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
- actor.start();
259
+ agent.start();
@@ -1,6 +1,6 @@
1
1
  import OpenAI from 'openai';
2
- import { createAgent, fromEventChoice } from '../src';
3
- import { assign, createActor, fromPromise, log, setup } from 'xstate';
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 agent = createAgent(openai, {
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
- types: agent.types,
83
+ schemas,
84
+ types: schemas.types,
69
85
  actors: {
70
- searchTavily: fromPromise(async ({ input }: { input: string }) => {
71
- const results = await searchTavily(input, {
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: 'searchTavily',
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
- createActor(machine).start();
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",
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.5.1"
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
- setup,
9
+ isMachineSnapshot,
11
10
  toObserver,
12
11
  } from 'xstate';
13
- import { getAllTransitions } from './utils';
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: CreateAgentOutput<any>,
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 fromChatCompletionStream<TInput>(
61
+ export function fromChatStream<TInput>(
71
62
  openai: OpenAI,
72
- agentSettings: CreateAgentOutput<any>,
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: CreateAgentOutput<any>,
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 CreateAgentOutput<
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
- schemas: T;
220
- types: {
221
- context: FromSchema<ConvertContextToJSONSchema<T['context']>>;
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
- ) => PromiseActorLogic<
233
- FromSchema<Values<ConvertToJSONSchemas<T['events']>>>[] | undefined,
234
- TInput
235
- >;
236
- fromChatCompletion: <TInput>(
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
- fromChatCompletionStream: <TInput>(
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 createAgent<
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): CreateAgentOutput<T> {
254
- const agentSettings: CreateAgentOutput<T> = {
249
+ >(openai: OpenAI, settings: T): OpenAIAdapterOutput<T> {
250
+ const agentSettings: OpenAIAdapterOutput<T> = {
255
251
  model: settings.model,
256
- schemas: {
257
- context: {
258
- type: 'object',
259
- properties: settings.context,
260
- additionalProperties: false,
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 as any;
259
+ return agentSettings;
277
260
  }
package/src/agent.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { ActorOptions, AnyStateMachine, createActor } from 'xstate';
2
+
3
+ export function createAgent<T extends AnyStateMachine>(
4
+ ...args: Parameters<typeof createActor<T>>
5
+ ) {
6
+ const [machine, options] = args;
7
+ return createActor(machine, options);
8
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,3 @@
1
- export {
2
- fromChatCompletion,
3
- fromChatCompletionStream,
4
- fromEventChoice,
5
- createAgent,
6
- } from './openai';
1
+ export { createSchemas } from './schemas';
2
+ export { createAgent } from './agent';
3
+ export { createOpenAIAdapter } from './adapters/openai';
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
+ }