@fluidframework/tree-agent 2.63.0-359286 → 2.63.0-359734

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.
Files changed (65) hide show
  1. package/api-report/tree-agent.alpha.api.md +16 -18
  2. package/dist/agent.d.ts +1 -2
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +24 -51
  5. package/dist/agent.js.map +1 -1
  6. package/dist/alpha.d.ts +1 -0
  7. package/dist/api.d.ts +38 -20
  8. package/dist/api.d.ts.map +1 -1
  9. package/dist/api.js.map +1 -1
  10. package/dist/index.d.ts +2 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +10 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/langchain.d.ts +3 -4
  15. package/dist/langchain.d.ts.map +1 -1
  16. package/dist/langchain.js +2 -4
  17. package/dist/langchain.js.map +1 -1
  18. package/dist/prompt.d.ts +1 -2
  19. package/dist/prompt.d.ts.map +1 -1
  20. package/dist/prompt.js +18 -21
  21. package/dist/prompt.js.map +1 -1
  22. package/dist/ses.d.ts +21 -0
  23. package/dist/ses.d.ts.map +1 -0
  24. package/dist/ses.js +64 -0
  25. package/dist/ses.js.map +1 -0
  26. package/dist/utils.d.ts +4 -0
  27. package/dist/utils.d.ts.map +1 -1
  28. package/dist/utils.js +16 -1
  29. package/dist/utils.js.map +1 -1
  30. package/lib/agent.d.ts +1 -2
  31. package/lib/agent.d.ts.map +1 -1
  32. package/lib/agent.js +25 -52
  33. package/lib/agent.js.map +1 -1
  34. package/lib/alpha.d.ts +1 -0
  35. package/lib/api.d.ts +38 -20
  36. package/lib/api.d.ts.map +1 -1
  37. package/lib/api.js.map +1 -1
  38. package/lib/index.d.ts +2 -1
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +7 -0
  41. package/lib/index.js.map +1 -1
  42. package/lib/langchain.d.ts +3 -4
  43. package/lib/langchain.d.ts.map +1 -1
  44. package/lib/langchain.js +2 -4
  45. package/lib/langchain.js.map +1 -1
  46. package/lib/prompt.d.ts +1 -2
  47. package/lib/prompt.d.ts.map +1 -1
  48. package/lib/prompt.js +18 -21
  49. package/lib/prompt.js.map +1 -1
  50. package/lib/ses.d.ts +21 -0
  51. package/lib/ses.d.ts.map +1 -0
  52. package/lib/ses.js +60 -0
  53. package/lib/ses.js.map +1 -0
  54. package/lib/utils.d.ts +4 -0
  55. package/lib/utils.d.ts.map +1 -1
  56. package/lib/utils.js +14 -0
  57. package/lib/utils.js.map +1 -1
  58. package/package.json +10 -9
  59. package/src/agent.ts +34 -74
  60. package/src/api.ts +39 -23
  61. package/src/index.ts +2 -1
  62. package/src/langchain.ts +6 -8
  63. package/src/prompt.ts +19 -23
  64. package/src/ses.ts +73 -0
  65. package/src/utils.ts +14 -0
