@fluidframework/ai-collab 2.10.0-306579

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 (168) hide show
  1. package/.eslintrc.cjs +26 -0
  2. package/CHANGELOG.md +9 -0
  3. package/LICENSE +21 -0
  4. package/README.md +280 -0
  5. package/alpha.d.ts +11 -0
  6. package/api-extractor/api-extractor-lint-alpha.cjs.json +5 -0
  7. package/api-extractor/api-extractor-lint-alpha.esm.json +5 -0
  8. package/api-extractor/api-extractor-lint-bundle.json +5 -0
  9. package/api-extractor/api-extractor-lint-index.cjs.json +5 -0
  10. package/api-extractor/api-extractor-lint-index.esm.json +5 -0
  11. package/api-extractor/api-extractor-lint-public.cjs.json +5 -0
  12. package/api-extractor/api-extractor-lint-public.esm.json +5 -0
  13. package/api-extractor-lint.json +4 -0
  14. package/api-extractor.json +4 -0
  15. package/api-report/ai-collab.alpha.api.md +164 -0
  16. package/api-report/ai-collab.beta.api.md +7 -0
  17. package/api-report/ai-collab.public.api.md +7 -0
  18. package/biome.jsonc +4 -0
  19. package/dist/aiCollab.d.ts +65 -0
  20. package/dist/aiCollab.d.ts.map +1 -0
  21. package/dist/aiCollab.js +81 -0
  22. package/dist/aiCollab.js.map +1 -0
  23. package/dist/aiCollabApi.d.ts +173 -0
  24. package/dist/aiCollabApi.d.ts.map +1 -0
  25. package/dist/aiCollabApi.js +7 -0
  26. package/dist/aiCollabApi.js.map +1 -0
  27. package/dist/alpha.d.ts +41 -0
  28. package/dist/explicit-strategy/agentEditReducer.d.ts +12 -0
  29. package/dist/explicit-strategy/agentEditReducer.d.ts.map +1 -0
  30. package/dist/explicit-strategy/agentEditReducer.js +394 -0
  31. package/dist/explicit-strategy/agentEditReducer.js.map +1 -0
  32. package/dist/explicit-strategy/agentEditTypes.d.ts +158 -0
  33. package/dist/explicit-strategy/agentEditTypes.d.ts.map +1 -0
  34. package/dist/explicit-strategy/agentEditTypes.js +50 -0
  35. package/dist/explicit-strategy/agentEditTypes.js.map +1 -0
  36. package/dist/explicit-strategy/idGenerator.d.ts +22 -0
  37. package/dist/explicit-strategy/idGenerator.d.ts.map +1 -0
  38. package/dist/explicit-strategy/idGenerator.js +74 -0
  39. package/dist/explicit-strategy/idGenerator.js.map +1 -0
  40. package/dist/explicit-strategy/index.d.ts +51 -0
  41. package/dist/explicit-strategy/index.d.ts.map +1 -0
  42. package/dist/explicit-strategy/index.js +223 -0
  43. package/dist/explicit-strategy/index.js.map +1 -0
  44. package/dist/explicit-strategy/jsonTypes.d.ts +23 -0
  45. package/dist/explicit-strategy/jsonTypes.d.ts.map +1 -0
  46. package/dist/explicit-strategy/jsonTypes.js +7 -0
  47. package/dist/explicit-strategy/jsonTypes.js.map +1 -0
  48. package/dist/explicit-strategy/promptGeneration.d.ts +51 -0
  49. package/dist/explicit-strategy/promptGeneration.d.ts.map +1 -0
  50. package/dist/explicit-strategy/promptGeneration.js +218 -0
  51. package/dist/explicit-strategy/promptGeneration.js.map +1 -0
  52. package/dist/explicit-strategy/typeGeneration.d.ts +15 -0
  53. package/dist/explicit-strategy/typeGeneration.d.ts.map +1 -0
  54. package/dist/explicit-strategy/typeGeneration.js +264 -0
  55. package/dist/explicit-strategy/typeGeneration.js.map +1 -0
  56. package/dist/explicit-strategy/utils.d.ts +37 -0
  57. package/dist/explicit-strategy/utils.d.ts.map +1 -0
  58. package/dist/explicit-strategy/utils.js +47 -0
  59. package/dist/explicit-strategy/utils.js.map +1 -0
  60. package/dist/implicit-strategy/index.d.ts +8 -0
  61. package/dist/implicit-strategy/index.d.ts.map +1 -0
  62. package/dist/implicit-strategy/index.js +18 -0
  63. package/dist/implicit-strategy/index.js.map +1 -0
  64. package/dist/implicit-strategy/sharedTreeBranchManager.d.ts +63 -0
  65. package/dist/implicit-strategy/sharedTreeBranchManager.d.ts.map +1 -0
  66. package/dist/implicit-strategy/sharedTreeBranchManager.js +212 -0
  67. package/dist/implicit-strategy/sharedTreeBranchManager.js.map +1 -0
  68. package/dist/implicit-strategy/sharedTreeDiff.d.ts +102 -0
  69. package/dist/implicit-strategy/sharedTreeDiff.d.ts.map +1 -0
  70. package/dist/implicit-strategy/sharedTreeDiff.js +522 -0
  71. package/dist/implicit-strategy/sharedTreeDiff.js.map +1 -0
  72. package/dist/implicit-strategy/utils.d.ts +21 -0
  73. package/dist/implicit-strategy/utils.d.ts.map +1 -0
  74. package/dist/implicit-strategy/utils.js +49 -0
  75. package/dist/implicit-strategy/utils.js.map +1 -0
  76. package/dist/index.d.ts +16 -0
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +24 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/package.json +3 -0
  81. package/dist/public.d.ts +19 -0
  82. package/eslintrc.cjs +11 -0
  83. package/internal.d.ts +11 -0
  84. package/lib/aiCollab.d.ts +65 -0
  85. package/lib/aiCollab.d.ts.map +1 -0
  86. package/lib/aiCollab.js +77 -0
  87. package/lib/aiCollab.js.map +1 -0
  88. package/lib/aiCollabApi.d.ts +173 -0
  89. package/lib/aiCollabApi.d.ts.map +1 -0
  90. package/lib/aiCollabApi.js +6 -0
  91. package/lib/aiCollabApi.js.map +1 -0
  92. package/lib/alpha.d.ts +41 -0
  93. package/lib/explicit-strategy/agentEditReducer.d.ts +12 -0
  94. package/lib/explicit-strategy/agentEditReducer.d.ts.map +1 -0
  95. package/lib/explicit-strategy/agentEditReducer.js +390 -0
  96. package/lib/explicit-strategy/agentEditReducer.js.map +1 -0
  97. package/lib/explicit-strategy/agentEditTypes.d.ts +158 -0
  98. package/lib/explicit-strategy/agentEditTypes.d.ts.map +1 -0
  99. package/lib/explicit-strategy/agentEditTypes.js +47 -0
  100. package/lib/explicit-strategy/agentEditTypes.js.map +1 -0
  101. package/lib/explicit-strategy/idGenerator.d.ts +22 -0
  102. package/lib/explicit-strategy/idGenerator.d.ts.map +1 -0
  103. package/lib/explicit-strategy/idGenerator.js +70 -0
  104. package/lib/explicit-strategy/idGenerator.js.map +1 -0
  105. package/lib/explicit-strategy/index.d.ts +51 -0
  106. package/lib/explicit-strategy/index.d.ts.map +1 -0
  107. package/lib/explicit-strategy/index.js +219 -0
  108. package/lib/explicit-strategy/index.js.map +1 -0
  109. package/lib/explicit-strategy/jsonTypes.d.ts +23 -0
  110. package/lib/explicit-strategy/jsonTypes.d.ts.map +1 -0
  111. package/lib/explicit-strategy/jsonTypes.js +6 -0
  112. package/lib/explicit-strategy/jsonTypes.js.map +1 -0
  113. package/lib/explicit-strategy/promptGeneration.d.ts +51 -0
  114. package/lib/explicit-strategy/promptGeneration.d.ts.map +1 -0
  115. package/lib/explicit-strategy/promptGeneration.js +208 -0
  116. package/lib/explicit-strategy/promptGeneration.js.map +1 -0
  117. package/lib/explicit-strategy/typeGeneration.d.ts +15 -0
  118. package/lib/explicit-strategy/typeGeneration.d.ts.map +1 -0
  119. package/lib/explicit-strategy/typeGeneration.js +260 -0
  120. package/lib/explicit-strategy/typeGeneration.js.map +1 -0
  121. package/lib/explicit-strategy/utils.d.ts +37 -0
  122. package/lib/explicit-strategy/utils.d.ts.map +1 -0
  123. package/lib/explicit-strategy/utils.js +41 -0
  124. package/lib/explicit-strategy/utils.js.map +1 -0
  125. package/lib/implicit-strategy/index.d.ts +8 -0
  126. package/lib/implicit-strategy/index.d.ts.map +1 -0
  127. package/lib/implicit-strategy/index.js +8 -0
  128. package/lib/implicit-strategy/index.js.map +1 -0
  129. package/lib/implicit-strategy/sharedTreeBranchManager.d.ts +63 -0
  130. package/lib/implicit-strategy/sharedTreeBranchManager.d.ts.map +1 -0
  131. package/lib/implicit-strategy/sharedTreeBranchManager.js +213 -0
  132. package/lib/implicit-strategy/sharedTreeBranchManager.js.map +1 -0
  133. package/lib/implicit-strategy/sharedTreeDiff.d.ts +102 -0
  134. package/lib/implicit-strategy/sharedTreeDiff.d.ts.map +1 -0
  135. package/lib/implicit-strategy/sharedTreeDiff.js +515 -0
  136. package/lib/implicit-strategy/sharedTreeDiff.js.map +1 -0
  137. package/lib/implicit-strategy/utils.d.ts +21 -0
  138. package/lib/implicit-strategy/utils.d.ts.map +1 -0
  139. package/lib/implicit-strategy/utils.js +43 -0
  140. package/lib/implicit-strategy/utils.js.map +1 -0
  141. package/lib/index.d.ts +16 -0
  142. package/lib/index.d.ts.map +1 -0
  143. package/lib/index.js +15 -0
  144. package/lib/index.js.map +1 -0
  145. package/lib/public.d.ts +19 -0
  146. package/lib/tsdoc-metadata.json +11 -0
  147. package/mocharc.cjs +14 -0
  148. package/package.json +165 -0
  149. package/prettier.config.cjs +8 -0
  150. package/src/aiCollab.ts +86 -0
  151. package/src/aiCollabApi.ts +184 -0
  152. package/src/explicit-strategy/agentEditReducer.ts +498 -0
  153. package/src/explicit-strategy/agentEditTypes.ts +177 -0
  154. package/src/explicit-strategy/idGenerator.ts +90 -0
  155. package/src/explicit-strategy/index.ts +364 -0
  156. package/src/explicit-strategy/jsonTypes.ts +27 -0
  157. package/src/explicit-strategy/promptGeneration.ts +294 -0
  158. package/src/explicit-strategy/typeGeneration.ts +374 -0
  159. package/src/explicit-strategy/utils.ts +60 -0
  160. package/src/implicit-strategy/README.md +4 -0
  161. package/src/implicit-strategy/index.ts +21 -0
  162. package/src/implicit-strategy/sharedTreeBranchManager.ts +294 -0
  163. package/src/implicit-strategy/sharedTreeDiff.ts +735 -0
  164. package/src/implicit-strategy/utils.ts +54 -0
  165. package/src/index.ts +39 -0
  166. package/tsconfig.cjs.json +7 -0
  167. package/tsconfig.json +12 -0
  168. package/tsdoc.json +4 -0
