@fluidframework/tree-agent 2.70.0-360753 → 2.70.0-361092
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/api-report/tree-agent.alpha.api.md +2 -2
- package/dist/agent.d.ts +4 -4
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +5 -0
- package/dist/agent.js.map +1 -1
- package/dist/api.d.ts +60 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js.map +1 -1
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +83 -17
- package/dist/prompt.js.map +1 -1
- package/lib/agent.d.ts +4 -4
- package/lib/agent.d.ts.map +1 -1
- package/lib/agent.js +5 -0
- package/lib/agent.js.map +1 -1
- package/lib/api.d.ts +60 -0
- package/lib/api.d.ts.map +1 -1
- package/lib/api.js.map +1 -1
- package/lib/prompt.d.ts.map +1 -1
- package/lib/prompt.js +82 -16
- package/lib/prompt.js.map +1 -1
- package/package.json +12 -12
- package/src/agent.ts +13 -7
- package/src/api.ts +65 -0
- package/src/prompt.ts +89 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/tree-agent",
|
|
3
|
-
"version": "2.70.0-
|
|
3
|
+
"version": "2.70.0-361092",
|
|
4
4
|
"description": "Experimental package to simplify integrating AI into Fluid-based applications",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -70,24 +70,24 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@anthropic-ai/sdk": "^0.39.0",
|
|
73
|
-
"@fluidframework/core-utils": "2.70.0-
|
|
74
|
-
"@fluidframework/runtime-utils": "2.70.0-
|
|
75
|
-
"@fluidframework/telemetry-utils": "2.70.0-
|
|
76
|
-
"@fluidframework/tree": "2.70.0-
|
|
73
|
+
"@fluidframework/core-utils": "2.70.0-361092",
|
|
74
|
+
"@fluidframework/runtime-utils": "2.70.0-361092",
|
|
75
|
+
"@fluidframework/telemetry-utils": "2.70.0-361092",
|
|
76
|
+
"@fluidframework/tree": "2.70.0-361092",
|
|
77
77
|
"uuid": "^11.1.0",
|
|
78
78
|
"zod": "^3.25.32"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@arethetypeswrong/cli": "^0.17.1",
|
|
82
82
|
"@biomejs/biome": "~1.9.3",
|
|
83
|
-
"@fluid-internal/mocha-test-setup": "2.70.0-
|
|
83
|
+
"@fluid-internal/mocha-test-setup": "2.70.0-361092",
|
|
84
84
|
"@fluid-tools/build-cli": "^0.58.3",
|
|
85
85
|
"@fluidframework/build-common": "^2.0.3",
|
|
86
86
|
"@fluidframework/build-tools": "^0.58.3",
|
|
87
|
-
"@fluidframework/eslint-config-fluid": "^6.
|
|
88
|
-
"@fluidframework/id-compressor": "2.70.0-
|
|
89
|
-
"@fluidframework/runtime-utils": "2.70.0-
|
|
90
|
-
"@fluidframework/test-runtime-utils": "2.70.0-
|
|
87
|
+
"@fluidframework/eslint-config-fluid": "^6.1.0",
|
|
88
|
+
"@fluidframework/id-compressor": "2.70.0-361092",
|
|
89
|
+
"@fluidframework/runtime-utils": "2.70.0-361092",
|
|
90
|
+
"@fluidframework/test-runtime-utils": "2.70.0-361092",
|
|
91
91
|
"@langchain/anthropic": "^0.3.24",
|
|
92
92
|
"@langchain/core": "^0.3.78",
|
|
93
93
|
"@langchain/google-genai": "^0.2.16",
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
"concurrently": "^8.2.1",
|
|
101
101
|
"copyfiles": "^2.4.1",
|
|
102
102
|
"cross-env": "^7.0.3",
|
|
103
|
-
"eslint": "~8.
|
|
104
|
-
"eslint-config-prettier": "~
|
|
103
|
+
"eslint": "~8.57.1",
|
|
104
|
+
"eslint-config-prettier": "~10.1.8",
|
|
105
105
|
"mocha": "^10.8.2",
|
|
106
106
|
"mocha-multi-reporters": "^1.5.1",
|
|
107
107
|
"prettier": "~3.0.3",
|
package/src/agent.ts
CHANGED
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
Logger,
|
|
25
25
|
SynchronousEditor,
|
|
26
26
|
AsynchronousEditor,
|
|
27
|
+
Context,
|
|
27
28
|
} from "./api.js";
|
|
28
29
|
import { getPrompt, stringifyTree } from "./prompt.js";
|
|
29
30
|
import { Subtree } from "./subtree.js";
|
|
@@ -248,25 +249,25 @@ export const defaultEditor: AsynchronousEditor = async (context, code) => {
|
|
|
248
249
|
};
|
|
249
250
|
|
|
250
251
|
/**
|
|
251
|
-
* Binds the given {@link
|
|
252
|
+
* Binds the given {@link AsynchronousEditor | editor} to the given view or tree.
|
|
252
253
|
* @returns A function that takes a string of JavaScript code and executes it on the given view or tree using the given editor function.
|
|
253
254
|
* @remarks This is useful for testing/debugging code execution without needing to set up a full {@link SharedTreeSemanticAgent | agent}.
|
|
254
255
|
* @alpha
|
|
255
256
|
*/
|
|
256
257
|
export function bindEditorImpl<TSchema extends ImplicitFieldSchema>(
|
|
257
258
|
tree: TreeView<TSchema> | (ReadableField<TSchema> & TreeNode),
|
|
258
|
-
editor:
|
|
259
|
-
): (code: string) => void
|
|
259
|
+
editor: AsynchronousEditor,
|
|
260
|
+
): (code: string) => Promise<void>;
|
|
260
261
|
/**
|
|
261
|
-
* Binds the given {@link
|
|
262
|
+
* Binds the given {@link SynchronousEditor | editor} to the given view or tree.
|
|
262
263
|
* @returns A function that takes a string of JavaScript code and executes it on the given view or tree using the given editor function.
|
|
263
264
|
* @remarks This is useful for testing/debugging code execution without needing to set up a full {@link SharedTreeSemanticAgent | agent}.
|
|
264
265
|
* @alpha
|
|
265
266
|
*/
|
|
266
267
|
export function bindEditorImpl<TSchema extends ImplicitFieldSchema>(
|
|
267
268
|
tree: TreeView<TSchema> | (ReadableField<TSchema> & TreeNode),
|
|
268
|
-
editor:
|
|
269
|
-
): (code: string) =>
|
|
269
|
+
editor: SynchronousEditor,
|
|
270
|
+
): (code: string) => void;
|
|
270
271
|
/**
|
|
271
272
|
* Binds the given {@link SynchronousEditor | editor} or {@link AsynchronousEditor | editor} to the given view or tree.
|
|
272
273
|
* @returns A function that takes a string of JavaScript code and executes it on the given view or tree using the given editor function.
|
|
@@ -296,9 +297,11 @@ function bindEditorToSubtree<TSchema extends ImplicitFieldSchema>(
|
|
|
296
297
|
): (code: string) => void | Promise<void> {
|
|
297
298
|
// Stick the tree schema constructors on an object passed to the function so that the LLM can create new nodes.
|
|
298
299
|
const create: Record<string, (input: FactoryContentObject) => TreeNode> = {};
|
|
300
|
+
const is: Record<string, <T extends TreeNode>(input: unknown) => input is T> = {};
|
|
299
301
|
for (const schema of findNamedSchemas(tree.schema)) {
|
|
300
302
|
const name = getFriendlyName(schema);
|
|
301
303
|
create[name] = (input: FactoryContentObject) => constructTreeNode(schema, input);
|
|
304
|
+
is[name] = <T extends TreeNode>(input: unknown): input is T => Tree.is(input, schema);
|
|
302
305
|
}
|
|
303
306
|
|
|
304
307
|
const context = {
|
|
@@ -309,7 +312,10 @@ function bindEditorToSubtree<TSchema extends ImplicitFieldSchema>(
|
|
|
309
312
|
tree.field = value;
|
|
310
313
|
},
|
|
311
314
|
create,
|
|
312
|
-
|
|
315
|
+
is,
|
|
316
|
+
parent: (child: TreeNode): TreeNode | undefined => Tree.parent(child),
|
|
317
|
+
key: (child: TreeNode): string | number => Tree.key(child),
|
|
318
|
+
} satisfies Context<TSchema>;
|
|
313
319
|
|
|
314
320
|
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
315
321
|
return (code: string) => executeEdit(context, code);
|
package/src/api.ts
CHANGED
|
@@ -22,6 +22,71 @@ export interface Logger {
|
|
|
22
22
|
log(message: string): void;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* The context object available to generated code when editing a tree.
|
|
27
|
+
* @remarks This object is provided to JavaScript code executed by the {@link SynchronousEditor | editor} as a variable named `context`.
|
|
28
|
+
* It contains the current state of the tree and utilities for creating and inspecting tree nodes.
|
|
29
|
+
* @alpha
|
|
30
|
+
*/
|
|
31
|
+
export interface Context<TSchema extends ImplicitFieldSchema> {
|
|
32
|
+
/**
|
|
33
|
+
* The root of the tree that can be read or modified.
|
|
34
|
+
* @remarks
|
|
35
|
+
* You can read properties and navigate through the tree starting from this root.
|
|
36
|
+
* You can also assign a new value to this property to replace the entire tree, as long as the new value is one of the types allowed at the root.
|
|
37
|
+
*
|
|
38
|
+
* Example: Read the current root with `const currentRoot = context.root;`
|
|
39
|
+
*
|
|
40
|
+
* Example: Replace the entire root with `context.root = context.create.MyRootType({ });`
|
|
41
|
+
*/
|
|
42
|
+
root: ReadableField<TSchema>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A collection of builder functions for creating new tree nodes.
|
|
46
|
+
* @remarks
|
|
47
|
+
* Each property on this object is named after a type in the tree schema.
|
|
48
|
+
* Call the corresponding function to create a new node of that type.
|
|
49
|
+
* Always use these builder functions when creating new nodes rather than plain JavaScript objects.
|
|
50
|
+
*
|
|
51
|
+
* Example: Create a new Person node with `context.create.Person({ name: "Alice", age: 30 })`
|
|
52
|
+
*
|
|
53
|
+
* Example: Create a new Task node with `context.create.Task({ title: "Buy groceries", completed: false })`
|
|
54
|
+
*/
|
|
55
|
+
create: Record<string, (input: FactoryContentObject) => TreeNode>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A collection of type-checking functions for tree nodes.
|
|
59
|
+
* @remarks
|
|
60
|
+
* Each property on this object is named after a type in the tree schema.
|
|
61
|
+
* Call the corresponding function to check if a node is of that specific type.
|
|
62
|
+
* This is useful when working with nodes that could be one of multiple types.
|
|
63
|
+
*
|
|
64
|
+
* Example: Check if a node is a Person with `if (context.is.Person(node)) { console.log(node.name); }`
|
|
65
|
+
*/
|
|
66
|
+
is: Record<string, <T extends TreeNode>(input: T) => input is T>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Returns the parent node of the given child node.
|
|
70
|
+
* @param child - The node whose parent you want to find.
|
|
71
|
+
* @returns The parent node, or `undefined` if the node is the root or is not in the tree.
|
|
72
|
+
* @remarks
|
|
73
|
+
* Example: Get the parent with `const parent = context.parent(childNode);`
|
|
74
|
+
*/
|
|
75
|
+
parent(child: TreeNode): TreeNode | undefined;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Returns the key or index of the given node within its parent.
|
|
79
|
+
* @param child - The node whose key you want to find.
|
|
80
|
+
* @returns A string key if the node is in an object or map, or a numeric index if the node is in an array.
|
|
81
|
+
* @remarks
|
|
82
|
+
* For a node in an object, this might return a string like "firstName".
|
|
83
|
+
* For a node in an array, this might return a number like 0, 1, 2, etc.
|
|
84
|
+
*
|
|
85
|
+
* Example: `const key = context.key(childNode);`
|
|
86
|
+
*/
|
|
87
|
+
key(child: TreeNode): string | number;
|
|
88
|
+
}
|
|
89
|
+
|
|
25
90
|
/**
|
|
26
91
|
* A synchronous function that executes a string of JavaScript code to perform an edit within a {@link SharedTreeSemanticAgent}.
|
|
27
92
|
* @param context - An object that must be provided to the generated code as a variable named "context" in its top-level scope.
|
package/src/prompt.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { oob } from "@fluidframework/core-utils/internal";
|
|
6
7
|
import { NodeKind, Tree, TreeNode } from "@fluidframework/tree";
|
|
7
8
|
import type { ImplicitFieldSchema, TreeMapNode } from "@fluidframework/tree";
|
|
8
9
|
import type { ReadableField } from "@fluidframework/tree/alpha";
|
|
@@ -34,10 +35,20 @@ export function getPrompt<TRoot extends ImplicitFieldSchema>(args: {
|
|
|
34
35
|
const mapInterfaceName = "TreeMap";
|
|
35
36
|
const simpleSchema = getSimpleSchema(schema);
|
|
36
37
|
// Inspect the schema to determine what kinds of nodes are possible - this will affect how much information we need to include in the prompt.
|
|
38
|
+
const rootTypes = [...normalizeFieldSchema(schema).allowedTypeSet];
|
|
39
|
+
const rootTypeUnion = `${rootTypes.map((t) => getFriendlyName(t)).join(" | ")}`;
|
|
40
|
+
let nodeTypeUnion: string | undefined;
|
|
37
41
|
let hasArrays = false;
|
|
38
42
|
let hasMaps = false;
|
|
39
43
|
let exampleObjectName: string | undefined;
|
|
40
44
|
for (const [definition, nodeSchema] of simpleSchema.definitions) {
|
|
45
|
+
if (nodeSchema.kind !== NodeKind.Leaf) {
|
|
46
|
+
nodeTypeUnion =
|
|
47
|
+
nodeTypeUnion === undefined
|
|
48
|
+
? unqualifySchema(definition)
|
|
49
|
+
: `${nodeTypeUnion} | ${unqualifySchema(definition)}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
switch (nodeSchema.kind) {
|
|
42
53
|
case NodeKind.Array: {
|
|
43
54
|
hasArrays = true;
|
|
@@ -68,24 +79,81 @@ export function getPrompt<TRoot extends ImplicitFieldSchema>(args: {
|
|
|
68
79
|
const stringified = stringifyTree(field);
|
|
69
80
|
const details: SchemaDetails = { hasHelperMethods: false };
|
|
70
81
|
const typescriptSchemaTypes = getZodSchemaAsTypeScript(domainTypes, details);
|
|
71
|
-
const helperMethodExplanation = details.hasHelperMethods
|
|
72
|
-
? `Manipulating the data using the APIs described below is allowed, but when possible ALWAYS prefer to use any application helper methods exposed on the schema TypeScript types if the goal can be accomplished that way.
|
|
73
|
-
It will often not be possible to fully accomplish the goal using those helpers. When this is the case, mutate the objects as normal, taking into account the following guidance.`
|
|
74
|
-
: "";
|
|
75
82
|
|
|
76
|
-
const
|
|
83
|
+
const create =
|
|
84
|
+
exampleObjectName === undefined
|
|
85
|
+
? ""
|
|
86
|
+
: `\n /**
|
|
87
|
+
* A collection of builder functions for creating new tree nodes.
|
|
88
|
+
* @remarks
|
|
89
|
+
* Each property on this object is named after a type in the tree schema.
|
|
90
|
+
* Call the corresponding function to create a new node of that type.
|
|
91
|
+
* Always use these builder functions when creating new nodes rather than plain JavaScript objects.
|
|
92
|
+
*
|
|
93
|
+
* For example:
|
|
94
|
+
*
|
|
95
|
+
* \`\`\`javascript
|
|
96
|
+
* // This creates a new ${exampleObjectName} object:
|
|
97
|
+
* const ${communize(exampleObjectName)} = context.create.${exampleObjectName}({ ...properties });
|
|
98
|
+
* // Don't do this:
|
|
99
|
+
* // const ${communize(exampleObjectName)} = { ...properties };
|
|
100
|
+
* \`\`\`
|
|
101
|
+
*/
|
|
102
|
+
create: Record<string, <T extends TreeData>(input: T) => T>;\n`;
|
|
103
|
+
|
|
104
|
+
const isDocs =
|
|
77
105
|
exampleObjectName === undefined
|
|
78
106
|
? ""
|
|
79
|
-
:
|
|
80
|
-
|
|
81
|
-
|
|
107
|
+
: `\n /**
|
|
108
|
+
* A collection of type-guard functions for data in the tree.
|
|
109
|
+
* @remarks
|
|
110
|
+
* Each property on this object is named after a type in the tree schema.
|
|
111
|
+
* Call the corresponding function to check if a node is of that specific type.
|
|
112
|
+
* This is useful when working with nodes that could be one of multiple types.
|
|
113
|
+
*
|
|
114
|
+
* ${`Example: Check if a node is a ${exampleObjectName} with \`if (context.is.${exampleObjectName}(node)) {}\``}
|
|
115
|
+
*/
|
|
116
|
+
is: Record<string, <T extends TreeData>(input: unknown) => input is T>;\n`;
|
|
82
117
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
118
|
+
const context = `\`\`\`typescript
|
|
119
|
+
${nodeTypeUnion === undefined ? "" : `type TreeData = ${nodeTypeUnion};\n\n`} /**
|
|
120
|
+
* An object available to generated code which provides read and write access to the tree as well as utilities for creating and inspecting data in the tree.
|
|
121
|
+
* @remarks This object is available as a variable named \`context\` in the scope of the generated JavaScript snippet.
|
|
122
|
+
*/
|
|
123
|
+
interface Context<TSchema extends ImplicitFieldSchema> {
|
|
124
|
+
/**
|
|
125
|
+
* The root of the tree that can be read or mutated.
|
|
126
|
+
* @remarks
|
|
127
|
+
* You can read properties and navigate through the tree starting from this root.
|
|
128
|
+
* You can also assign a new value to this property to replace the entire tree, as long as the new value is one of the types allowed at the root.
|
|
129
|
+
*
|
|
130
|
+
* Example: Read the current root with \`const currentRoot = context.root;\`
|
|
131
|
+
*${rootTypes.length > 0 ? ` Example: Replace the entire root with \`context.root = context.create.${getFriendlyName(rootTypes[0] ?? oob())}({ });\`\n *` : ""}/
|
|
132
|
+
root: ReadableField<TSchema>;
|
|
133
|
+
${create}
|
|
134
|
+
${isDocs}
|
|
135
|
+
/**
|
|
136
|
+
* Returns the parent object/array/map of the given object/array/map, if there is one.
|
|
137
|
+
* @returns The parent node, or \`undefined\` if the node is the root or is not in the tree.
|
|
138
|
+
* @remarks
|
|
139
|
+
* Example: Get the parent with \`const parent = context.parent(child);\`
|
|
140
|
+
*/
|
|
141
|
+
parent(child: TreeData): TreeData | undefined;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Returns the property key or index of the given object/array/map within its parent.
|
|
145
|
+
* @returns A string key if the child is in an object or map, or a numeric index if the child is in an array.
|
|
146
|
+
*
|
|
147
|
+
* Example: \`const key = context.key(child);\`
|
|
148
|
+
*/
|
|
149
|
+
key(child: TreeData): string | number;
|
|
150
|
+
}
|
|
151
|
+
\`\`\``;
|
|
152
|
+
|
|
153
|
+
const helperMethodExplanation = details.hasHelperMethods
|
|
154
|
+
? `Manipulating the data using the APIs described below is allowed, but when possible ALWAYS prefer to use any application helper methods exposed on the schema TypeScript types if the goal can be accomplished that way.
|
|
155
|
+
It will often not be possible to fully accomplish the goal using those helpers. When this is the case, mutate the objects as normal, taking into account the following guidance.`
|
|
156
|
+
: "";
|
|
89
157
|
|
|
90
158
|
const reinsertionExplanation = `Once non-primitive data has been removed from the tree (e.g. replaced via assignment, or removed from an array), that data cannot be re-inserted into the tree.
|
|
91
159
|
Instead, it must be deep cloned and recreated.
|
|
@@ -160,7 +228,6 @@ ${getTreeMapNodeDocumentation(mapInterfaceName)}
|
|
|
160
228
|
|
|
161
229
|
`;
|
|
162
230
|
|
|
163
|
-
const rootTypes = normalizeFieldSchema(schema).allowedTypeSet;
|
|
164
231
|
const editing = `If the user asks you to edit the tree, you should author a snippet of JavaScript code to accomplish the user-specified goal, following the instructions for editing detailed below.
|
|
165
232
|
You must use the "${editToolName}" tool to run the generated code.
|
|
166
233
|
After editing the tree, review the latest state of the tree to see if it satisfies the user's request.
|
|
@@ -173,14 +240,18 @@ If the user asks you to edit the document, you will write a snippet of JavaScrip
|
|
|
173
240
|
The snippet may be synchronous or asynchronous (i.e. it may \`await\` functions if necessary).
|
|
174
241
|
The snippet has a \`context\` variable in its scope.
|
|
175
242
|
This \`context\` variable holds the current state of the tree in the \`root\` property.
|
|
176
|
-
You may mutate any part of
|
|
177
|
-
You may also set the \`root\` property of the context to be an entirely new value as long as it is one of the types allowed at the root of the tree (\`${
|
|
243
|
+
You may mutate any part of this tree as necessary, taking into account the caveats around${hasArrays ? ` arrays${hasMaps ? " and" : ""}` : ""}${hasMaps ? " maps" : ""} detailed below.
|
|
244
|
+
You may also set the \`root\` property of the context to be an entirely new value as long as it is one of the types allowed at the root of the tree (\`${rootTypeUnion}\`).
|
|
245
|
+
You should also use the \`context\` object to create new data to insert into the tree, using the builder functions available on the \`create\` property.
|
|
246
|
+
There are other additional helper functions available on the \`context\` object to help you analyze the tree.
|
|
247
|
+
Here is the definition of the \`Context\` interface:
|
|
248
|
+
${context}
|
|
178
249
|
${helperMethodExplanation}
|
|
179
250
|
${hasArrays ? arrayEditing : ""}${hasMaps ? mapEditing : ""}#### Additional Notes
|
|
180
251
|
|
|
181
252
|
Before outputting the edit function, you should check that it is valid according to both the application tree's schema and any restrictions of the editing APIs described above.
|
|
182
253
|
|
|
183
|
-
${
|
|
254
|
+
${reinsertionExplanation}
|
|
184
255
|
|
|
185
256
|
Finally, double check that the edits would accomplish the user's request (if it is possible).
|
|
186
257
|
|