@statelyai/agent 2.0.0-next.1 → 2.0.0-next.2
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/.changeset/grumpy-dolphins-think.md +17 -0
- package/.changeset/old-teachers-tap.md +5 -0
- package/.changeset/pre.json +4 -1
- package/.changeset/smart-yaks-pull.md +23 -0
- package/CHANGELOG.md +40 -0
- package/dist/index.d.mts +55 -46
- package/dist/index.d.ts +55 -46
- package/dist/index.js +78 -50
- package/dist/index.mjs +81 -53
- package/examples/chatbot.ts +2 -2
- package/examples/cot.ts +6 -24
- package/examples/customer-service-sim.ts +3 -3
- package/examples/email.ts +9 -3
- package/examples/example.ts +2 -2
- package/examples/goal.ts +2 -2
- package/examples/joke.ts +2 -2
- package/examples/jugs.ts +4 -7
- package/examples/learn-from-feedback.ts +100 -0
- package/examples/number.ts +2 -2
- package/examples/raffle.ts +2 -2
- package/examples/river-crossing.ts +4 -7
- package/examples/simple.ts +13 -10
- package/examples/summary.ts +2 -5
- package/examples/support.ts +42 -38
- package/examples/ticTacToe.ts +46 -4
- package/examples/todo.ts +2 -2
- package/examples/tutor.ts +2 -2
- package/examples/verify.ts +2 -2
- package/examples/weather-agent.ts +141 -0
- package/examples/weather.ts +23 -23
- package/examples/word.ts +8 -6
- package/package.json +2 -1
- package/src/agent.test.ts +17 -15
- package/src/agent.ts +48 -35
- package/src/decide.test.ts +56 -8
- package/src/decide.ts +34 -24
- package/src/strategies/chainOfThought.ts +48 -0
- package/src/{planners → strategies}/shortestPath.test.ts +4 -7
- package/src/strategies/shortestPath.ts +173 -0
- package/src/{planners → strategies}/simple.ts +28 -18
- package/src/types.ts +62 -28
- package/src/utils.ts +12 -0
- package/src/planners/shortestPath.ts +0 -177
- package/src/strategies/chain-of-note.ts +0 -106
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { generateObject } from 'ai';
|
|
2
|
+
import {
|
|
3
|
+
AgentDecision,
|
|
4
|
+
AgentDecideInput,
|
|
5
|
+
AgentStrategy,
|
|
6
|
+
AgentStep,
|
|
7
|
+
AnyAgent,
|
|
8
|
+
CostFunction,
|
|
9
|
+
ObservedState,
|
|
10
|
+
} from '../types';
|
|
11
|
+
import { getShortestPaths } from '@xstate/graph';
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
14
|
+
import Ajv from 'ajv';
|
|
15
|
+
import { AnyMachineSnapshot } from 'xstate';
|
|
16
|
+
|
|
17
|
+
const ajv = new Ajv();
|
|
18
|
+
|
|
19
|
+
function observedStatesEqual(state1: ObservedState, state2: ObservedState) {
|
|
20
|
+
// check state value && state context
|
|
21
|
+
return (
|
|
22
|
+
JSON.stringify(state1.value) === JSON.stringify(state2.value) &&
|
|
23
|
+
JSON.stringify(state1.context) === JSON.stringify(state2.context)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function trimSteps(steps: AgentStep<any>[], currentState: ObservedState) {
|
|
28
|
+
const index = steps.findIndex(
|
|
29
|
+
(step) => step.state && observedStatesEqual(step.state, currentState)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
if (index === -1) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return steps.slice(index + 1, steps.length);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function experimental_shortestPathStrategy<T extends AnyAgent>(
|
|
40
|
+
agent: T,
|
|
41
|
+
input: AgentDecideInput<any>
|
|
42
|
+
): Promise<AgentDecision<any> | undefined> {
|
|
43
|
+
const costFunction: CostFunction<any> =
|
|
44
|
+
input.costFunction ?? ((path) => path.weight ?? Infinity);
|
|
45
|
+
const existingDecision = agent
|
|
46
|
+
.getDecisions()
|
|
47
|
+
.find((p) => p.strategy === 'shortestPath' && p.goal === input.goal);
|
|
48
|
+
|
|
49
|
+
let paths = existingDecision?.paths;
|
|
50
|
+
|
|
51
|
+
if (existingDecision) {
|
|
52
|
+
console.log('Existing decision found');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!input.machine && !existingDecision) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (input.machine && !existingDecision) {
|
|
60
|
+
const contextSchema = zodToJsonSchema(z.object(agent.context));
|
|
61
|
+
const result = await generateObject({
|
|
62
|
+
model: agent.model,
|
|
63
|
+
system: input.system ?? agent.description,
|
|
64
|
+
prompt: `
|
|
65
|
+
<goal>
|
|
66
|
+
${input.goal}
|
|
67
|
+
</goal>
|
|
68
|
+
<contextSchema>
|
|
69
|
+
${contextSchema}
|
|
70
|
+
</contextSchema>
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
Update the context JSON schema so that it validates the context to determine that it reaches the goal. Return the result as a diff.
|
|
74
|
+
|
|
75
|
+
The contextSchema properties must not change. Do not add or remove properties, or modify the name of the properties.
|
|
76
|
+
Use "const" for exact required values and define ranges/types for flexible conditions.
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
1. For "user is logged in with admin role":
|
|
80
|
+
{
|
|
81
|
+
"contextSchema": "{"type": "object", "properties": {"role": {"const": "admin"}, "lastLogin": {"type": "string"}}, "required": ["role"]}"
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
2. For "score is above 100":
|
|
85
|
+
{
|
|
86
|
+
"contextSchema": "{"type": "object", "properties": {"score": {"type": "number", "minimum": 100}}, "required": ["score"]}"
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
3. For "fruits contain apple, orange, banana":
|
|
90
|
+
{
|
|
91
|
+
"type": "array",
|
|
92
|
+
"allOf": [
|
|
93
|
+
{ "contains": { "const": "apple" } },
|
|
94
|
+
{ "contains": { "const": "orange" } },
|
|
95
|
+
{ "contains": { "const": "banana" } }
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
`.trim(),
|
|
99
|
+
schema: z.object({
|
|
100
|
+
// valueSchema: z
|
|
101
|
+
// .string()
|
|
102
|
+
// .describe('The JSON Schema representing the goal state value'),
|
|
103
|
+
contextSchema: z
|
|
104
|
+
.object({
|
|
105
|
+
type: z.literal('object'),
|
|
106
|
+
properties: z.object(
|
|
107
|
+
Object.keys((contextSchema as any).properties).reduce(
|
|
108
|
+
(acc, key) => {
|
|
109
|
+
acc[key] = z.any();
|
|
110
|
+
return acc;
|
|
111
|
+
},
|
|
112
|
+
{} as any
|
|
113
|
+
)
|
|
114
|
+
),
|
|
115
|
+
required: z.array(z.string()).optional(),
|
|
116
|
+
})
|
|
117
|
+
.describe('The JSON Schema representing the goal state context'),
|
|
118
|
+
}),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
console.log(result.object);
|
|
122
|
+
const validateContext = ajv.compile(result.object.contextSchema);
|
|
123
|
+
|
|
124
|
+
const stateFilter = (state: AnyMachineSnapshot) => {
|
|
125
|
+
return validateContext(state.context);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const resolvedState = input.machine.resolveState({
|
|
129
|
+
...input.state,
|
|
130
|
+
context: input.state.context ?? {},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
paths = getShortestPaths(input.machine, {
|
|
134
|
+
fromState: resolvedState,
|
|
135
|
+
toState: stateFilter,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!paths) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const trimmedPaths = paths
|
|
144
|
+
.map((path) => {
|
|
145
|
+
const trimmedSteps = trimSteps(path.steps, input.state);
|
|
146
|
+
if (!trimmedSteps) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
...path,
|
|
151
|
+
steps: trimmedSteps,
|
|
152
|
+
};
|
|
153
|
+
})
|
|
154
|
+
.filter((p): p is NonNullable<typeof p> => p !== undefined);
|
|
155
|
+
|
|
156
|
+
// Sort paths from least weight to most weight
|
|
157
|
+
const sortedPaths = trimmedPaths.sort(
|
|
158
|
+
(a, b) => costFunction(a) - costFunction(b)
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const leastWeightPath = sortedPaths[0];
|
|
162
|
+
const nextStep = leastWeightPath?.steps[0];
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
strategy: 'shortestPath',
|
|
166
|
+
episodeId: agent.episodeId,
|
|
167
|
+
goal: input.goal,
|
|
168
|
+
goalState: paths[0]?.state,
|
|
169
|
+
nextEvent: nextStep?.event,
|
|
170
|
+
paths,
|
|
171
|
+
timestamp: Date.now(),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
import { CoreMessage, generateText } from 'ai';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
AgentDecision,
|
|
4
|
+
AgentDecideInput,
|
|
5
|
+
PromptTemplate,
|
|
6
|
+
AnyAgent,
|
|
7
|
+
} from '../types';
|
|
8
|
+
import { convertToXml, randomId } from '../utils';
|
|
4
9
|
import { getNextSnapshot } from 'xstate';
|
|
5
|
-
import { defaultTextTemplate } from '../templates/defaultText';
|
|
6
10
|
import { getMessages } from '../text';
|
|
7
11
|
import { getToolMap } from '../decide';
|
|
8
12
|
|
|
9
|
-
const
|
|
13
|
+
const simpleStrategyPromptTemplate: PromptTemplate<any> = (data) => {
|
|
10
14
|
return `
|
|
11
|
-
${
|
|
15
|
+
${convertToXml(data)}
|
|
12
16
|
|
|
13
17
|
Make at most one tool call to achieve the above goal. If the goal cannot be achieved with any tool calls, do not make any tool call.
|
|
14
18
|
`.trim();
|
|
15
19
|
};
|
|
16
20
|
|
|
17
|
-
export async function
|
|
21
|
+
export async function simpleStrategy<T extends AnyAgent>(
|
|
18
22
|
agent: T,
|
|
19
|
-
input:
|
|
20
|
-
): Promise<
|
|
23
|
+
input: AgentDecideInput<any>
|
|
24
|
+
): Promise<AgentDecision<any> | undefined> {
|
|
21
25
|
const toolMap = getToolMap(agent, input);
|
|
22
26
|
|
|
23
27
|
if (!toolMap) {
|
|
@@ -27,8 +31,8 @@ export async function simplePlanner<T extends AnyAgent>(
|
|
|
27
31
|
|
|
28
32
|
// Create a prompt with the given context and goal.
|
|
29
33
|
// The template is used to ensure that a single tool call at most is made.
|
|
30
|
-
const prompt =
|
|
31
|
-
context: input.
|
|
34
|
+
const prompt = simpleStrategyPromptTemplate({
|
|
35
|
+
context: input.context,
|
|
32
36
|
goal: input.goal,
|
|
33
37
|
});
|
|
34
38
|
|
|
@@ -38,20 +42,22 @@ export async function simplePlanner<T extends AnyAgent>(
|
|
|
38
42
|
|
|
39
43
|
const {
|
|
40
44
|
state,
|
|
45
|
+
context,
|
|
41
46
|
machine,
|
|
42
|
-
|
|
47
|
+
prevDecision,
|
|
43
48
|
events,
|
|
44
49
|
goal,
|
|
45
50
|
model: _,
|
|
46
51
|
...rest
|
|
47
52
|
} = input;
|
|
48
53
|
|
|
49
|
-
const machineState =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
const machineState =
|
|
55
|
+
input.machine && input.state
|
|
56
|
+
? input.machine.resolveState({
|
|
57
|
+
...input.state,
|
|
58
|
+
context: input.state.context ?? {},
|
|
59
|
+
})
|
|
60
|
+
: undefined;
|
|
55
61
|
|
|
56
62
|
const result = await generateText({
|
|
57
63
|
...rest,
|
|
@@ -81,7 +87,7 @@ export async function simplePlanner<T extends AnyAgent>(
|
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
return {
|
|
84
|
-
|
|
90
|
+
strategy: 'simple',
|
|
85
91
|
goal: input.goal,
|
|
86
92
|
goalState: input.state,
|
|
87
93
|
nextEvent: singleResult.result,
|
|
@@ -103,3 +109,7 @@ export async function simplePlanner<T extends AnyAgent>(
|
|
|
103
109
|
],
|
|
104
110
|
};
|
|
105
111
|
}
|
|
112
|
+
|
|
113
|
+
export function createSimpleStrategy<T extends AnyAgent>() {
|
|
114
|
+
return simpleStrategy;
|
|
115
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -31,7 +31,7 @@ export type CostFunction<TEvent extends EventObject> = (
|
|
|
31
31
|
path: AgentPath<TEvent>
|
|
32
32
|
) => number;
|
|
33
33
|
|
|
34
|
-
export type
|
|
34
|
+
export type AgentDecideInput<TEvent extends EventObject> = Omit<
|
|
35
35
|
AgentGenerateTextOptions,
|
|
36
36
|
'prompt' | 'tools'
|
|
37
37
|
> & {
|
|
@@ -41,7 +41,7 @@ export type AgentPlanInput<TEvent extends EventObject> = Omit<
|
|
|
41
41
|
state: ObservedState;
|
|
42
42
|
/**
|
|
43
43
|
* The goal for the agent to accomplish.
|
|
44
|
-
* The agent will
|
|
44
|
+
* The agent will make a decision based on this goal.
|
|
45
45
|
*/
|
|
46
46
|
goal: string;
|
|
47
47
|
/**
|
|
@@ -55,9 +55,9 @@ export type AgentPlanInput<TEvent extends EventObject> = Omit<
|
|
|
55
55
|
*/
|
|
56
56
|
machine?: AnyStateMachine;
|
|
57
57
|
/**
|
|
58
|
-
* The previous
|
|
58
|
+
* The previous decision made by the agent.
|
|
59
59
|
*/
|
|
60
|
-
|
|
60
|
+
prevDecision?: AgentDecision<TEvent>;
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* The total cost of the path to the goal state.
|
|
@@ -65,7 +65,7 @@ export type AgentPlanInput<TEvent extends EventObject> = Omit<
|
|
|
65
65
|
costFunction?: CostFunction<TEvent>;
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
* The maximum number of attempts to
|
|
68
|
+
* The maximum number of attempts to make a decision.
|
|
69
69
|
* Defaults to 2.
|
|
70
70
|
*/
|
|
71
71
|
maxAttempts?: number;
|
|
@@ -86,14 +86,14 @@ export type AgentPath<TEvent extends EventObject> = {
|
|
|
86
86
|
weight?: number;
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
export type
|
|
89
|
+
export type AgentDecision<TEvent extends EventObject> = {
|
|
90
90
|
/**
|
|
91
|
-
* The
|
|
91
|
+
* The strategy used to generate the decision
|
|
92
92
|
*/
|
|
93
|
-
|
|
93
|
+
strategy: string;
|
|
94
94
|
goal: string;
|
|
95
95
|
/**
|
|
96
|
-
* The ending state of the
|
|
96
|
+
* The ending state of the decision.
|
|
97
97
|
*/
|
|
98
98
|
goalState: ObservedState | undefined;
|
|
99
99
|
/**
|
|
@@ -144,22 +144,29 @@ export type PromptTemplate<TEvents extends EventObject> = (data: {
|
|
|
144
144
|
observations?: AgentObservation<any>[]; // TODO
|
|
145
145
|
feedback?: AgentFeedback[];
|
|
146
146
|
messages?: AgentMessage[];
|
|
147
|
-
|
|
147
|
+
decisions?: AgentDecision<TEvents>[];
|
|
148
148
|
}) => string;
|
|
149
149
|
|
|
150
|
-
export type
|
|
150
|
+
export type AgentStrategy<T extends AnyAgent> = (
|
|
151
151
|
agent: T,
|
|
152
|
-
input:
|
|
153
|
-
) => Promise<
|
|
152
|
+
input: AgentDecideInput<EventsFromAgent<T>>
|
|
153
|
+
) => Promise<AgentDecision<EventsFromAgent<T>> | undefined>;
|
|
154
|
+
|
|
155
|
+
export type AgentInteractInput<T extends AnyAgent> = Omit<
|
|
156
|
+
AgentDecideOptions<T>,
|
|
157
|
+
'state'
|
|
158
|
+
>;
|
|
154
159
|
|
|
155
160
|
export type AgentDecideOptions<T extends AnyAgent> = {
|
|
156
161
|
goal: string;
|
|
157
|
-
model?: LanguageModel;
|
|
158
162
|
state: ObservedState;
|
|
163
|
+
context?: Record<string, any>;
|
|
159
164
|
machine?: AnyStateMachine;
|
|
165
|
+
model?: LanguageModel;
|
|
160
166
|
execute?: (event: AnyEventObject) => Promise<void>;
|
|
161
|
-
|
|
167
|
+
strategy?: AgentStrategy<T>;
|
|
162
168
|
events?: ZodEventMapping;
|
|
169
|
+
allowedEvents?: Array<EventsFromAgent<T>['type']>;
|
|
163
170
|
/**
|
|
164
171
|
* The maximum number of times the agent will attempt to make a decision.
|
|
165
172
|
* Defaults to 2.
|
|
@@ -168,23 +175,21 @@ export type AgentDecideOptions<T extends AnyAgent> = {
|
|
|
168
175
|
} & Omit<Parameters<typeof generateText>[0], 'model' | 'tools' | 'prompt'>;
|
|
169
176
|
|
|
170
177
|
export interface AgentFeedback {
|
|
171
|
-
goal
|
|
172
|
-
observationId
|
|
178
|
+
goal: string;
|
|
179
|
+
observationId: string;
|
|
173
180
|
/**
|
|
174
181
|
* The message correlation that the feedback is relevant for
|
|
175
182
|
*/
|
|
176
183
|
attributes: Record<string, any>;
|
|
177
|
-
reward: number;
|
|
178
184
|
timestamp: number;
|
|
179
185
|
episodeId: string;
|
|
180
186
|
}
|
|
181
187
|
|
|
182
188
|
export interface AgentFeedbackInput {
|
|
183
|
-
goal
|
|
184
|
-
observationId
|
|
185
|
-
attributes
|
|
189
|
+
goal: string;
|
|
190
|
+
observationId: string;
|
|
191
|
+
attributes: Record<string, any>;
|
|
186
192
|
timestamp?: number;
|
|
187
|
-
reward?: number;
|
|
188
193
|
}
|
|
189
194
|
|
|
190
195
|
export type AgentMessage = CoreMessage & {
|
|
@@ -327,6 +332,7 @@ export type AgentMessageInput = CoreMessage & {
|
|
|
327
332
|
|
|
328
333
|
export interface AgentObservation<TActor extends ActorRefLike> {
|
|
329
334
|
id: string;
|
|
335
|
+
// TODO: goal
|
|
330
336
|
prevState: SnapshotFrom<TActor> | undefined;
|
|
331
337
|
event: EventFrom<TActor> | undefined;
|
|
332
338
|
state: SnapshotFrom<TActor>;
|
|
@@ -351,7 +357,7 @@ export type AgentDecisionInput = {
|
|
|
351
357
|
} & Omit<Parameters<typeof generateText>[0], 'model' | 'tools' | 'prompt'>;
|
|
352
358
|
|
|
353
359
|
export type AgentDecisionLogic<TEvents extends EventObject> = PromiseActorLogic<
|
|
354
|
-
|
|
360
|
+
AgentDecision<TEvents> | undefined,
|
|
355
361
|
AgentDecisionInput | string
|
|
356
362
|
>;
|
|
357
363
|
|
|
@@ -369,8 +375,8 @@ export type AgentEmitted<TEvents extends EventObject> =
|
|
|
369
375
|
message: AgentMessage;
|
|
370
376
|
}
|
|
371
377
|
| {
|
|
372
|
-
type: '
|
|
373
|
-
|
|
378
|
+
type: 'decision';
|
|
379
|
+
decision: AgentDecision<TEvents>;
|
|
374
380
|
};
|
|
375
381
|
|
|
376
382
|
export type AgentLogic<TEvents extends EventObject> = ActorLogic<
|
|
@@ -388,8 +394,8 @@ export type AgentLogic<TEvents extends EventObject> = ActorLogic<
|
|
|
388
394
|
message: AgentMessage;
|
|
389
395
|
}
|
|
390
396
|
| {
|
|
391
|
-
type: 'agent.
|
|
392
|
-
|
|
397
|
+
type: 'agent.decision';
|
|
398
|
+
decision: AgentDecision<TEvents>;
|
|
393
399
|
},
|
|
394
400
|
any, // TODO: input
|
|
395
401
|
any,
|
|
@@ -453,7 +459,7 @@ export type ObservedStateFrom<TActor extends ActorRefLike> = Pick<
|
|
|
453
459
|
export type AgentMemoryContext = {
|
|
454
460
|
observations: AgentObservation<any>[]; // TODO
|
|
455
461
|
messages: AgentMessage[];
|
|
456
|
-
|
|
462
|
+
decisions: AgentDecision<any>[];
|
|
457
463
|
feedback: AgentFeedback[];
|
|
458
464
|
};
|
|
459
465
|
|
|
@@ -472,3 +478,31 @@ export interface AgentLongTermMemory {
|
|
|
472
478
|
}
|
|
473
479
|
|
|
474
480
|
export type Compute<A extends any> = { [K in keyof A]: A[K] } & unknown;
|
|
481
|
+
|
|
482
|
+
export type MaybePromise<T> = T | Promise<T>;
|
|
483
|
+
|
|
484
|
+
export type EventsFromAgent<T extends AnyAgent> = T extends Agent<
|
|
485
|
+
infer _,
|
|
486
|
+
infer __,
|
|
487
|
+
infer TEvents,
|
|
488
|
+
infer ___
|
|
489
|
+
>
|
|
490
|
+
? TEvents
|
|
491
|
+
: never;
|
|
492
|
+
|
|
493
|
+
export type TypesFromAgent<T extends AnyAgent> = T extends Agent<
|
|
494
|
+
infer TContextSchema,
|
|
495
|
+
infer TEventSchema
|
|
496
|
+
>
|
|
497
|
+
? {
|
|
498
|
+
context: ContextFromZodContextMapping<TContextSchema>;
|
|
499
|
+
events: EventsFromZodEventMapping<TEventSchema>;
|
|
500
|
+
}
|
|
501
|
+
: never;
|
|
502
|
+
|
|
503
|
+
export type ContextFromAgent<T extends AnyAgent> = T extends Agent<
|
|
504
|
+
infer TContextSchema,
|
|
505
|
+
infer _TEventSchema
|
|
506
|
+
>
|
|
507
|
+
? ContextFromZodContextMapping<TContextSchema>
|
|
508
|
+
: never;
|
package/src/utils.ts
CHANGED
|
@@ -59,6 +59,18 @@ export function wrapInXml(tagName: string, content: string): string {
|
|
|
59
59
|
return `<${tagName}>${content}</${tagName}>`;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
export function convertToXml(obj: Record<string, any>): string {
|
|
63
|
+
return Object.entries(obj)
|
|
64
|
+
.map(([key, value]) => {
|
|
65
|
+
if (typeof value === 'object' && value !== null) {
|
|
66
|
+
return wrapInXml(key, convertToXml(value));
|
|
67
|
+
} else {
|
|
68
|
+
return wrapInXml(key, value);
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
.join('');
|
|
72
|
+
}
|
|
73
|
+
|
|
62
74
|
export function randomId(prefix?: string): string {
|
|
63
75
|
const timestamp = Date.now().toString(36);
|
|
64
76
|
const random = Math.random().toString(36).substring(2, 9);
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { generateObject } from 'ai';
|
|
2
|
-
import {
|
|
3
|
-
AgentPlan,
|
|
4
|
-
AgentPlanInput,
|
|
5
|
-
AgentPlanner,
|
|
6
|
-
AgentStep,
|
|
7
|
-
AnyAgent,
|
|
8
|
-
CostFunction,
|
|
9
|
-
ObservedState,
|
|
10
|
-
} from '../types';
|
|
11
|
-
import { getShortestPaths } from '@xstate/graph';
|
|
12
|
-
import { z } from 'zod';
|
|
13
|
-
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
14
|
-
import Ajv from 'ajv';
|
|
15
|
-
import { AnyMachineSnapshot } from 'xstate';
|
|
16
|
-
|
|
17
|
-
const ajv = new Ajv();
|
|
18
|
-
|
|
19
|
-
function observedStatesEqual(state1: ObservedState, state2: ObservedState) {
|
|
20
|
-
// check state value && state context
|
|
21
|
-
return (
|
|
22
|
-
JSON.stringify(state1.value) === JSON.stringify(state2.value) &&
|
|
23
|
-
JSON.stringify(state1.context) === JSON.stringify(state2.context)
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function trimSteps(steps: AgentStep<any>[], currentState: ObservedState) {
|
|
28
|
-
const index = steps.findIndex(
|
|
29
|
-
(step) => step.state && observedStatesEqual(step.state, currentState)
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
if (index === -1) {
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return steps.slice(index + 1, steps.length);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function experimental_createShortestPathPlanner<
|
|
40
|
-
T extends AnyAgent
|
|
41
|
-
>(): AgentPlanner<T> {
|
|
42
|
-
return async function shortestPathPlanner<T extends AnyAgent>(
|
|
43
|
-
agent: T,
|
|
44
|
-
input: AgentPlanInput<any>
|
|
45
|
-
): Promise<AgentPlan<any> | undefined> {
|
|
46
|
-
const costFunction: CostFunction<any> =
|
|
47
|
-
input.costFunction ?? ((path) => path.weight ?? Infinity);
|
|
48
|
-
const existingPlan = agent
|
|
49
|
-
.getPlans()
|
|
50
|
-
.find((p) => p.planner === 'shortestPath' && p.goal === input.goal);
|
|
51
|
-
|
|
52
|
-
let paths = existingPlan?.paths;
|
|
53
|
-
|
|
54
|
-
if (existingPlan) {
|
|
55
|
-
console.log('Existing plan found');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!input.machine && !existingPlan) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (input.machine && !existingPlan) {
|
|
63
|
-
const contextSchema = zodToJsonSchema(z.object(agent.context));
|
|
64
|
-
const result = await generateObject({
|
|
65
|
-
model: agent.model,
|
|
66
|
-
system: input.system ?? agent.description,
|
|
67
|
-
prompt: `
|
|
68
|
-
<goal>
|
|
69
|
-
${input.goal}
|
|
70
|
-
</goal>
|
|
71
|
-
<contextSchema>
|
|
72
|
-
${contextSchema}
|
|
73
|
-
</contextSchema>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
Update the context JSON schema so that it validates the context to determine that it reaches the goal. Return the result as a diff.
|
|
77
|
-
|
|
78
|
-
The contextSchema properties must not change. Do not add or remove properties, or modify the name of the properties.
|
|
79
|
-
Use "const" for exact required values and define ranges/types for flexible conditions.
|
|
80
|
-
|
|
81
|
-
Examples:
|
|
82
|
-
1. For "user is logged in with admin role":
|
|
83
|
-
{
|
|
84
|
-
"contextSchema": "{"type": "object", "properties": {"role": {"const": "admin"}, "lastLogin": {"type": "string"}}, "required": ["role"]}"
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
2. For "score is above 100":
|
|
88
|
-
{
|
|
89
|
-
"contextSchema": "{"type": "object", "properties": {"score": {"type": "number", "minimum": 100}}, "required": ["score"]}"
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
3. For "fruits contain apple, orange, banana":
|
|
93
|
-
{
|
|
94
|
-
"type": "array",
|
|
95
|
-
"allOf": [
|
|
96
|
-
{ "contains": { "const": "apple" } },
|
|
97
|
-
{ "contains": { "const": "orange" } },
|
|
98
|
-
{ "contains": { "const": "banana" } }
|
|
99
|
-
]
|
|
100
|
-
}
|
|
101
|
-
`.trim(),
|
|
102
|
-
schema: z.object({
|
|
103
|
-
// valueSchema: z
|
|
104
|
-
// .string()
|
|
105
|
-
// .describe('The JSON Schema representing the goal state value'),
|
|
106
|
-
contextSchema: z
|
|
107
|
-
.object({
|
|
108
|
-
type: z.literal('object'),
|
|
109
|
-
properties: z.object(
|
|
110
|
-
Object.keys((contextSchema as any).properties).reduce(
|
|
111
|
-
(acc, key) => {
|
|
112
|
-
acc[key] = z.any();
|
|
113
|
-
return acc;
|
|
114
|
-
},
|
|
115
|
-
{} as any
|
|
116
|
-
)
|
|
117
|
-
),
|
|
118
|
-
required: z.array(z.string()).optional(),
|
|
119
|
-
})
|
|
120
|
-
.describe('The JSON Schema representing the goal state context'),
|
|
121
|
-
}),
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
console.log(result.object);
|
|
125
|
-
const validateContext = ajv.compile(result.object.contextSchema);
|
|
126
|
-
|
|
127
|
-
const stateFilter = (state: AnyMachineSnapshot) => {
|
|
128
|
-
return validateContext(state.context);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const resolvedState = input.machine.resolveState({
|
|
132
|
-
...input.state,
|
|
133
|
-
context: input.state.context ?? {},
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
paths = getShortestPaths(input.machine, {
|
|
137
|
-
fromState: resolvedState,
|
|
138
|
-
toState: stateFilter,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (!paths) {
|
|
143
|
-
return undefined;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const trimmedPaths = paths
|
|
147
|
-
.map((path) => {
|
|
148
|
-
const trimmedSteps = trimSteps(path.steps, input.state);
|
|
149
|
-
if (!trimmedSteps) {
|
|
150
|
-
return undefined;
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
...path,
|
|
154
|
-
steps: trimmedSteps,
|
|
155
|
-
};
|
|
156
|
-
})
|
|
157
|
-
.filter((p): p is NonNullable<typeof p> => p !== undefined);
|
|
158
|
-
|
|
159
|
-
// Sort paths from least weight to most weight
|
|
160
|
-
const sortedPaths = trimmedPaths.sort(
|
|
161
|
-
(a, b) => costFunction(a) - costFunction(b)
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
const leastWeightPath = sortedPaths[0];
|
|
165
|
-
const nextStep = leastWeightPath?.steps[0];
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
planner: 'shortestPath',
|
|
169
|
-
episodeId: agent.episodeId,
|
|
170
|
-
goal: input.goal,
|
|
171
|
-
goalState: paths[0]?.state,
|
|
172
|
-
nextEvent: nextStep?.event,
|
|
173
|
-
paths,
|
|
174
|
-
timestamp: Date.now(),
|
|
175
|
-
};
|
|
176
|
-
};
|
|
177
|
-
}
|