package/src/prompt.ts CHANGED
@@ -25,11 +25,10 @@ import {
25
25
  */
26
26
  export function getPrompt<TRoot extends ImplicitFieldSchema>(args: {
27
27
  subtree: Subtree<TRoot>;
28
- editToolName?: string;
29
- editFunctionName?: string;
28
+ editToolName: string | undefined;
30
29
  domainHints?: string;
31
30
  }): string {
32
- const { subtree, editToolName, editFunctionName, domainHints } = args;
31
+ const { subtree, editToolName, domainHints } = args;
33
32
  const { field, schema } = subtree;
34
33
  const arrayInterfaceName = "TreeArray";
35
34
  const mapInterfaceName = "TreeMap";
@@ -70,7 +69,7 @@ export function getPrompt<TRoot extends ImplicitFieldSchema>(args: {
70
69
  const details: SchemaDetails = { hasHelperMethods: false };
71
70
  const typescriptSchemaTypes = getZodSchemaAsTypeScript(domainTypes, details);
72
71
  const helperMethodExplanation = details.hasHelperMethods
73
- ? `Manipulating the data using the APIs described below is allowed, but when possible ALWAYS prefer to use the application helper methods exposed on the schema TypeScript types if the goal can be accomplished that way.
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.
74
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.`
75
74
  : "";
76
75
 
@@ -78,16 +77,14 @@ It will often not be possible to fully accomplish the goal using those helpers.
78
77
  exampleObjectName === undefined
79
78
  ? ""
80
79
  : `When constructing new objects, you should wrap them in the appropriate builder function rather than simply making a javascript object.
81
- The builders are available on the "create" property on the first argument of the \`${editFunctionName}\` function and are named according to the type that they create.
80
+ The builders are available on the \`create\` property on the context object and are named according to the type that they create.
82
81
  For example:
83
82
 
84
83
  \`\`\`javascript
85
- function ${editFunctionName}({ root, create }) {
86
- // This creates a new ${exampleObjectName} object:
87
- const ${communize(exampleObjectName)} = create.${exampleObjectName}({ /* ...properties... */ });
88
- // Don't do this:
89
- // const ${communize(exampleObjectName)} = { /* ...properties... */ };
90
- }
84
+ // This creates a new ${exampleObjectName} object:
85
+ const ${communize(exampleObjectName)} = context.create.${exampleObjectName}({ /* ...properties... */ });
86
+ // Don't do this:
87
+ // const ${communize(exampleObjectName)} = { /* ...properties... */ };
91
88
  \`\`\`\n\n`;
92
89
 
93
90
  const arrayEditing = `#### Editing Arrays
@@ -121,26 +118,25 @@ ${getTreeMapNodeDocumentation(mapInterfaceName)}
121
118
  `;
122
119
 
123
120
  const rootTypes = normalizeFieldSchema(schema).allowedTypeSet;
124
- const editing = `If the user asks you to edit the tree, you should author a JavaScript function to accomplish the user-specified goal, following the instructions for editing detailed below.
125
- ${editToolName === undefined ? "Use an applicable tool to perform the edit (if one is available)." : `You must use the "${editToolName}" tool to perform the edit.`}
121
+ 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.
122
+ You must use the "${editToolName}" tool to run the generated code.
126
123
  After editing the tree, review the latest state of the tree to see if it satisfies the user's request.
127
124
  If it does not, or if you receive an error, you may try again with a different approach.
128
125
  Once the tree is in the desired state, you should inform the user that the request has been completed.
129
126
 
130
127
  ### Editing
131
128
 
132
- If the user asks you to edit the document, you will write a JavaScript function that mutates the data in-place to achieve the user's goal.
133
- The function must be named "${editFunctionName}".
134
- It may be synchronous or asynchronous.
135
- The ${editFunctionName} function must have a first parameter which has a \`root\` property.
136
- This \`root\` property holds the current state of the tree as shown above.
137
- You may mutate any part of the tree as necessary, taking into account the caveats around arrays and maps detailed below.
138
- You may also set the \`root\` property to be an entirely new value as long as it is one of the types allowed at the root of the tree (\`${Array.from(rootTypes.values(), (t) => getFriendlyName(t)).join(" | ")}\`).
129
+ If the user asks you to edit the document, you will write a snippet of JavaScript code that mutates the data in-place to achieve the user's goal.
130
+ The snippet may be synchronous or asynchronous (i.e. it may \`await\` functions if necessary).
131
+ The snippet has a \`context\` variable in its scope.
132
+ This \`context\` variable holds the current state of the tree in the \`root\` property.
133
+ You may mutate any part of the root tree as necessary, taking into account the caveats around${hasArrays ? ` arrays${hasMaps ? " and" : ""}` : ""}${hasMaps ? " maps" : ""} detailed below.
134
+ 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 (\`${Array.from(rootTypes.values(), (t) => getFriendlyName(t)).join(" | ")}\`).
139
135
  ${helperMethodExplanation}
140
136
 
141
137
  ${hasArrays ? arrayEditing : ""}${hasMaps ? mapEditing : ""}#### Additional Notes
142
138
 
143
- Before outputting the ${editFunctionName} 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.
139
+ 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.
144
140
 
145
141
  Once 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 - instead, it must be deep cloned and recreated.
146
142
 
@@ -158,14 +154,14 @@ ${typescriptSchemaTypes}
158
154
  If the user asks you a question about the tree, you should inspect the state of the tree and answer the question.
159
155
  When answering such a question, DO NOT answer with information that is not part of the document unless requested to do so.
160
156
 
161
- ${editFunctionName === undefined ? "" : editing}### Application data
157
+ ${editToolName === undefined ? "" : editing}### Application data
162
158
 
163
159
  ${
164
160
  domainHints === undefined
165
161
  ? ""
166
162
  : `\nThe application supplied the following additional instructions: ${domainHints}`
167
163
  }
168
- The current state of the application tree (a \`${field === undefined ? "undefined" : getFriendlyName(Tree.schema(field))}\`) is:
164
+ The current state of \`context.root\` (a \`${field === undefined ? "undefined" : getFriendlyName(Tree.schema(field))}\`) is:
169
165
 
170
166
  \`\`\`JSON
171
167
  ${stringified}
package/src/ses.ts ADDED
@@ -0,0 +1,73 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import type { CompartmentOptions, LockdownOptions } from "ses";
7
+
8
+ import type { SemanticAgentOptions } from "./api.js";
9
+ import { toErrorString } from "./utils.js";
10
+
11
+ const lockdownSymbol = Symbol.for("tree-agent.ses.locked");
12
+
13
+ /**
14
+ * Create an implementation of {@link SemanticAgentOptions.executeEdit} that uses the SES library to run the provided code in a secure environment.
15
+ * @param createCompartment - This function can be used to optionally configure the SES Compartment used to execute the code.
16
+ * The provided globals must be included in the compartment's globals and must not conflict with any additional globals passed in.
17
+ * @param lockdownOptions - Optional configuration passed to the SES `lockdown` function.
18
+ * @returns A function that can be used as the {@link SemanticAgentOptions.executeEdit | executeEdit} callback.
19
+ * @remarks This function will both import the SES library and call its `lockdown` function the first time it is called.
20
+ * Therefore, this function should be called only once, early in an application's lifetime.
21
+ * @alpha
22
+ */
23
+ export async function createSesEditEvaluator(options?: {
24
+ compartmentOptions?: CompartmentOptions;
25
+ lockdownOptions?: LockdownOptions;
26
+ }): Promise<SemanticAgentOptions["executeEdit"]> {
27
+ const optionsGlobals: Map<string, unknown> =
28
+ options?.compartmentOptions?.globals ?? new Map<string, unknown>();
29
+ if (optionsGlobals.has("context") === true) {
30
+ throw new Error(
31
+ "The 'context' global is reserved and cannot be overridden in the compartment options.",
32
+ );
33
+ }
34
+
35
+ // Importing 'ses' has side effects, so we do it lazily to avoid impacting environments that don't use this evaluator.
36
+ await import("ses");
37
+
38
+ if (!(lockdownSymbol in globalThis)) {
39
+ try {
40
+ lockdown(options?.lockdownOptions);
41
+ Object.defineProperty(globalThis, lockdownSymbol, {
42
+ value: true,
43
+ writable: false,
44
+ configurable: false,
45
+ enumerable: false,
46
+ });
47
+ } catch (error: unknown) {
48
+ if (toErrorString(error).includes("SES_ALREADY_LOCKED_DOWN")) {
49
+ Object.defineProperty(globalThis, lockdownSymbol, {
50
+ value: true,
51
+ writable: false,
52
+ configurable: false,
53
+ enumerable: false,
54
+ });
55
+ } else {
56
+ throw error;
57
+ }
58
+ }
59
+ }
60
+
61
+ return async (context: Record<string, unknown>, code: string) => {
62
+ const compartmentOptions = {
63
+ ...options?.compartmentOptions,
64
+ globals: {
65
+ ...Object.fromEntries(optionsGlobals),
66
+ context,
67
+ },
68
+ };
69
+
70
+ const compartment = new Compartment({ ...compartmentOptions, __options__: true });
71
+ await compartment.evaluate(code);
72
+ };
73
+ }
package/src/utils.ts CHANGED
@@ -619,3 +619,17 @@ export function findNamedSchemas(
619
619
  export function communize(str: string): string {
620
620
  return str.charAt(0).toLowerCase() + str.slice(1);
621
621
  }
622
+
623
+ /**
624
+ * Stringify an unknown error value
625
+ */
626
+ export function toErrorString(error: unknown): string {
627
+ if (error instanceof Error) {
628
+ return error.message;
629
+ }
630
+ try {
631
+ return JSON.stringify(error);
632
+ } catch {
633
+ return String(error);
634
+ }
635
+ }