66import { UsageError } from "@fluidframework/telemetry-utils/internal" ;
77import type {
88 ImplicitFieldSchema ,
9- RestrictiveStringRecord ,
109 TreeFieldFromImplicitField ,
11- TreeObjectNode ,
10+ TreeNodeSchema ,
1211} from "@fluidframework/tree" ;
1312import { TreeNode , NodeKind , Tree } from "@fluidframework/tree" ;
14- import { getSimpleSchema } from "@fluidframework/tree/alpha" ;
1513import type {
16- ObjectNodeSchema ,
1714 ReadableField ,
1815 TreeBranch ,
1916 FactoryContentObject ,
2017 InsertableContent ,
2118 UnsafeUnknownSchema ,
2219 ReadSchema ,
2320} from "@fluidframework/tree/alpha" ;
21+ import { getSimpleSchema , ObjectNodeSchema } from "@fluidframework/tree/alpha" ;
2422import { normalizeFieldSchema , type TreeMapNode } from "@fluidframework/tree/internal" ;
2523import type { BaseChatModel } from "@langchain/core/language_models/chat_models" ; // eslint-disable-line import/no-internal-modules
2624import { HumanMessage , SystemMessage } from "@langchain/core/messages" ; // eslint-disable-line import/no-internal-modules
@@ -34,12 +32,14 @@ import {
3432 constructNode ,
3533 fail ,
3634 failUsage ,
37- getFriendlySchema ,
38- getFriendlySchemaName ,
35+ getFriendlyName ,
36+ unqualifySchema ,
3937 getZodSchemaAsTypeScript ,
4038 llmDefault ,
4139 type SchemaDetails ,
4240 type TreeView ,
41+ findNamedSchemas ,
42+ isNamedSchema ,
4343} from "./utils.js" ;
4444
4545const functionName = "editTree" ;
@@ -173,13 +173,10 @@ export class FunctioningSemanticAgent<TRoot extends ImplicitFieldSchema>
173173 ) ;
174174 const tree = this . queryTree ;
175175 const create : Record < string , ( input : FactoryContentObject ) => TreeNode > = { } ;
176- visitObjectNodeSchema ( tree . schema , ( schema ) => {
177- const name =
178- getFriendlySchemaName ( schema . identifier ) ??
179- fail ( "Expected friendly name for object node schema" ) ;
180-
181- create [ name ] = ( input : FactoryContentObject ) => constructObjectNode ( schema , input ) ;
182- } ) ;
176+ for ( const schema of findNamedSchemas ( tree . schema ) ) {
177+ const name = getFriendlyName ( schema ) ;
178+ create [ name ] = ( input : FactoryContentObject ) => constructTreeNode ( schema , input ) ;
179+ }
183180 if ( this . options ?. validator ?.( functionCode ) === false ) {
184181 this . options ?. log ?.( `#### Code Validation Failed\n\n` ) ;
185182 return "Code validation failed" ;
@@ -347,7 +344,7 @@ export class FunctioningSemanticAgent<TRoot extends ImplicitFieldSchema>
347344 break ;
348345 }
349346 case NodeKind . Object : {
350- exampleObjectName ??= getFriendlySchemaName ( definition ) ;
347+ exampleObjectName ??= unqualifySchema ( definition ) ;
351348 break ;
352349 }
353350 // No default
@@ -356,15 +353,10 @@ export class FunctioningSemanticAgent<TRoot extends ImplicitFieldSchema>
356353
357354 const { domainTypes } = generateEditTypesForPrompt ( schema , simpleSchema ) ;
358355 for ( const [ key , value ] of Object . entries ( domainTypes ) ) {
359- const friendlyKey = getFriendlySchemaName ( key ) ;
360356 // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
361357 delete domainTypes [ key ] ;
362- if (
363- friendlyKey !== undefined &&
364- friendlyKey !== "string" &&
365- friendlyKey !== "number" &&
366- friendlyKey !== "boolean"
367- ) {
358+ if ( isNamedSchema ( key ) ) {
359+ const friendlyKey = unqualifySchema ( key ) ;
368360 domainTypes [ friendlyKey ] = value ;
369361 }
370362 }
@@ -429,7 +421,7 @@ ${getTreeMapNodeDocumentation(mapInterfaceName)}
429421
430422` ;
431423
432- const rootTypes = [ ... simpleSchema . root . allowedTypesIdentifiers ] ;
424+ const rootTypes = normalizeFieldSchema ( schema ) . allowedTypeSet ;
433425 const prompt = `You are a helpful assistant collaborating with the user on a document. The document state is a JSON tree, and you are able to analyze and edit it.
434426The JSON tree adheres to the following Typescript schema:
435427
@@ -451,7 +443,7 @@ It may be synchronous or asynchronous.
451443The ${ functionName } function must have a first parameter which has a \`root\` property.
452444This \`root\` property holds the current state of the tree as shown above.
453445You may mutate any part of the tree as necessary, taking into account the caveats around arrays and maps detailed below.
454- 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 (\`${ rootTypes . map ( ( t ) => getFriendlySchemaName ( t ) ) . join ( " | " ) } \`).
446+ 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 ( " | " ) } \`).
455447${ helperMethodExplanation }
456448
457449${ hasArrays ? arrayEditing : "" } ${ hasMaps ? mapEditing : "" } ### Additional Notes
@@ -465,7 +457,7 @@ ${builderExplanation}Finally, double check that the edits would accomplish the u
465457### Application data
466458
467459${ domainHints }
468- The current state of the application tree (a \`${ getFriendlySchema ( field ) } \`) is:
460+ The current state of the application tree (a \`${ field === undefined ? "undefined" : getFriendlyName ( Tree . schema ( field ) ) } \`) is:
469461
470462\`\`\`JSON
471463${ stringified }
@@ -731,19 +723,6 @@ function uncapitalize(str: string): string {
731723 return str . charAt ( 0 ) . toLowerCase ( ) + str . slice ( 1 ) ;
732724}
733725
734- function visitObjectNodeSchema (
735- schema : ImplicitFieldSchema ,
736- visitor : ( schema : ObjectNodeSchema ) => void ,
737- ) : void {
738- const normalizedSchema = normalizeFieldSchema ( schema ) ;
739- for ( const nodeSchema of normalizedSchema . allowedTypeSet ) {
740- if ( nodeSchema . kind === NodeKind . Object ) {
741- visitor ( nodeSchema as ObjectNodeSchema ) ;
742- }
743- visitObjectNodeSchema ( [ ...nodeSchema . childTypes ] , visitor ) ;
744- }
745- }
746-
747726function processLlmCode ( code : string ) : string {
748727 // TODO: use a library like Acorn to analyze the code more robustly
749728 const regex = new RegExp ( `function\\s+${ functionName } \\s*\\(` ) ;
@@ -755,33 +734,32 @@ function processLlmCode(code: string): string {
755734}
756735
757736/**
758- * Creates an unhydrated object node and populates it with `llmDefault` values if they exist.
737+ * Creates an unhydrated node of the given schema with the given value.
738+ * @remarks If the schema is an object with {@link llmDefault | default values}, this function populates the node with those defaults.
759739 */
760- function constructObjectNode (
761- schema : ObjectNodeSchema ,
762- input : FactoryContentObject ,
763- ) : TreeObjectNode < RestrictiveStringRecord < ImplicitFieldSchema > > {
764- const inputWithDefaults : Record < string , InsertableContent | undefined > = { } ;
765- for ( const [ key , field ] of schema . fields ) {
766- if ( input [ key ] === undefined ) {
767- if (
768- typeof field . metadata . custom === "object" &&
769- field . metadata . custom !== null &&
770- llmDefault in field . metadata . custom
771- ) {
772- const defaulter = field . metadata . custom [ llmDefault ] ;
773- if ( typeof defaulter === "function" ) {
774- const defaultValue : unknown = defaulter ( ) ;
775- if ( defaultValue !== undefined ) {
776- inputWithDefaults [ key ] = defaultValue ;
740+ function constructTreeNode ( schema : TreeNodeSchema , value : FactoryContentObject ) : TreeNode {
741+ if ( schema instanceof ObjectNodeSchema ) {
742+ const inputWithDefaults : Record < string , InsertableContent | undefined > = { } ;
743+ for ( const [ key , field ] of schema . fields ) {
744+ if ( value [ key ] === undefined ) {
745+ if (
746+ typeof field . metadata . custom === "object" &&
747+ field . metadata . custom !== null &&
748+ llmDefault in field . metadata . custom
749+ ) {
750+ const defaulter = field . metadata . custom [ llmDefault ] ;
751+ if ( typeof defaulter === "function" ) {
752+ const defaultValue : unknown = defaulter ( ) ;
753+ if ( defaultValue !== undefined ) {
754+ inputWithDefaults [ key ] = defaultValue ;
755+ }
777756 }
778757 }
758+ } else {
759+ inputWithDefaults [ key ] = value [ key ] ;
779760 }
780- } else {
781- inputWithDefaults [ key ] = input [ key ] ;
782761 }
762+ return constructNode ( schema , inputWithDefaults ) ;
783763 }
784- return constructNode ( schema , inputWithDefaults ) as TreeObjectNode <
785- RestrictiveStringRecord < ImplicitFieldSchema >
786- > ;
764+ return constructNode ( schema , value ) ;
787765}
0 commit comments