@statelyai/agent 0.0.8 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.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 +135 -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 +29 -23
- 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/package.json
CHANGED
|
@@ -1,41 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
10
|
+
"lint": "tsc --noEmit",
|
|
11
|
+
"test": "vitest",
|
|
12
|
+
"example": "ts-node examples/helpers/runner.ts",
|
|
13
|
+
"prepublishOnly": "tsup src/index.ts --dts",
|
|
14
|
+
"changeset": "changeset",
|
|
15
|
+
"release": "changeset publish",
|
|
16
|
+
"version": "changeset version"
|
|
17
|
+
},
|
|
8
18
|
"keywords": [],
|
|
9
19
|
"author": "",
|
|
10
20
|
"license": "MIT",
|
|
11
21
|
"devDependencies": {
|
|
12
22
|
"@changesets/changelog-github": "^0.5.0",
|
|
13
|
-
"@changesets/cli": "^2.27.
|
|
14
|
-
"@
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
23
|
+
"@changesets/cli": "^2.27.5",
|
|
24
|
+
"@langchain/community": "^0.0.53",
|
|
25
|
+
"@langchain/core": "^0.1.63",
|
|
26
|
+
"@langchain/openai": "^0.0.28",
|
|
27
|
+
"@types/node": "^20.14.2",
|
|
28
|
+
"dotenv": "^16.4.5",
|
|
29
|
+
"json-schema-to-ts": "^3.1.0",
|
|
18
30
|
"ts-node": "^10.9.2",
|
|
19
|
-
"tsup": "^8.0
|
|
20
|
-
"typescript": "^5.
|
|
21
|
-
"vitest": "^1.
|
|
22
|
-
"
|
|
23
|
-
"zod
|
|
31
|
+
"tsup": "^8.1.0",
|
|
32
|
+
"typescript": "^5.4.5",
|
|
33
|
+
"vitest": "^1.6.0",
|
|
34
|
+
"wikipedia": "^2.1.2",
|
|
35
|
+
"zod": "^3.23.8"
|
|
24
36
|
},
|
|
25
37
|
"publishConfig": {
|
|
26
38
|
"access": "public"
|
|
27
39
|
},
|
|
28
40
|
"dependencies": {
|
|
29
|
-
"
|
|
41
|
+
"@ai-sdk/openai": "^0.0.13",
|
|
42
|
+
"@xstate/graph": "^2.0.0",
|
|
43
|
+
"ai": "^3.1.32",
|
|
44
|
+
"xstate": "^5.13.2"
|
|
30
45
|
},
|
|
31
|
-
"packageManager": "pnpm@8.11.0"
|
|
32
|
-
|
|
33
|
-
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
34
|
-
"lint": "tsc --noEmit",
|
|
35
|
-
"test": "vitest",
|
|
36
|
-
"example": "ts-node examples/helpers/runner.ts",
|
|
37
|
-
"changeset": "changeset",
|
|
38
|
-
"release": "changeset publish",
|
|
39
|
-
"version": "changeset version"
|
|
40
|
-
}
|
|
41
|
-
}
|
|
46
|
+
"packageManager": "pnpm@8.11.0"
|
|
47
|
+
}
|
package/readme.md
CHANGED
|
@@ -1,42 +1,13 @@
|
|
|
1
|
-
# Stately Agent
|
|
1
|
+
# Stately Agent
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> Alpha software! Not ready for production yet.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Stately Agent is a flexible framework for building AI agents using state machines. Stately agents go beyond normal LLM-based AI agents by:
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
- Using state machines to guide the agent's behavior, powered by [XState](https://stately.ai/docs/xstate)
|
|
9
|
+
- Incorporating **observations**, **message history**, and **feedback** to the agent decision-making and text-generation processes, as needed
|
|
10
|
+
- Enabling custom **planning** abilities for agents to achieve specific goals based on state machine logic, observations, and feedback
|
|
11
|
+
- Wrapping the [Vercel AI SDK](https://sdk.vercel.ai/) to easily support multiple model providers, such as OpenAI, Anthropic, Google, Mistral, Groq, Perplexity, and more
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
pnpm install openai @statelyai/agent
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Usage
|
|
15
|
-
|
|
16
|
-
Work in progress. For now, see the examples:
|
|
17
|
-
|
|
18
|
-
- [Joke generator](https://github.com/statelyai/agent/tree/main/examples/joke.ts)
|
|
19
|
-
- Demonstrates `agent.fromChatCompletion(...)` to generate a joke and provide a joke rating
|
|
20
|
-
- Demonstrates `agent.fromEvent(...)` to choose whether to keep generating jokes or stop
|
|
21
|
-
- [Tic-tac-toe](https://github.com/statelyai/agent/tree/main/examples/ticTacToe.ts)
|
|
22
|
-
- Demonstrates `agent.fromEvent(...)` to have an agent play itself in a game of tic-tac-toe with precise events
|
|
23
|
-
- Demonstrates `agent.fromChatCompletionStream(...)` to produce a game report at the end of the game
|
|
24
|
-
- [Weather](https://github.com/statelyai/agent/tree/main/examples/weather.ts)
|
|
25
|
-
- Demonstrates using [Tavily](https://tavily.com/) as an external API
|
|
26
|
-
- Demonstrates `agent.fromEvent(...)` to only use Tavily to get the weather if the user provides a valid location
|
|
27
|
-
|
|
28
|
-
## Examples
|
|
29
|
-
|
|
30
|
-
First, clone this repo locally. To run the examples in this repo, create a `.env` file at the root of the repo with the following contents:
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
OPENAI_API_KEY="your-openai-api-key"
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Then, install the dependencies (`npm install`) and run the examples:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
pnpm run example joke
|
|
40
|
-
# or:
|
|
41
|
-
# pnpm run example ticTacToe
|
|
42
|
-
```
|
|
13
|
+
_Documentation coming soon_
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// // type AgentExperiences<TState, TReward> = Record<
|
|
2
|
+
// // string, // serialized state
|
|
3
|
+
// // Record<
|
|
4
|
+
// // string, // serialized event
|
|
5
|
+
// // {
|
|
6
|
+
// // state: TState;
|
|
7
|
+
// // reward: TReward;
|
|
8
|
+
// // }
|
|
9
|
+
// // >
|
|
10
|
+
|
|
11
|
+
// import OpenAI from 'openai';
|
|
12
|
+
// import {
|
|
13
|
+
// AnyEventObject,
|
|
14
|
+
// EventObject,
|
|
15
|
+
// AnyActorLogic,
|
|
16
|
+
// AnyMachineSnapshot,
|
|
17
|
+
// ActorRef,
|
|
18
|
+
// AnyActorRef,
|
|
19
|
+
// SnapshotFrom,
|
|
20
|
+
// EventFrom,
|
|
21
|
+
// } from 'xstate';
|
|
22
|
+
// import { getToolCalls } from './adapters/openai';
|
|
23
|
+
// import { ZodEventMapping } from './schemas';
|
|
24
|
+
|
|
25
|
+
// // >;
|
|
26
|
+
// interface AgentExperience<TState, TEvent extends AnyEventObject> {
|
|
27
|
+
// prevState: TState | undefined;
|
|
28
|
+
// event: TEvent;
|
|
29
|
+
// nextState: TState;
|
|
30
|
+
// }
|
|
31
|
+
|
|
32
|
+
// type AgentPlan<TState, TEvent extends EventObject> = Array<{
|
|
33
|
+
// /**
|
|
34
|
+
// * The current state
|
|
35
|
+
// */
|
|
36
|
+
// state: TState;
|
|
37
|
+
// /**
|
|
38
|
+
// * The event to execute
|
|
39
|
+
// */
|
|
40
|
+
// event: TEvent;
|
|
41
|
+
// /**
|
|
42
|
+
// * The expected next state
|
|
43
|
+
// */
|
|
44
|
+
// nextState?: TState;
|
|
45
|
+
// }>;
|
|
46
|
+
|
|
47
|
+
// interface AgentModel<
|
|
48
|
+
// // TLogic extends AnyActorLogic,
|
|
49
|
+
// TReward,
|
|
50
|
+
// TState,
|
|
51
|
+
// TEvent extends EventObject
|
|
52
|
+
// > {
|
|
53
|
+
// // policy: ({
|
|
54
|
+
// // logic,
|
|
55
|
+
// // state,
|
|
56
|
+
// // goal,
|
|
57
|
+
// // }: {
|
|
58
|
+
// // logic: TLogic;
|
|
59
|
+
// // state: TState;
|
|
60
|
+
// // goal: string;
|
|
61
|
+
// // }) => Promise<AgentPlan<TState>>;
|
|
62
|
+
// getExperiences: () => Promise<Array<AgentExperience<TState, TEvent>>>; // TODO: TLogic instead?
|
|
63
|
+
// addExperience: (experience: AgentExperience<TState, TEvent>) => void;
|
|
64
|
+
// getLogic: ({
|
|
65
|
+
// experiences,
|
|
66
|
+
// }: {
|
|
67
|
+
// experiences: Array<AgentExperience<TState, TEvent>>; // TODO: TLogic instead?
|
|
68
|
+
// }) => Promise<AnyActorLogic>;
|
|
69
|
+
// getNextEvents: ({
|
|
70
|
+
// logic,
|
|
71
|
+
// state,
|
|
72
|
+
// }: {
|
|
73
|
+
// logic: AnyActorLogic;
|
|
74
|
+
// state: TState;
|
|
75
|
+
// }) => Promise<AnyEventObject[]>;
|
|
76
|
+
// getPlans: ({
|
|
77
|
+
// logic,
|
|
78
|
+
// state,
|
|
79
|
+
// goals,
|
|
80
|
+
// }: {
|
|
81
|
+
// logic: AnyActorLogic;
|
|
82
|
+
// state: TState;
|
|
83
|
+
// goals: string[];
|
|
84
|
+
// }) => Promise<Array<AgentPlan<TState, TEvent>>>;
|
|
85
|
+
// getReward: ({
|
|
86
|
+
// logic,
|
|
87
|
+
// state,
|
|
88
|
+
// goals,
|
|
89
|
+
// action,
|
|
90
|
+
// }: {
|
|
91
|
+
// logic: AnyActorLogic;
|
|
92
|
+
// state: TState;
|
|
93
|
+
// goals: string[];
|
|
94
|
+
// action: EventObject;
|
|
95
|
+
// }) => Promise<TReward>;
|
|
96
|
+
// }
|
|
97
|
+
|
|
98
|
+
// interface AgentLogic<T> {
|
|
99
|
+
// /**
|
|
100
|
+
// * The next possible actions (represented by events) that the agent can take
|
|
101
|
+
// * based on the current state of the environment
|
|
102
|
+
// */
|
|
103
|
+
// getActions(state: T): Promise<AnyEventObject[]>;
|
|
104
|
+
// getPlan(state: T, goal: any): Promise<Array<[T, EventObject]>>;
|
|
105
|
+
// }
|
|
106
|
+
|
|
107
|
+
// interface Agent<TState extends AnyMachineSnapshot, TEvent extends EventObject> {
|
|
108
|
+
// act: (env: ActorRef<TState, TEvent>) => Promise<void>;
|
|
109
|
+
// }
|
|
110
|
+
|
|
111
|
+
// function createAgent2<TEnvironment extends AnyActorRef>(
|
|
112
|
+
// openai: OpenAI,
|
|
113
|
+
// // logic: AnyActorLogic,
|
|
114
|
+
// // input: InputFrom<TLogic>,
|
|
115
|
+
// getGoals: (state: SnapshotFrom<TEnvironment>) => string | string[],
|
|
116
|
+
// schemas: ZodEventMapping
|
|
117
|
+
// ): Agent<SnapshotFrom<TEnvironment>, EventFrom<TEnvironment>> {
|
|
118
|
+
// const experiences: Array<AgentExperience<any, any>> = [];
|
|
119
|
+
|
|
120
|
+
// const agentModel: AgentModel<
|
|
121
|
+
// any,
|
|
122
|
+
// SnapshotFrom<TEnvironment>,
|
|
123
|
+
// EventFrom<TEnvironment>
|
|
124
|
+
// > = {
|
|
125
|
+
// // policy: async ({ logic, state, goal }) => {
|
|
126
|
+
// // const toolEvents = await getToolCalls(
|
|
127
|
+
// // openai,
|
|
128
|
+
// // goal,
|
|
129
|
+
// // state,
|
|
130
|
+
// // 'gpt-4-1106-preview',
|
|
131
|
+
// // (eventType) => eventType.startsWith('agent.'),
|
|
132
|
+
// // schemas
|
|
133
|
+
// // );
|
|
134
|
+
// // console.log(toolEvents);
|
|
135
|
+
// // return toolEvents.map((te) => ({
|
|
136
|
+
// // state,
|
|
137
|
+
// // event: te as EventFromLogic<TLogic>,
|
|
138
|
+
// // }));
|
|
139
|
+
// // },
|
|
140
|
+
// addExperience: (experience) => {
|
|
141
|
+
// experiences.push(experience);
|
|
142
|
+
// },
|
|
143
|
+
// getExperiences: async () => experiences,
|
|
144
|
+
// getLogic: async ({ experiences }) => {
|
|
145
|
+
// return null as any; // TODO
|
|
146
|
+
// },
|
|
147
|
+
// getNextEvents: async ({ logic, state }) => {
|
|
148
|
+
// return [];
|
|
149
|
+
// },
|
|
150
|
+
// getReward: async ({ logic, state, goals, action }) => {
|
|
151
|
+
// return 0;
|
|
152
|
+
// },
|
|
153
|
+
// getPlans: async ({ logic, state, goals }) => {
|
|
154
|
+
// if (!goals[0]) {
|
|
155
|
+
// return [];
|
|
156
|
+
// }
|
|
157
|
+
|
|
158
|
+
// const toolEvents = await getToolCalls(
|
|
159
|
+
// openai,
|
|
160
|
+
// goals[0] + '\nOnly make a single tool call.',
|
|
161
|
+
// state as any,
|
|
162
|
+
// 'gpt-3.5-turbo-16k-0613',
|
|
163
|
+
// (eventType) => eventType.startsWith('agent.'),
|
|
164
|
+
// schemas
|
|
165
|
+
// );
|
|
166
|
+
|
|
167
|
+
// console.log(toolEvents);
|
|
168
|
+
|
|
169
|
+
// return [
|
|
170
|
+
// toolEvents.map((toolEvent) => ({
|
|
171
|
+
// state,
|
|
172
|
+
// event: toolEvent as EventFrom<TEnvironment>,
|
|
173
|
+
// })),
|
|
174
|
+
// ];
|
|
175
|
+
// },
|
|
176
|
+
// };
|
|
177
|
+
|
|
178
|
+
// // const actor = createActor(logic, {
|
|
179
|
+
// // input,
|
|
180
|
+
// // inspect: (inspEv) => {
|
|
181
|
+
// // if (inspEv.type === '@xstate.snapshot') {
|
|
182
|
+
// // agentModel.addExperience({
|
|
183
|
+
// // prevState: experiences[experiences.length - 1]?.nextState,
|
|
184
|
+
// // nextState: (inspEv.snapshot as AnyMachineSnapshot).value,
|
|
185
|
+
// // event: inspEv.event as EventFromLogic<TLogic>,
|
|
186
|
+
// // });
|
|
187
|
+
// // }
|
|
188
|
+
// // },
|
|
189
|
+
// // });
|
|
190
|
+
|
|
191
|
+
// return {
|
|
192
|
+
// act: async (actorRef) => {
|
|
193
|
+
// const state = actorRef.getSnapshot();
|
|
194
|
+
// // @ts-ignore
|
|
195
|
+
// console.log(state.value, state.context);
|
|
196
|
+
// const experiences = await agentModel.getExperiences();
|
|
197
|
+
// const goals = toArray(getGoals(state));
|
|
198
|
+
// console.log('Goal:', goals);
|
|
199
|
+
|
|
200
|
+
// const nextPlans = await agentModel.getPlans({
|
|
201
|
+
// logic: await agentModel.getLogic({ experiences }),
|
|
202
|
+
// goals,
|
|
203
|
+
// state,
|
|
204
|
+
// });
|
|
205
|
+
|
|
206
|
+
// const nextStep = nextPlans?.[0]?.[0];
|
|
207
|
+
|
|
208
|
+
// // TODO: race conditions!
|
|
209
|
+
// if (nextStep) {
|
|
210
|
+
// console.log('nextStep', nextStep?.event);
|
|
211
|
+
// actorRef.send(nextStep.event);
|
|
212
|
+
// } else {
|
|
213
|
+
// console.log('No next step');
|
|
214
|
+
// }
|
|
215
|
+
// },
|
|
216
|
+
// } satisfies Agent<SnapshotFrom<TEnvironment>, EventFrom<TEnvironment>>; // TODO: fix types
|
|
217
|
+
// }
|
|
218
|
+
|
|
219
|
+
// function toArray<T>(value: T | T[]): T[] {
|
|
220
|
+
// return Array.isArray(value) ? value : [value];
|
|
221
|
+
// }
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { test, expect } from 'vitest';
|
|
2
|
+
import { createAgent, type AIAdapter } from './';
|
|
3
|
+
import { createActor, createMachine } from 'xstate';
|
|
4
|
+
|
|
5
|
+
test('an agent has the expected interface', () => {
|
|
6
|
+
const agent = createAgent({
|
|
7
|
+
name: 'test',
|
|
8
|
+
events: {},
|
|
9
|
+
model: {} as any,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
expect(agent.decide).toBeDefined();
|
|
13
|
+
expect(agent.generateText).toBeDefined();
|
|
14
|
+
expect(agent.streamText).toBeDefined();
|
|
15
|
+
|
|
16
|
+
expect(agent.addFeedback).toBeDefined();
|
|
17
|
+
expect(agent.addMessage).toBeDefined();
|
|
18
|
+
expect(agent.addObservation).toBeDefined();
|
|
19
|
+
expect(agent.addPlan).toBeDefined();
|
|
20
|
+
|
|
21
|
+
expect(agent.interact).toBeDefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('agent.addMessage() adds to message history', () => {
|
|
25
|
+
const agent = createAgent({
|
|
26
|
+
name: 'test',
|
|
27
|
+
events: {},
|
|
28
|
+
model: {} as any,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
agent.addMessage({
|
|
32
|
+
content: 'msg 1',
|
|
33
|
+
role: 'user',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const messageHistory = agent.addMessage({
|
|
37
|
+
content: 'response 1',
|
|
38
|
+
role: 'assistant',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(messageHistory.sessionId).toEqual(agent.sessionId);
|
|
42
|
+
|
|
43
|
+
expect(agent.select((c) => c.messages)).toContainEqual(
|
|
44
|
+
expect.objectContaining({
|
|
45
|
+
content: 'msg 1',
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
expect(agent.select((c) => c.messages)).toContainEqual(
|
|
50
|
+
expect.objectContaining({
|
|
51
|
+
content: 'response 1',
|
|
52
|
+
sessionId: expect.any(String),
|
|
53
|
+
timestamp: expect.any(Number),
|
|
54
|
+
})
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('agent.addFeedback() adds to feedback', () => {
|
|
59
|
+
const agent = createAgent({
|
|
60
|
+
name: 'test',
|
|
61
|
+
events: {},
|
|
62
|
+
model: {} as any,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const feedback = agent.addFeedback({
|
|
66
|
+
attributes: {
|
|
67
|
+
score: -1,
|
|
68
|
+
},
|
|
69
|
+
goal: 'Win the game',
|
|
70
|
+
observationId: 'obs-1',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(feedback.sessionId).toEqual(agent.sessionId);
|
|
74
|
+
|
|
75
|
+
expect(agent.select((c) => c.feedback)).toContainEqual(
|
|
76
|
+
expect.objectContaining({
|
|
77
|
+
attributes: {
|
|
78
|
+
score: -1,
|
|
79
|
+
},
|
|
80
|
+
goal: 'Win the game',
|
|
81
|
+
observationId: 'obs-1',
|
|
82
|
+
sessionId: expect.any(String),
|
|
83
|
+
timestamp: expect.any(Number),
|
|
84
|
+
})
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('agent.addObservation() adds to observations', () => {
|
|
89
|
+
const agent = createAgent({
|
|
90
|
+
name: 'test',
|
|
91
|
+
events: {},
|
|
92
|
+
model: {} as any,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const observation = agent.addObservation({
|
|
96
|
+
prevState: { value: 'playing', context: {} },
|
|
97
|
+
event: { type: 'play', position: 3 },
|
|
98
|
+
state: { value: 'lost', context: {} },
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(observation.sessionId).toEqual(agent.sessionId);
|
|
102
|
+
|
|
103
|
+
expect(agent.select((c) => c.observations)).toContainEqual(
|
|
104
|
+
expect.objectContaining({
|
|
105
|
+
prevState: { value: 'playing', context: {} },
|
|
106
|
+
event: { type: 'play', position: 3 },
|
|
107
|
+
state: { value: 'lost', context: {} },
|
|
108
|
+
sessionId: expect.any(String),
|
|
109
|
+
timestamp: expect.any(Number),
|
|
110
|
+
})
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('agent.interact() observes machine actors (no 2nd arg)', () => {
|
|
115
|
+
const machine = createMachine({
|
|
116
|
+
initial: 'a',
|
|
117
|
+
states: {
|
|
118
|
+
a: {
|
|
119
|
+
on: { NEXT: 'b' },
|
|
120
|
+
},
|
|
121
|
+
b: {},
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const agent = createAgent({
|
|
126
|
+
name: 'test',
|
|
127
|
+
events: {},
|
|
128
|
+
model: {} as any,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const actor = createActor(machine);
|
|
132
|
+
|
|
133
|
+
agent.interact(actor);
|
|
134
|
+
|
|
135
|
+
actor.start();
|
|
136
|
+
|
|
137
|
+
expect(agent.select((c) => c.observations)).toContainEqual(
|
|
138
|
+
expect.objectContaining({
|
|
139
|
+
prevState: undefined,
|
|
140
|
+
state: expect.objectContaining({ value: 'a' }),
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
actor.send({ type: 'NEXT' });
|
|
145
|
+
|
|
146
|
+
expect(agent.select((c) => c.observations)).toContainEqual(
|
|
147
|
+
expect.objectContaining({
|
|
148
|
+
prevState: expect.objectContaining({ value: 'a' }),
|
|
149
|
+
event: { type: 'NEXT' },
|
|
150
|
+
state: expect.objectContaining({ value: 'b' }),
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('Agents can use a custom adapter', async () => {
|
|
156
|
+
const adapter = {
|
|
157
|
+
generateText: async () => {
|
|
158
|
+
return {
|
|
159
|
+
text: 'Response',
|
|
160
|
+
} as any;
|
|
161
|
+
},
|
|
162
|
+
} as unknown as AIAdapter;
|
|
163
|
+
|
|
164
|
+
const agent = createAgent({
|
|
165
|
+
name: 'test',
|
|
166
|
+
events: {},
|
|
167
|
+
adapter,
|
|
168
|
+
model: {} as any,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const res = await agent.generateText({
|
|
172
|
+
prompt: 'Question?',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(res.text).toEqual('Response');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test.skip('You can listen for emitted agent events', () => {
|
|
179
|
+
const agent = createAgent({
|
|
180
|
+
name: 'test',
|
|
181
|
+
events: {},
|
|
182
|
+
adapter: {} as any,
|
|
183
|
+
model: {} as any,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
agent.on('feedback', (x) => {});
|
|
187
|
+
});
|