@@ -0,0 +1,47 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ /**
6
+ * TODO: The current scheme does not allow manipulation of arrays of primitive values because you cannot refer to them.
7
+ * We could accomplish this via a path (probably JSON Pointer or JSONPath) from a possibly-null objectId, or wrap arrays in an identified object.
8
+ *
9
+ * TODO: only 100 object fields total are allowed by OpenAI right now, so larger schemas will fail faster if we have a bunch of schema types generated for type-specific edits.
10
+ *
11
+ * TODO: experiment using https://github.com/outlines-dev/outlines (and maybe a llama model) to avoid many of the annoyances of OpenAI's JSON Schema subset.
12
+ *
13
+ * TODO: without field count limits, we could generate a schema for valid paths from the root object to any field, but it's not clear how useful that would be.
14
+ *
15
+ * TODO: We don't supported nested arrays yet.
16
+ *
17
+ * TODO: Add a prompt suggestion API!
18
+ *
19
+ * TODO: Could encourage the model to output more technical explanations of the edits (e.g. "insert a new Foo after "Foo2").
20
+ *
21
+ * TODO: Get explanation strings from o1.
22
+ *
23
+ * TODO: Tests of range edits.
24
+ *
25
+ * TODO: Handle 429 rate limit error from OpenAI.
26
+ *
27
+ * TODO: Add an app-specific guidance string.
28
+ *
29
+ * TODO: Give the model a final chance to evaluate the result.
30
+ *
31
+ * TODO: Separate system prompt into [system, user, system] for security.
32
+ *
33
+ * TODO: Top level arrays are not supported with current DSL.
34
+ *
35
+ * TODO: Structured Output fails when multiple schema types have the same first field name (e.g. id: sf.identifier on multiple types).
36
+ *
37
+ * TODO: Pass descriptions from schema metadata to the generated TS types that we put in the prompt
38
+ */
39
+ /**
40
+ * This is the field we force the LLM to generate to avoid any type ambiguity (e.g. a vector and a point both have x/y and are ambiguous without the LLM telling us which it means).
41
+ */
42
+ export const typeField = "__fluid_type";
43
+ /**
44
+ * A field that is auto-generated and injected into nodes before passing data to the LLM to ensure the LLM can refer to nodes in a stable way.
45
+ */
46
+ export const objectIdKey = "__fluid_objectId";
47
+ //# sourceMappingURL=agentEditTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentEditTypes.js","sourceRoot":"","sources":["../../src/explicit-strategy/agentEditTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,cAAc,CAAC;AAExC;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { JsonPrimitive } from \"./jsonTypes.js\";\n\n/**\n * TODO: The current scheme does not allow manipulation of arrays of primitive values because you cannot refer to them.\n * We could accomplish this via a path (probably JSON Pointer or JSONPath) from a possibly-null objectId, or wrap arrays in an identified object.\n *\n * TODO: only 100 object fields total are allowed by OpenAI right now, so larger schemas will fail faster if we have a bunch of schema types generated for type-specific edits.\n *\n * TODO: experiment using https://github.com/outlines-dev/outlines (and maybe a llama model) to avoid many of the annoyances of OpenAI's JSON Schema subset.\n *\n * TODO: without field count limits, we could generate a schema for valid paths from the root object to any field, but it's not clear how useful that would be.\n *\n * TODO: We don't supported nested arrays yet.\n *\n * TODO: Add a prompt suggestion API!\n *\n * TODO: Could encourage the model to output more technical explanations of the edits (e.g. \"insert a new Foo after \"Foo2\").\n *\n * TODO: Get explanation strings from o1.\n *\n * TODO: Tests of range edits.\n *\n * TODO: Handle 429 rate limit error from OpenAI.\n *\n * TODO: Add an app-specific guidance string.\n *\n * TODO: Give the model a final chance to evaluate the result.\n *\n * TODO: Separate system prompt into [system, user, system] for security.\n *\n * TODO: Top level arrays are not supported with current DSL.\n *\n * TODO: Structured Output fails when multiple schema types have the same first field name (e.g. id: sf.identifier on multiple types).\n *\n * TODO: Pass descriptions from schema metadata to the generated TS types that we put in the prompt\n */\n\n/**\n * This is the field we force the LLM to generate to avoid any type ambiguity (e.g. a vector and a point both have x/y and are ambiguous without the LLM telling us which it means).\n */\nexport const typeField = \"__fluid_type\";\n\n/**\n * A field that is auto-generated and injected into nodes before passing data to the LLM to ensure the LLM can refer to nodes in a stable way.\n */\nexport const objectIdKey = \"__fluid_objectId\";\n\n/**\n * Describes an edit to a field within a node.\n * @remarks TODO: what is the [key: string] for?\n */\nexport interface TreeEditObject {\n\t[key: string]: TreeEditValue;\n\t[typeField]: string;\n}\n/**\n * An array of {@link TreeEditValue}'s, allowing a single {@link TreeEdit} to contain edits to multiple fields.\n */\nexport type TreeEditArray = TreeEditValue[];\n\n/**\n * The potential values for a given {@link TreeEdit}.\n * @remarks These values are typically a field within a node or an entire node,\n */\nexport type TreeEditValue = JsonPrimitive | TreeEditObject | TreeEditArray;\n\n/**\n * This is the the final object we expected from an LLM response.\n * @remarks Because TreeEdit can be multiple different types (polymorphic),\n * we need to wrap to avoid anyOf at the root level when generating the necessary JSON Schema.\n */\nexport interface EditWrapper {\n\t// eslint-disable-next-line @rushstack/no-new-null\n\tedit: TreeEdit | null;\n}\n\n/**\n * Union type representing all possible types of edits that can be made to a tree.\n */\nexport type TreeEdit = Insert | Modify | Remove | Move;\n\n/**\n * The base interface for all types of {@link TreeEdit}.\n */\nexport interface Edit {\n\texplanation: string;\n\ttype: \"insert\" | \"modify\" | \"remove\" | \"move\";\n}\n\n/**\n * This object provides a way to 'select' either a given node or a range of nodes in an array.\n */\nexport type Selection = ObjectTarget | Range;\n\n/**\n * A Target object for an {@link TreeEdit}, identified by the target object's Id\n */\nexport interface ObjectTarget {\n\ttarget: string;\n}\n\n/**\n * Desribes where an object can be inserted into an array.\n * For example, if you have an array with 5 objects, and you insert an object at index 3, this differentiates whether you want\n * the existing item at index 3 to be shifted forward (if the 'location' is 'start') or shifted backwards (if the 'location' is 'end')\n *\n * @remarks TODO: Allow support for nested arrays\n */\nexport interface ArrayPlace {\n\ttype: \"arrayPlace\";\n\tparentId: string;\n\tfield: string;\n\tlocation: \"start\" | \"end\";\n}\n\n/**\n * Desribes where an object can be inserted into an array.\n * For example, if you have an array with 5 objects, and you insert an object at index 3, this differentiates whether you want\n * the existing item at index 3 to be shifted forward (if the 'location' is 'start') or shifted backwards (if the 'location' is 'end')\n *\n * @remarks Why does this and {@link ArrayPlace} exist together?\n */\nexport interface ObjectPlace extends ObjectTarget {\n\ttype: \"objectPlace\";\n\t// No \"start\" or \"end\" because we don't have a way to refer to arrays directly.\n\tplace: \"before\" | \"after\";\n}\n\n/**\n * A range of objects within an array. This allows the LLM to select multiple nodes at once,\n * for example during an {@link Remove} operation to remove a range of nodes.\n */\nexport interface Range {\n\tfrom: ObjectPlace;\n\tto: ObjectPlace;\n}\n\n/**\n * Describes an operation to insert a new node into the tree.\n */\nexport interface Insert extends Edit {\n\ttype: \"insert\";\n\tcontent: TreeEditObject | JsonPrimitive;\n\tdestination: ObjectPlace | ArrayPlace;\n}\n\n/**\n * Describes an operation to modify an existing node in the tree.\n */\nexport interface Modify extends Edit {\n\ttype: \"modify\";\n\ttarget: ObjectTarget;\n\tfield: string;\n\tmodification: TreeEditValue;\n}\n\n/**\n * Describes an operation to remove either a specific node or a range of nodes in an array.\n */\nexport interface Remove extends Edit {\n\ttype: \"remove\";\n\tsource: Selection;\n}\n\n/**\n * Describes an operation to move a node within an array\n */\nexport interface Move extends Edit {\n\ttype: \"move\";\n\tsource: Selection;\n\tdestination: ObjectPlace | ArrayPlace;\n}\n"]}
@@ -0,0 +1,22 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import type { TreeNode, ImplicitFieldSchema, TreeFieldFromImplicitField } from "@fluidframework/tree/internal";
6
+ /**
7
+ * Given a tree, generates a set of LLM-friendly, unique IDs for each node in the tree.
8
+ * @remarks The ability to uniquely and stably in the tree is important for the LLM and this library to create and distinguish between different types certain {@link TreeEdit}s.
9
+ */
10
+ export declare class IdGenerator {
11
+ private readonly idCountMap;
12
+ private readonly prefixMap;
13
+ private readonly nodeToIdMap;
14
+ private readonly idToNodeMap;
15
+ constructor();
16
+ getOrCreateId(node: TreeNode): string;
17
+ getNode(id: string): TreeNode | undefined;
18
+ getId(node: TreeNode): string | undefined;
19
+ assignIds(node: TreeFieldFromImplicitField<ImplicitFieldSchema>): string | undefined;
20
+ private generateID;
21
+ }
22
+ //# sourceMappingURL=idGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idGenerator.d.ts","sourceRoot":"","sources":["../../src/explicit-strategy/idGenerator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EACX,QAAQ,EACR,mBAAmB,EAEnB,0BAA0B,EAC1B,MAAM,+BAA+B,CAAC;AAEvC;;;GAGG;AACH,qBAAa,WAAW;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IACxD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IACvD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;;IAIpD,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM;IAcrC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIzC,KAAK,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS;IAIzC,SAAS,CAAC,IAAI,EAAE,0BAA0B,CAAC,mBAAmB,CAAC,GAAG,MAAM,GAAG,SAAS;IAsB3F,OAAO,CAAC,UAAU;CAmBlB"}
@@ -0,0 +1,70 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { assert, oob } from "@fluidframework/core-utils/internal";
6
+ import { Tree, NodeKind } from "@fluidframework/tree/internal";
7
+ /**
8
+ * Given a tree, generates a set of LLM-friendly, unique IDs for each node in the tree.
9
+ * @remarks The ability to uniquely and stably in the tree is important for the LLM and this library to create and distinguish between different types certain {@link TreeEdit}s.
10
+ */
11
+ export class IdGenerator {
12
+ constructor() {
13
+ this.idCountMap = new Map();
14
+ this.prefixMap = new Map();
15
+ this.nodeToIdMap = new Map();
16
+ this.idToNodeMap = new Map();
17
+ }
18
+ getOrCreateId(node) {
19
+ const existingID = this.nodeToIdMap.get(node);
20
+ if (existingID !== undefined) {
21
+ return existingID;
22
+ }
23
+ const schema = Tree.schema(node).identifier;
24
+ const id = this.generateID(schema);
25
+ this.nodeToIdMap.set(node, id);
26
+ this.idToNodeMap.set(id, node);
27
+ return id;
28
+ }
29
+ getNode(id) {
30
+ return this.idToNodeMap.get(id);
31
+ }
32
+ getId(node) {
33
+ return this.nodeToIdMap.get(node);
34
+ }
35
+ assignIds(node) {
36
+ if (typeof node === "object" && node !== null) {
37
+ const schema = Tree.schema(node);
38
+ if (schema.kind === NodeKind.Array) {
39
+ for (const element of node) {
40
+ this.assignIds(element);
41
+ }
42
+ }
43
+ else {
44
+ // TODO: SharedTree Team needs to either publish TreeNode as a class to use .instanceof() or a typeguard.
45
+ // Uncomment this assertion back once we have a typeguard ready.
46
+ // assert(isTreeNode(node), "Non-TreeNode value in tree.");
47
+ const objId = this.getOrCreateId(node);
48
+ for (const key of Object.keys(node)) {
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument
50
+ this.assignIds(node[key]);
51
+ }
52
+ return objId;
53
+ }
54
+ }
55
+ return undefined;
56
+ }
57
+ generateID(schema) {
58
+ const segments = schema.split(".");
59
+ // If there's no period, the schema itself is the last segment
60
+ const lastSegment = segments[segments.length - 1] ?? oob();
61
+ const prefix = segments.length > 1 ? segments.slice(0, -1).join(".") : "";
62
+ // Check if the last segment already exists with a different prefix
63
+ assert(!this.prefixMap.has(lastSegment) || this.prefixMap.get(lastSegment) === prefix, "Different scopes not supported yet.");
64
+ this.prefixMap.set(lastSegment, prefix);
65
+ const count = this.idCountMap.get(lastSegment) ?? 1;
66
+ this.idCountMap.set(lastSegment, count + 1);
67
+ return `${lastSegment}${count}`;
68
+ }
69
+ }
70
+ //# sourceMappingURL=idGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idGenerator.js","sourceRoot":"","sources":["../../src/explicit-strategy/idGenerator.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert, oob } from \"@fluidframework/core-utils/internal\";\nimport { Tree, NodeKind } from \"@fluidframework/tree/internal\";\nimport type {\n\tTreeNode,\n\tImplicitFieldSchema,\n\tTreeArrayNode,\n\tTreeFieldFromImplicitField,\n} from \"@fluidframework/tree/internal\";\n\n/**\n * Given a tree, generates a set of LLM-friendly, unique IDs for each node in the tree.\n * @remarks The ability to uniquely and stably in the tree is important for the LLM and this library to create and distinguish between different types certain {@link TreeEdit}s.\n */\nexport class IdGenerator {\n\tprivate readonly idCountMap = new Map<string, number>();\n\tprivate readonly prefixMap = new Map<string, string>();\n\tprivate readonly nodeToIdMap = new Map<TreeNode, string>();\n\tprivate readonly idToNodeMap = new Map<string, TreeNode>();\n\n\tpublic constructor() {}\n\n\tpublic getOrCreateId(node: TreeNode): string {\n\t\tconst existingID = this.nodeToIdMap.get(node);\n\t\tif (existingID !== undefined) {\n\t\t\treturn existingID;\n\t\t}\n\n\t\tconst schema = Tree.schema(node).identifier;\n\t\tconst id = this.generateID(schema);\n\t\tthis.nodeToIdMap.set(node, id);\n\t\tthis.idToNodeMap.set(id, node);\n\n\t\treturn id;\n\t}\n\n\tpublic getNode(id: string): TreeNode | undefined {\n\t\treturn this.idToNodeMap.get(id);\n\t}\n\n\tpublic getId(node: TreeNode): string | undefined {\n\t\treturn this.nodeToIdMap.get(node);\n\t}\n\n\tpublic assignIds(node: TreeFieldFromImplicitField<ImplicitFieldSchema>): string | undefined {\n\t\tif (typeof node === \"object\" && node !== null) {\n\t\t\tconst schema = Tree.schema(node as unknown as TreeNode);\n\t\t\tif (schema.kind === NodeKind.Array) {\n\t\t\t\tfor (const element of node as unknown as TreeArrayNode) {\n\t\t\t\t\tthis.assignIds(element);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// TODO: SharedTree Team needs to either publish TreeNode as a class to use .instanceof() or a typeguard.\n\t\t\t\t// Uncomment this assertion back once we have a typeguard ready.\n\t\t\t\t// assert(isTreeNode(node), \"Non-TreeNode value in tree.\");\n\t\t\t\tconst objId = this.getOrCreateId(node as TreeNode);\n\t\t\t\tfor (const key of Object.keys(node)) {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument\n\t\t\t\t\tthis.assignIds((node as unknown as any)[key]);\n\t\t\t\t}\n\t\t\t\treturn objId;\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate generateID(schema: string): string {\n\t\tconst segments = schema.split(\".\");\n\n\t\t// If there's no period, the schema itself is the last segment\n\t\tconst lastSegment = segments[segments.length - 1] ?? oob();\n\t\tconst prefix = segments.length > 1 ? segments.slice(0, -1).join(\".\") : \"\";\n\n\t\t// Check if the last segment already exists with a different prefix\n\t\tassert(\n\t\t\t!this.prefixMap.has(lastSegment) || this.prefixMap.get(lastSegment) === prefix,\n\t\t\t\"Different scopes not supported yet.\",\n\t\t);\n\n\t\tthis.prefixMap.set(lastSegment, prefix);\n\t\tconst count = this.idCountMap.get(lastSegment) ?? 1;\n\t\tthis.idCountMap.set(lastSegment, count + 1);\n\n\t\treturn `${lastSegment}${count}`;\n\t}\n}\n"]}
@@ -0,0 +1,51 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { type TreeNode } from "@fluidframework/tree/internal";
6
+ import type { OpenAiClientOptions, TokenLimits, TokenUsage } from "../aiCollabApi.js";
7
+ /**
8
+ * {@link generateTreeEdits} options.
9
+ *
10
+ * @internal
11
+ */
12
+ export interface GenerateTreeEditsOptions {
13
+ openAI: OpenAiClientOptions;
14
+ treeNode: TreeNode;
15
+ prompt: {
16
+ systemRoleContext: string;
17
+ userAsk: string;
18
+ };
19
+ limiters?: {
20
+ abortController?: AbortController;
21
+ maxSequentialErrors?: number;
22
+ maxModelCalls?: number;
23
+ tokenLimits?: TokenLimits;
24
+ };
25
+ finalReviewStep?: boolean;
26
+ validator?: (newContent: TreeNode) => void;
27
+ dumpDebugLog?: boolean;
28
+ planningStep?: boolean;
29
+ }
30
+ interface GenerateTreeEditsSuccessResponse {
31
+ status: "success";
32
+ tokensUsed: TokenUsage;
33
+ }
34
+ interface GenerateTreeEditsErrorResponse {
35
+ status: "failure" | "partial-failure";
36
+ errorMessage: "tokenLimitExceeded" | "tooManyErrors" | "tooManyModelCalls" | "aborted";
37
+ tokensUsed: TokenUsage;
38
+ }
39
+ /**
40
+ * Prompts the provided LLM client to generate valid tree edits.
41
+ * Applies those edits to the provided tree branch before returning.
42
+ *
43
+ * @remarks
44
+ * - Optional root nodes are not supported
45
+ * - Primitive root nodes are not supported
46
+ *
47
+ * @internal
48
+ */
49
+ export declare function generateTreeEdits(options: GenerateTreeEditsOptions): Promise<GenerateTreeEditsSuccessResponse | GenerateTreeEditsErrorResponse>;
50
+ export {};
51
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/explicit-strategy/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAIN,KAAK,QAAQ,EACb,MAAM,+BAA+B,CAAC;AASvC,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAiBtF;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACxC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE;QACP,iBAAiB,EAAE,MAAM,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,CAAC,EAAE;QACV,eAAe,CAAC,EAAE,eAAe,CAAC;QAClC,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,WAAW,CAAC,EAAE,WAAW,CAAC;KAC1B,CAAC;IACF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,CAAC,UAAU,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC3C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,gCAAgC;IACzC,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;CACvB;AAED,UAAU,8BAA8B;IACvC,MAAM,EAAE,SAAS,GAAG,iBAAiB,CAAC;IACtC,YAAY,EAAE,oBAAoB,GAAG,eAAe,GAAG,mBAAmB,GAAG,SAAS,CAAC;IACvF,UAAU,EAAE,UAAU,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,wBAAwB,GAC/B,OAAO,CAAC,gCAAgC,GAAG,8BAA8B,CAAC,CAmG5E"}
@@ -0,0 +1,219 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { getSimpleSchema, Tree, } from "@fluidframework/tree/internal";
6
+ // eslint-disable-next-line import/no-internal-modules
7
+ import { zodResponseFormat } from "openai/helpers/zod";
8
+ import { z } from "zod";
9
+ import { applyAgentEdit } from "./agentEditReducer.js";
10
+ import { IdGenerator } from "./idGenerator.js";
11
+ import { getEditingSystemPrompt, getPlanningSystemPrompt, getReviewSystemPrompt, toDecoratedJson, } from "./promptGeneration.js";
12
+ import { generateGenericEditTypes } from "./typeGeneration.js";
13
+ import { fail } from "./utils.js";
14
+ const DEBUG_LOG = [];
15
+ /**
16
+ * Prompts the provided LLM client to generate valid tree edits.
17
+ * Applies those edits to the provided tree branch before returning.
18
+ *
19
+ * @remarks
20
+ * - Optional root nodes are not supported
21
+ * - Primitive root nodes are not supported
22
+ *
23
+ * @internal
24
+ */
25
+ export async function generateTreeEdits(options) {
26
+ const idGenerator = new IdGenerator();
27
+ const editLog = [];
28
+ let editCount = 0;
29
+ let sequentialErrorCount = 0;
30
+ const simpleSchema = getSimpleSchema(Tree.schema(options.treeNode));
31
+ const tokensUsed = { inputTokens: 0, outputTokens: 0 };
32
+ try {
33
+ for await (const edit of generateEdits(options, simpleSchema, idGenerator, editLog, options.limiters?.tokenLimits, tokensUsed)) {
34
+ try {
35
+ const result = applyAgentEdit(edit, idGenerator, simpleSchema.definitions, options.validator);
36
+ const explanation = result.explanation;
37
+ editLog.push({ edit: { ...result, explanation } });
38
+ sequentialErrorCount = 0;
39
+ }
40
+ catch (error) {
41
+ if (error instanceof Error) {
42
+ sequentialErrorCount += 1;
43
+ editLog.push({ edit, error: error.message });
44
+ DEBUG_LOG?.push(`Error: ${error.message}`);
45
+ }
46
+ else {
47
+ throw error;
48
+ }
49
+ }
50
+ const responseStatus = editCount > 0 && sequentialErrorCount < editCount ? "partial-failure" : "failure";
51
+ if (options.limiters?.abortController?.signal.aborted === true) {
52
+ return {
53
+ status: responseStatus,
54
+ errorMessage: "aborted",
55
+ tokensUsed,
56
+ };
57
+ }
58
+ if (sequentialErrorCount >
59
+ (options.limiters?.maxSequentialErrors ?? Number.POSITIVE_INFINITY)) {
60
+ return {
61
+ status: responseStatus,
62
+ errorMessage: "tooManyErrors",
63
+ tokensUsed,
64
+ };
65
+ }
66
+ if (++editCount >= (options.limiters?.maxModelCalls ?? Number.POSITIVE_INFINITY)) {
67
+ return {
68
+ status: responseStatus,
69
+ errorMessage: "tooManyModelCalls",
70
+ tokensUsed,
71
+ };
72
+ }
73
+ }
74
+ }
75
+ catch (error) {
76
+ if (error instanceof Error) {
77
+ DEBUG_LOG?.push(`Error: ${error.message}`);
78
+ }
79
+ if (options.dumpDebugLog ?? false) {
80
+ console.log(DEBUG_LOG.join("\n\n"));
81
+ DEBUG_LOG.length = 0;
82
+ }
83
+ if (error instanceof TokenLimitExceededError) {
84
+ return {
85
+ status: editCount > 0 && sequentialErrorCount < editCount ? "partial-failure" : "failure",
86
+ errorMessage: "tokenLimitExceeded",
87
+ tokensUsed,
88
+ };
89
+ }
90
+ throw error;
91
+ }
92
+ if (options.dumpDebugLog ?? false) {
93
+ console.log(DEBUG_LOG.join("\n\n"));
94
+ DEBUG_LOG.length = 0;
95
+ }
96
+ return {
97
+ status: "success",
98
+ tokensUsed,
99
+ };
100
+ }
101
+ /**
102
+ * Generates a single {@link TreeEdit} from an LLM.
103
+ *
104
+ * @remarks
105
+ * The design of this async generator function is such that which each iteration of this functions values,
106
+ * an LLM will be prompted to generate the next value (a {@link TreeEdit}) based on the users ask.
107
+ * Once the LLM believes it has completed the user's ask, it will no longer return an edit and as a result
108
+ * this generator will no longer yield a next value.
109
+ */
110
+ async function* generateEdits(options, simpleSchema, idGenerator, editLog, tokenLimits, tokensUsed) {
111
+ const [types, rootTypeName] = generateGenericEditTypes(simpleSchema, true);
112
+ let plan;
113
+ if (options.planningStep !== undefined) {
114
+ const planningPromt = getPlanningSystemPrompt(options.treeNode, options.prompt.userAsk, options.prompt.systemRoleContext);
115
+ DEBUG_LOG?.push(planningPromt);
116
+ plan = await getStringFromLlm(planningPromt, options.openAI, tokensUsed);
117
+ DEBUG_LOG?.push(`AI Generated the following plan: ${planningPromt}`);
118
+ }
119
+ const originalDecoratedJson = (options.finalReviewStep ?? false)
120
+ ? toDecoratedJson(idGenerator, options.treeNode)
121
+ : undefined;
122
+ // reviewed is implicitly true if finalReviewStep is false
123
+ let hasReviewed = (options.finalReviewStep ?? false) ? false : true;
124
+ async function getNextEdit() {
125
+ const systemPrompt = getEditingSystemPrompt(options.prompt.userAsk, idGenerator, options.treeNode, editLog, options.prompt.systemRoleContext, plan);
126
+ DEBUG_LOG?.push(systemPrompt);
127
+ const schema = types[rootTypeName] ?? fail("Root type not found.");
128
+ const wrapper = await getStructuredOutputFromLlm(systemPrompt, options.openAI, schema, "A JSON object that represents an edit to a JSON tree.", tokensUsed);
129
+ // eslint-disable-next-line unicorn/no-null
130
+ DEBUG_LOG?.push(JSON.stringify(wrapper, null, 2));
131
+ if (wrapper === undefined) {
132
+ DEBUG_LOG?.push("Failed to get response");
133
+ return undefined;
134
+ }
135
+ if (wrapper.edit === null) {
136
+ DEBUG_LOG?.push("No more edits.");
137
+ if ((options.finalReviewStep ?? false) && !hasReviewed) {
138
+ const reviewResult = await reviewGoal();
139
+ if (reviewResult === undefined) {
140
+ DEBUG_LOG?.push("Failed to get review response");
141
+ return undefined;
142
+ }
143
+ // eslint-disable-next-line require-atomic-updates
144
+ hasReviewed = true;
145
+ if (reviewResult.goalAccomplished === "yes") {
146
+ return undefined;
147
+ }
148
+ else {
149
+ // eslint-disable-next-line require-atomic-updates
150
+ editLog.length = 0;
151
+ return getNextEdit();
152
+ }
153
+ }
154
+ }
155
+ else {
156
+ return wrapper.edit;
157
+ }
158
+ }
159
+ async function reviewGoal() {
160
+ const systemPrompt = getReviewSystemPrompt(options.prompt.userAsk, idGenerator, options.treeNode, originalDecoratedJson ?? fail("Original decorated tree not provided."), options.prompt.systemRoleContext);
161
+ DEBUG_LOG?.push(systemPrompt);
162
+ const schema = z.object({
163
+ goalAccomplished: z
164
+ .enum(["yes", "no"])
165
+ .describe('Whether the user\'s goal was met in the "after" tree.'),
166
+ });
167
+ return getStructuredOutputFromLlm(systemPrompt, options.openAI, schema);
168
+ }
169
+ let edit = await getNextEdit();
170
+ while (edit !== undefined) {
171
+ yield edit;
172
+ if (tokensUsed.inputTokens > (tokenLimits?.inputTokens ?? Number.POSITIVE_INFINITY)) {
173
+ throw new TokenLimitExceededError("Input token limit exceeded.");
174
+ }
175
+ if (tokensUsed.outputTokens > (tokenLimits?.outputTokens ?? Number.POSITIVE_INFINITY)) {
176
+ throw new TokenLimitExceededError("Output token limit exceeded.");
177
+ }
178
+ edit = await getNextEdit();
179
+ }
180
+ }
181
+ /**
182
+ * Calls the LLM to generate a structured output response based on the provided prompt.
183
+ */
184
+ async function getStructuredOutputFromLlm(prompt, openAi, structuredOutputSchema, description, tokensUsed) {
185
+ const response_format = zodResponseFormat(structuredOutputSchema, "SharedTreeAI", {
186
+ description,
187
+ });
188
+ const body = {
189
+ messages: [{ role: "system", content: prompt }],
190
+ model: openAi.modelName ?? "gpt-4o",
191
+ response_format,
192
+ };
193
+ const result = await openAi.client.beta.chat.completions.parse(body);
194
+ if (result.usage !== undefined && tokensUsed !== undefined) {
195
+ tokensUsed.inputTokens += result.usage?.prompt_tokens;
196
+ tokensUsed.outputTokens += result.usage?.completion_tokens;
197
+ }
198
+ // TODO: fix types so this isn't null and doesn't need a cast
199
+ // The type should be derived from the zod schema
200
+ return result.choices[0]?.message.parsed;
201
+ }
202
+ /**
203
+ * Calls the LLM to generate a response based on the provided prompt.
204
+ */
205
+ async function getStringFromLlm(prompt, openAi, tokensUsed) {
206
+ const body = {
207
+ messages: [{ role: "system", content: prompt }],
208
+ model: openAi.modelName ?? "gpt-4o",
209
+ };
210
+ const result = await openAi.client.chat.completions.create(body);
211
+ if (result.usage !== undefined && tokensUsed !== undefined) {
212
+ tokensUsed.inputTokens += result.usage?.prompt_tokens;
213
+ tokensUsed.outputTokens += result.usage?.completion_tokens;
214
+ }
215
+ return result.choices[0]?.message.content ?? undefined;
216
+ }
217
+ class TokenLimitExceededError extends Error {
218
+ }
219
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/explicit-strategy/index.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tgetSimpleSchema,\n\tTree,\n\ttype SimpleTreeSchema,\n\ttype TreeNode,\n} from \"@fluidframework/tree/internal\";\n// eslint-disable-next-line import/no-internal-modules\nimport { zodResponseFormat } from \"openai/helpers/zod\";\nimport type {\n\tChatCompletionCreateParams,\n\t// eslint-disable-next-line import/no-internal-modules\n} from \"openai/resources/index.mjs\";\nimport { z } from \"zod\";\n\nimport type { OpenAiClientOptions, TokenLimits, TokenUsage } from \"../aiCollabApi.js\";\n\nimport { applyAgentEdit } from \"./agentEditReducer.js\";\nimport type { EditWrapper, TreeEdit } from \"./agentEditTypes.js\";\nimport { IdGenerator } from \"./idGenerator.js\";\nimport {\n\tgetEditingSystemPrompt,\n\tgetPlanningSystemPrompt,\n\tgetReviewSystemPrompt,\n\ttoDecoratedJson,\n\ttype EditLog,\n} from \"./promptGeneration.js\";\nimport { generateGenericEditTypes } from \"./typeGeneration.js\";\nimport { fail } from \"./utils.js\";\n\nconst DEBUG_LOG: string[] = [];\n\n/**\n * {@link generateTreeEdits} options.\n *\n * @internal\n */\nexport interface GenerateTreeEditsOptions {\n\topenAI: OpenAiClientOptions;\n\ttreeNode: TreeNode;\n\tprompt: {\n\t\tsystemRoleContext: string;\n\t\tuserAsk: string;\n\t};\n\tlimiters?: {\n\t\tabortController?: AbortController;\n\t\tmaxSequentialErrors?: number;\n\t\tmaxModelCalls?: number;\n\t\ttokenLimits?: TokenLimits;\n\t};\n\tfinalReviewStep?: boolean;\n\tvalidator?: (newContent: TreeNode) => void;\n\tdumpDebugLog?: boolean;\n\tplanningStep?: boolean;\n}\n\ninterface GenerateTreeEditsSuccessResponse {\n\tstatus: \"success\";\n\ttokensUsed: TokenUsage;\n}\n\ninterface GenerateTreeEditsErrorResponse {\n\tstatus: \"failure\" | \"partial-failure\";\n\terrorMessage: \"tokenLimitExceeded\" | \"tooManyErrors\" | \"tooManyModelCalls\" | \"aborted\";\n\ttokensUsed: TokenUsage;\n}\n\n/**\n * Prompts the provided LLM client to generate valid tree edits.\n * Applies those edits to the provided tree branch before returning.\n *\n * @remarks\n * - Optional root nodes are not supported\n * - Primitive root nodes are not supported\n *\n * @internal\n */\nexport async function generateTreeEdits(\n\toptions: GenerateTreeEditsOptions,\n): Promise<GenerateTreeEditsSuccessResponse | GenerateTreeEditsErrorResponse> {\n\tconst idGenerator = new IdGenerator();\n\tconst editLog: EditLog = [];\n\tlet editCount = 0;\n\tlet sequentialErrorCount = 0;\n\n\tconst simpleSchema = getSimpleSchema(Tree.schema(options.treeNode));\n\n\tconst tokensUsed = { inputTokens: 0, outputTokens: 0 };\n\n\ttry {\n\t\tfor await (const edit of generateEdits(\n\t\t\toptions,\n\t\t\tsimpleSchema,\n\t\t\tidGenerator,\n\t\t\teditLog,\n\t\t\toptions.limiters?.tokenLimits,\n\t\t\ttokensUsed,\n\t\t)) {\n\t\t\ttry {\n\t\t\t\tconst result = applyAgentEdit(\n\t\t\t\t\tedit,\n\t\t\t\t\tidGenerator,\n\t\t\t\t\tsimpleSchema.definitions,\n\t\t\t\t\toptions.validator,\n\t\t\t\t);\n\t\t\t\tconst explanation = result.explanation;\n\t\t\t\teditLog.push({ edit: { ...result, explanation } });\n\t\t\t\tsequentialErrorCount = 0;\n\t\t\t} catch (error: unknown) {\n\t\t\t\tif (error instanceof Error) {\n\t\t\t\t\tsequentialErrorCount += 1;\n\t\t\t\t\teditLog.push({ edit, error: error.message });\n\t\t\t\t\tDEBUG_LOG?.push(`Error: ${error.message}`);\n\t\t\t\t} else {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst responseStatus =\n\t\t\t\teditCount > 0 && sequentialErrorCount < editCount ? \"partial-failure\" : \"failure\";\n\n\t\t\tif (options.limiters?.abortController?.signal.aborted === true) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: responseStatus,\n\t\t\t\t\terrorMessage: \"aborted\",\n\t\t\t\t\ttokensUsed,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\tsequentialErrorCount >\n\t\t\t\t(options.limiters?.maxSequentialErrors ?? Number.POSITIVE_INFINITY)\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: responseStatus,\n\t\t\t\t\terrorMessage: \"tooManyErrors\",\n\t\t\t\t\ttokensUsed,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (++editCount >= (options.limiters?.maxModelCalls ?? Number.POSITIVE_INFINITY)) {\n\t\t\t\treturn {\n\t\t\t\t\tstatus: responseStatus,\n\t\t\t\t\terrorMessage: \"tooManyModelCalls\",\n\t\t\t\t\ttokensUsed,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t} catch (error: unknown) {\n\t\tif (error instanceof Error) {\n\t\t\tDEBUG_LOG?.push(`Error: ${error.message}`);\n\t\t}\n\n\t\tif (options.dumpDebugLog ?? false) {\n\t\t\tconsole.log(DEBUG_LOG.join(\"\\n\\n\"));\n\t\t\tDEBUG_LOG.length = 0;\n\t\t}\n\n\t\tif (error instanceof TokenLimitExceededError) {\n\t\t\treturn {\n\t\t\t\tstatus:\n\t\t\t\t\teditCount > 0 && sequentialErrorCount < editCount ? \"partial-failure\" : \"failure\",\n\t\t\t\terrorMessage: \"tokenLimitExceeded\",\n\t\t\t\ttokensUsed,\n\t\t\t};\n\t\t}\n\t\tthrow error;\n\t}\n\n\tif (options.dumpDebugLog ?? false) {\n\t\tconsole.log(DEBUG_LOG.join(\"\\n\\n\"));\n\t\tDEBUG_LOG.length = 0;\n\t}\n\n\treturn {\n\t\tstatus: \"success\",\n\t\ttokensUsed,\n\t};\n}\n\ninterface ReviewResult {\n\tgoalAccomplished: \"yes\" | \"no\";\n}\n\n/**\n * Generates a single {@link TreeEdit} from an LLM.\n *\n * @remarks\n * The design of this async generator function is such that which each iteration of this functions values,\n * an LLM will be prompted to generate the next value (a {@link TreeEdit}) based on the users ask.\n * Once the LLM believes it has completed the user's ask, it will no longer return an edit and as a result\n * this generator will no longer yield a next value.\n */\nasync function* generateEdits(\n\toptions: GenerateTreeEditsOptions,\n\tsimpleSchema: SimpleTreeSchema,\n\tidGenerator: IdGenerator,\n\teditLog: EditLog,\n\ttokenLimits: TokenLimits | undefined,\n\ttokensUsed: TokenUsage,\n): AsyncGenerator<TreeEdit> {\n\tconst [types, rootTypeName] = generateGenericEditTypes(simpleSchema, true);\n\n\tlet plan: string | undefined;\n\tif (options.planningStep !== undefined) {\n\t\tconst planningPromt = getPlanningSystemPrompt(\n\t\t\toptions.treeNode,\n\t\t\toptions.prompt.userAsk,\n\t\t\toptions.prompt.systemRoleContext,\n\t\t);\n\t\tDEBUG_LOG?.push(planningPromt);\n\t\tplan = await getStringFromLlm(planningPromt, options.openAI, tokensUsed);\n\t\tDEBUG_LOG?.push(`AI Generated the following plan: ${planningPromt}`);\n\t}\n\n\tconst originalDecoratedJson =\n\t\t(options.finalReviewStep ?? false)\n\t\t\t? toDecoratedJson(idGenerator, options.treeNode)\n\t\t\t: undefined;\n\t// reviewed is implicitly true if finalReviewStep is false\n\tlet hasReviewed = (options.finalReviewStep ?? false) ? false : true;\n\tasync function getNextEdit(): Promise<TreeEdit | undefined> {\n\t\tconst systemPrompt = getEditingSystemPrompt(\n\t\t\toptions.prompt.userAsk,\n\t\t\tidGenerator,\n\t\t\toptions.treeNode,\n\t\t\teditLog,\n\t\t\toptions.prompt.systemRoleContext,\n\t\t\tplan,\n\t\t);\n\n\t\tDEBUG_LOG?.push(systemPrompt);\n\n\t\tconst schema = types[rootTypeName] ?? fail(\"Root type not found.\");\n\t\tconst wrapper = await getStructuredOutputFromLlm<EditWrapper>(\n\t\t\tsystemPrompt,\n\t\t\toptions.openAI,\n\t\t\tschema,\n\t\t\t\"A JSON object that represents an edit to a JSON tree.\",\n\t\t\ttokensUsed,\n\t\t);\n\n\t\t// eslint-disable-next-line unicorn/no-null\n\t\tDEBUG_LOG?.push(JSON.stringify(wrapper, null, 2));\n\t\tif (wrapper === undefined) {\n\t\t\tDEBUG_LOG?.push(\"Failed to get response\");\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (wrapper.edit === null) {\n\t\t\tDEBUG_LOG?.push(\"No more edits.\");\n\t\t\tif ((options.finalReviewStep ?? false) && !hasReviewed) {\n\t\t\t\tconst reviewResult = await reviewGoal();\n\t\t\t\tif (reviewResult === undefined) {\n\t\t\t\t\tDEBUG_LOG?.push(\"Failed to get review response\");\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\t// eslint-disable-next-line require-atomic-updates\n\t\t\t\thasReviewed = true;\n\t\t\t\tif (reviewResult.goalAccomplished === \"yes\") {\n\t\t\t\t\treturn undefined;\n\t\t\t\t} else {\n\t\t\t\t\t// eslint-disable-next-line require-atomic-updates\n\t\t\t\t\teditLog.length = 0;\n\t\t\t\t\treturn getNextEdit();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\treturn wrapper.edit;\n\t\t}\n\t}\n\n\tasync function reviewGoal(): Promise<ReviewResult | undefined> {\n\t\tconst systemPrompt = getReviewSystemPrompt(\n\t\t\toptions.prompt.userAsk,\n\t\t\tidGenerator,\n\t\t\toptions.treeNode,\n\t\t\toriginalDecoratedJson ?? fail(\"Original decorated tree not provided.\"),\n\t\t\toptions.prompt.systemRoleContext,\n\t\t);\n\n\t\tDEBUG_LOG?.push(systemPrompt);\n\n\t\tconst schema = z.object({\n\t\t\tgoalAccomplished: z\n\t\t\t\t.enum([\"yes\", \"no\"])\n\t\t\t\t.describe('Whether the user\\'s goal was met in the \"after\" tree.'),\n\t\t});\n\t\treturn getStructuredOutputFromLlm<ReviewResult>(systemPrompt, options.openAI, schema);\n\t}\n\n\tlet edit = await getNextEdit();\n\twhile (edit !== undefined) {\n\t\tyield edit;\n\t\tif (tokensUsed.inputTokens > (tokenLimits?.inputTokens ?? Number.POSITIVE_INFINITY)) {\n\t\t\tthrow new TokenLimitExceededError(\"Input token limit exceeded.\");\n\t\t}\n\t\tif (tokensUsed.outputTokens > (tokenLimits?.outputTokens ?? Number.POSITIVE_INFINITY)) {\n\t\t\tthrow new TokenLimitExceededError(\"Output token limit exceeded.\");\n\t\t}\n\t\tedit = await getNextEdit();\n\t}\n}\n\n/**\n * Calls the LLM to generate a structured output response based on the provided prompt.\n */\nasync function getStructuredOutputFromLlm<T>(\n\tprompt: string,\n\topenAi: OpenAiClientOptions,\n\tstructuredOutputSchema: Zod.ZodTypeAny,\n\tdescription?: string,\n\ttokensUsed?: TokenUsage,\n): Promise<T | undefined> {\n\tconst response_format = zodResponseFormat(structuredOutputSchema, \"SharedTreeAI\", {\n\t\tdescription,\n\t});\n\n\tconst body: ChatCompletionCreateParams = {\n\t\tmessages: [{ role: \"system\", content: prompt }],\n\t\tmodel: openAi.modelName ?? \"gpt-4o\",\n\t\tresponse_format,\n\t};\n\n\tconst result = await openAi.client.beta.chat.completions.parse(body);\n\n\tif (result.usage !== undefined && tokensUsed !== undefined) {\n\t\ttokensUsed.inputTokens += result.usage?.prompt_tokens;\n\t\ttokensUsed.outputTokens += result.usage?.completion_tokens;\n\t}\n\n\t// TODO: fix types so this isn't null and doesn't need a cast\n\t// The type should be derived from the zod schema\n\treturn result.choices[0]?.message.parsed as T | undefined;\n}\n\n/**\n * Calls the LLM to generate a response based on the provided prompt.\n */\nasync function getStringFromLlm(\n\tprompt: string,\n\topenAi: OpenAiClientOptions,\n\ttokensUsed?: TokenUsage,\n): Promise<string | undefined> {\n\tconst body: ChatCompletionCreateParams = {\n\t\tmessages: [{ role: \"system\", content: prompt }],\n\t\tmodel: openAi.modelName ?? \"gpt-4o\",\n\t};\n\n\tconst result = await openAi.client.chat.completions.create(body);\n\n\tif (result.usage !== undefined && tokensUsed !== undefined) {\n\t\ttokensUsed.inputTokens += result.usage?.prompt_tokens;\n\t\ttokensUsed.outputTokens += result.usage?.completion_tokens;\n\t}\n\n\treturn result.choices[0]?.message.content ?? undefined;\n}\n\nclass TokenLimitExceededError extends Error {}\n"]}
@@ -0,0 +1,23 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ /**
6
+ * Primitive JSON Types
7
+ */
8
+ export type JsonPrimitive = string | number | boolean | null;
9
+ /**
10
+ * A JSON Object, a collection of key to {@link JsonValue} pairs
11
+ */
12
+ export interface JsonObject {
13
+ [key: string]: JsonValue;
14
+ }
15
+ /**
16
+ * An Array of {@link JsonValue}
17
+ */
18
+ export type JsonArray = JsonValue[];
19
+ /**
20
+ * A union type of all possible JSON values, including primitives, objects, and arrays
21
+ */
22
+ export type JsonValue = JsonPrimitive | JsonObject | JsonArray;
23
+ //# sourceMappingURL=jsonTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonTypes.d.ts","sourceRoot":"","sources":["../../src/explicit-strategy/jsonTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AAEH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAE7D;;GAEG;AAEH,MAAM,WAAW,UAAU;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AACD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,SAAS,EAAE,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,SAAS,CAAC"}
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=jsonTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonTypes.js","sourceRoot":"","sources":["../../src/explicit-strategy/jsonTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n/**\n * Primitive JSON Types\n */\n// eslint-disable-next-line @rushstack/no-new-null\nexport type JsonPrimitive = string | number | boolean | null;\n\n/**\n * A JSON Object, a collection of key to {@link JsonValue} pairs\n */\n// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style\nexport interface JsonObject {\n\t[key: string]: JsonValue;\n}\n/**\n * An Array of {@link JsonValue}\n */\nexport type JsonArray = JsonValue[];\n\n/**\n * A union type of all possible JSON values, including primitives, objects, and arrays\n */\nexport type JsonValue = JsonPrimitive | JsonObject | JsonArray;\n"]}
@@ -0,0 +1,51 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import { type ImplicitFieldSchema, type TreeFieldFromImplicitField, type JsonTreeSchema, type TreeNode } from "@fluidframework/tree/internal";
6
+ import { type TreeEdit } from "./agentEditTypes.js";
7
+ import type { IdGenerator } from "./idGenerator.js";
8
+ /**
9
+ * A log of edits that have been made to a tree.
10
+ * @remarks This is primarily used to help an LLM keep track of the active changes it has made.
11
+ */
12
+ export type EditLog = {
13
+ edit: TreeEdit;
14
+ error?: string;
15
+ }[];
16
+ /**
17
+ * TBD
18
+ */
19
+ export declare function toDecoratedJson(idGenerator: IdGenerator, root: TreeFieldFromImplicitField<ImplicitFieldSchema>): string;
20
+ /**
21
+ * Generates a prompt designed to make an LLM produce a plan to edit the SharedTree to accomplish a user-specified goal.
22
+ */
23
+ export declare function getPlanningSystemPrompt(treeNode: TreeNode, userPrompt: string, systemRoleContext?: string): string;
24
+ /**
25
+ * Generates a prompt that provides a history of the edits an LLM has made to a SharedTree as well as any errors that occured from attemping to apply each respsecitve edit to the tree.
26
+ */
27
+ export declare function createEditListHistoryPrompt(edits: EditLog): string;
28
+ /**
29
+ * Generates the main prompt of this explicit strategy.
30
+ * This prompt is designed to give an LLM instructions on how it can modify a SharedTree using specific types of {@link TreeEdit}'s
31
+ * and provides with both a serialized version of the current state of the provided tree node as well as the interfaces that compromise said tree nodes data.
32
+ */
33
+ export declare function getEditingSystemPrompt(userPrompt: string, idGenerator: IdGenerator, treeNode: TreeNode, log: EditLog, appGuidance?: string, plan?: string): string;
34
+ /**
35
+ * Generates a prompt designed to make an LLM review the edits it created and applied to a SharedTree based
36
+ * on a user-specified goal. This prompt is designed to give the LLM's ability to correct for mistakes and improve the accuracy/fidelity of its final set of tree edits
37
+ */
38
+ export declare function getReviewSystemPrompt(userPrompt: string, idGenerator: IdGenerator, treeNode: TreeNode, originalDecoratedJson: string, appGuidance?: string): string;
39
+ /**
40
+ * Converts a fully-qualified SharedTree schema name to a single-word name for use in textual TypeScript-style types.
41
+ *
42
+ * @remarks
43
+ * - TODO: Determine what to do with user-provided names that include periods (e.g. "Foo.Bar").
44
+ * - TODO: Should probably ensure name starts with an uppercase character.
45
+ */
46
+ export declare function getPromptFriendlyTreeSchema(jsonSchema: JsonTreeSchema): string;
47
+ /**
48
+ * TBD
49
+ */
50
+ export declare function getFriendlySchemaName(schemaName: string): string;
51
+ //# sourceMappingURL=promptGeneration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promptGeneration.d.ts","sourceRoot":"","sources":["../../src/explicit-strategy/promptGeneration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAEN,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,EAK/B,KAAK,cAAc,EAGnB,KAAK,QAAQ,EACb,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAIpD;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG;IACrB,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CACf,EAAE,CAAC;AAEJ;;GAEG;AACH,wBAAgB,eAAe,CAC9B,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,0BAA0B,CAAC,mBAAmB,CAAC,GACnD,MAAM,CAsBR;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACtC,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,MAAM,EAClB,iBAAiB,CAAC,EAAE,MAAM,GACxB,MAAM,CAsBR;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAUlE;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,OAAO,EACZ,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,MAAM,GACX,MAAM,CAqCR;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,QAAQ,EAClB,qBAAqB,EAAE,MAAM,EAC7B,WAAW,CAAC,EAAE,MAAM,GAClB,MAAM,CAwBR;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,cAAc,GAAG,MAAM,CA6B9E;AAgDD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAOhE"}