@fluidframework/tree-agent 2.73.0 → 2.74.0-368706

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 (68) hide show
  1. package/api-report/tree-agent.alpha.api.md +1 -3
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +2 -0
  4. package/dist/agent.js.map +1 -1
  5. package/dist/methodBinding.d.ts +7 -4
  6. package/dist/methodBinding.d.ts.map +1 -1
  7. package/dist/methodBinding.js +14 -3
  8. package/dist/methodBinding.js.map +1 -1
  9. package/dist/prompt.d.ts.map +1 -1
  10. package/dist/prompt.js +2 -12
  11. package/dist/prompt.js.map +1 -1
  12. package/dist/propertyBinding.d.ts.map +1 -1
  13. package/dist/propertyBinding.js +2 -2
  14. package/dist/propertyBinding.js.map +1 -1
  15. package/dist/renderSchemaTypeScript.d.ts +18 -0
  16. package/dist/renderSchemaTypeScript.d.ts.map +1 -0
  17. package/dist/renderSchemaTypeScript.js +399 -0
  18. package/dist/renderSchemaTypeScript.js.map +1 -0
  19. package/dist/renderZodTypeScript.d.ts +21 -0
  20. package/dist/renderZodTypeScript.d.ts.map +1 -0
  21. package/dist/renderZodTypeScript.js +272 -0
  22. package/dist/renderZodTypeScript.js.map +1 -0
  23. package/dist/typeGeneration.d.ts +3 -5
  24. package/dist/typeGeneration.d.ts.map +1 -1
  25. package/dist/typeGeneration.js +21 -254
  26. package/dist/typeGeneration.js.map +1 -1
  27. package/dist/utils.d.ts +10 -27
  28. package/dist/utils.d.ts.map +1 -1
  29. package/dist/utils.js +20 -364
  30. package/dist/utils.js.map +1 -1
  31. package/lib/agent.d.ts.map +1 -1
  32. package/lib/agent.js +2 -0
  33. package/lib/agent.js.map +1 -1
  34. package/lib/methodBinding.d.ts +7 -4
  35. package/lib/methodBinding.d.ts.map +1 -1
  36. package/lib/methodBinding.js +11 -1
  37. package/lib/methodBinding.js.map +1 -1
  38. package/lib/prompt.d.ts.map +1 -1
  39. package/lib/prompt.js +3 -13
  40. package/lib/prompt.js.map +1 -1
  41. package/lib/propertyBinding.d.ts.map +1 -1
  42. package/lib/propertyBinding.js +1 -1
  43. package/lib/propertyBinding.js.map +1 -1
  44. package/lib/renderSchemaTypeScript.d.ts +18 -0
  45. package/lib/renderSchemaTypeScript.d.ts.map +1 -0
  46. package/lib/renderSchemaTypeScript.js +395 -0
  47. package/lib/renderSchemaTypeScript.js.map +1 -0
  48. package/lib/renderZodTypeScript.d.ts +21 -0
  49. package/lib/renderZodTypeScript.d.ts.map +1 -0
  50. package/lib/renderZodTypeScript.js +267 -0
  51. package/lib/renderZodTypeScript.js.map +1 -0
  52. package/lib/typeGeneration.d.ts +3 -5
  53. package/lib/typeGeneration.d.ts.map +1 -1
  54. package/lib/typeGeneration.js +24 -257
  55. package/lib/typeGeneration.js.map +1 -1
  56. package/lib/utils.d.ts +10 -27
  57. package/lib/utils.d.ts.map +1 -1
  58. package/lib/utils.js +20 -362
  59. package/lib/utils.js.map +1 -1
  60. package/package.json +10 -10
  61. package/src/agent.ts +2 -0
  62. package/src/methodBinding.ts +17 -5
  63. package/src/prompt.ts +6 -22
  64. package/src/propertyBinding.ts +2 -1
  65. package/src/renderSchemaTypeScript.ts +523 -0
  66. package/src/renderZodTypeScript.ts +314 -0
  67. package/src/typeGeneration.ts +32 -414
  68. package/src/utils.ts +20 -414
@@ -3,441 +3,59 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
7
- import { UsageError } from "@fluidframework/telemetry-utils/internal";
8
- import {
9
- ArrayNodeSchema,
10
- FieldKind,
11
- getSimpleSchema,
12
- MapNodeSchema,
13
- NodeKind,
14
- ObjectNodeSchema,
15
- RecordNodeSchema,
16
- ValueSchema,
17
- walkFieldSchema,
18
- } from "@fluidframework/tree/internal";
19
- import type {
20
- ImplicitFieldSchema,
21
- SimpleFieldSchema,
22
- SimpleNodeSchema,
23
- SimpleTreeSchema,
24
- TreeNodeSchema,
25
- } from "@fluidframework/tree/internal";
26
- import { z, type ZodTypeAny } from "zod";
6
+ import { getSimpleSchema, walkFieldSchema } from "@fluidframework/tree/internal";
7
+ import type { ImplicitFieldSchema, SimpleTreeSchema } from "@fluidframework/tree/internal";
27
8
 
28
9
  import type { BindableSchema } from "./methodBinding.js";
29
- import { FunctionWrapper, getExposedMethods } from "./methodBinding.js";
10
+ import { getExposedMethods, isBindableSchema } from "./methodBinding.js";
30
11
  import { getExposedProperties } from "./propertyBinding.js";
31
12
  import {
32
- fail,
33
- getOrCreate,
34
- hasAtLeastTwo,
35
- llmDefault,
36
- mapIterable,
37
- tryGetSingleton,
38
- type MapGetSet,
39
- } from "./utils.js";
13
+ renderSchemaTypeScript,
14
+ type SchemaTypeScriptRenderResult,
15
+ } from "./renderSchemaTypeScript.js";
16
+ import { getOrCreate } from "./utils.js";
40
17
 
41
- /**
42
- *
43
- * TODO: Add a prompt suggestion API!
44
- *
45
- * TODO: Handle rate limit errors.
46
- *
47
- * TODO: Pass descriptions from schema metadata to the generated TS types that we put in the prompt
48
- *
49
- * TODO make the Ids be "Vector-2" instead of "Vector2" (or else it gets weird when you have a type called "Vector2")
50
- */
18
+ const promptSchemaCache = new WeakMap<SimpleTreeSchema, SchemaTypeScriptRenderResult>();
51
19
 
52
20
  /**
53
- * Cache used to prevent repeatedly generating the same Zod validation objects for the same {@link SimpleTreeSchema} as generate propts for repeated calls to an LLM
54
- */
55
- const promptSchemaCache = new WeakMap<
56
- SimpleTreeSchema,
57
- ReturnType<typeof generateEditTypes>
58
- >();
59
-
60
- /**
61
- * TODO
21
+ * Generates TypeScript declarations for the schemas reachable from the provided root field schema.
62
22
  */
63
23
  export function generateEditTypesForPrompt(
64
24
  rootSchema: ImplicitFieldSchema,
65
25
  schema: SimpleTreeSchema,
66
- ): {
67
- domainTypes: Record<string, ZodTypeAny>;
68
- } {
69
- return getOrCreate(promptSchemaCache, schema, () => {
70
- const allSchemas = new Set<TreeNodeSchema>();
71
- const objectTypeSchemas = new Set<TreeNodeSchema>();
72
- walkFieldSchema(rootSchema, {
73
- node: (n) => {
74
- allSchemas.add(n);
75
- if (
76
- n instanceof ObjectNodeSchema ||
77
- n instanceof MapNodeSchema ||
78
- n instanceof ArrayNodeSchema ||
79
- n instanceof RecordNodeSchema
80
- ) {
81
- objectTypeSchemas.add(n);
82
- const exposedMethods = getExposedMethods(n);
83
- for (const t of exposedMethods.referencedTypes) {
84
- allSchemas.add(t);
85
- objectTypeSchemas.add(t);
86
- }
87
- const exposedProperties = getExposedProperties(n);
88
- for (const t of exposedProperties.referencedTypes) {
89
- allSchemas.add(t);
90
- objectTypeSchemas.add(t);
91
- }
92
- }
93
- },
94
- });
95
- const nodeSchemas = [...objectTypeSchemas.values()] as BindableSchema[];
96
- const bindableSchemas = new Map<string, BindableSchema>(
97
- nodeSchemas.map((nodeSchema) => [nodeSchema.identifier, nodeSchema]),
98
- );
99
- return generateEditTypes(
100
- [...allSchemas.values()].map((s) => getSimpleSchema(s)),
101
- new Map(),
102
- bindableSchemas,
103
- );
104
- });
105
- }
106
-
107
- /**
108
- * Generates a set of ZOD validation objects for the various types of data that can be put into the provided {@link SimpleTreeSchema}
109
- * and then uses those sets to generate an all-encompassing ZOD object for each type of {@link TreeEdit} that can validate any of the types of data that can be put into the tree.
110
- *
111
- * @returns a Record of schema names to Zod validation objects, and the name of the root schema used to encompass all of the other schemas.
112
- *
113
- * @remarks The return type of this function is designed to work with Typechat's createZodJsonValidator as well as be used as the JSON schema for OpenAi's structured output response format.
114
- */
115
- function generateEditTypes(
116
- schemas: Iterable<SimpleTreeSchema>,
117
- objectCache: MapGetSet<SimpleNodeSchema, ZodTypeAny>,
118
- bindableSchemas: Map<string, BindableSchema>,
119
- ): {
120
- domainTypes: Record<string, ZodTypeAny>;
121
- } {
122
- const domainTypes: Record<string, ZodTypeAny> = {};
123
- for (const schema of schemas) {
124
- for (const name of schema.definitions.keys()) {
125
- // If this does overwrite anything in domainTypes, it is guaranteed to be overwritten with an identical value due to the getOrCreate
126
- domainTypes[name] = getOrCreateType(
127
- schema.definitions,
128
- name,
129
- objectCache,
130
- bindableSchemas,
131
- );
132
- }
133
- }
134
-
135
- return {
136
- domainTypes,
137
- };
138
- }
139
-
140
- function getBoundMembersForBindable(bindableSchema: BindableSchema): {
141
- referencedTypes: Set<TreeNodeSchema>;
142
- members: [string, ZodTypeAny][];
143
- } {
144
- const memberTypes: [string, ZodTypeAny][] = [];
145
-
146
- const methods = getExposedMethods(bindableSchema);
147
- for (const [name, method] of Object.entries(methods.methods)) {
148
- const zodFunction = z.instanceof(FunctionWrapper);
149
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
150
- (zodFunction as any).method = method;
151
- memberTypes.push([name, zodFunction]);
152
- }
153
- const props = getExposedProperties(bindableSchema);
154
- for (const [name, prop] of Object.entries(props.properties)) {
155
- const schema = prop.schema;
156
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
157
- (schema as any).property = prop;
158
- memberTypes.push([name, prop.schema]);
159
- }
160
-
161
- return {
162
- members: memberTypes,
163
- referencedTypes: new Set([...props.referencedTypes, ...methods.referencedTypes]),
164
- };
165
- }
166
-
167
- function getBoundMembers(
168
- definition: string,
169
- bindableSchemas: Map<string, BindableSchema>,
170
- ): [string, ZodTypeAny][] {
171
- const bindableSchema = bindableSchemas.get(definition) ?? fail("unknown definition");
172
- return getBoundMembersForBindable(bindableSchema).members;
26
+ ): SchemaTypeScriptRenderResult {
27
+ return getOrCreate(promptSchemaCache, schema, () =>
28
+ buildPromptSchemaDescription(rootSchema),
29
+ );
173
30
  }
174
31
 
175
- function addBindingIntersectionIfNeeded(
176
- typeString: "array" | "map" | "record",
177
- zodTypeBound: ZodTypeAny,
178
- definition: string,
179
- simpleNodeSchema: SimpleNodeSchema,
180
- bindableSchemas: Map<string, BindableSchema>,
181
- ): ZodTypeAny {
182
- let zodType = zodTypeBound;
183
- let description = simpleNodeSchema.metadata?.description ?? "";
184
-
185
- const boundMembers = getBoundMembers(definition, bindableSchemas);
186
- const methods: Record<string, z.ZodTypeAny> = {};
187
- const properties: Record<string, z.ZodTypeAny> = {};
188
-
189
- let hasProperties: boolean = false;
190
- let hasMethods: boolean = false;
191
-
192
- if (boundMembers.length > 0) {
193
- for (const [name, zodMember] of boundMembers) {
194
- const kind =
195
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
196
- (zodMember as any).method === undefined
197
- ? // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
198
- (zodMember as any).property === undefined
199
- ? "Unknown"
200
- : "Property"
201
- : "Method";
202
-
203
- if (kind === "Unknown") {
204
- throw new UsageError(`Unrecognized bound member ${name} in schema ${definition}`);
205
- }
206
- if (kind === "Method") {
207
- if (methods[name] !== undefined) {
208
- throw new UsageError(
209
- `${kind} ${name} conflicts with field of the same name in schema ${definition}`,
210
- );
211
- }
212
- methods[name] = zodMember;
213
- }
214
- if (kind === "Property") {
215
- if (properties[name] !== undefined) {
216
- throw new UsageError(
217
- `${kind} ${name} conflicts with field of the same name in schema ${definition}`,
218
- );
219
- }
220
- properties[name] = zodMember;
221
- }
222
- }
223
- hasMethods = Object.keys(methods).length > 0 ? true : false;
224
- hasProperties = Object.keys(properties).length > 0 ? true : false;
225
-
226
- if (hasMethods) {
227
- zodType = z.intersection(zodType, z.object(methods));
228
- }
229
- if (hasProperties) {
230
- zodType = z.intersection(zodType, z.object(properties));
231
- }
232
- }
233
-
234
- let note = "";
235
- if (hasMethods && hasProperties) {
236
- note = `Note: this ${typeString} has custom user-defined methods and properties directly on it.`;
237
- } else if (hasMethods) {
238
- note = `Note: this ${typeString} has custom user-defined methods directly on it.`;
239
- } else if (hasProperties) {
240
- note = `Note: this ${typeString} has custom user-defined properties directly on it.`;
241
- }
242
- if (note !== "") {
243
- description = description === "" ? note : `${description} - ${note}`;
244
- }
245
-
246
- return zodType.describe(description);
247
- }
32
+ function buildPromptSchemaDescription(
33
+ rootSchema: ImplicitFieldSchema,
34
+ ): SchemaTypeScriptRenderResult {
35
+ const bindableSchemas = new Map<string, BindableSchema>();
248
36
 
249
- function getOrCreateType(
250
- definitionMap: ReadonlyMap<string, SimpleNodeSchema>,
251
- definition: string,
252
- objectCache: MapGetSet<SimpleNodeSchema, ZodTypeAny>,
253
- bindableSchemas: Map<string, BindableSchema>,
254
- ): ZodTypeAny {
255
- const simpleNodeSchema = definitionMap.get(definition) ?? fail("Unexpected definition");
256
- return getOrCreate(objectCache, simpleNodeSchema, () => {
257
- // Handle recursive types: temporarily create a zod "lazy" type that can be referenced by a recursive call to getOrCreateType.
258
- let type: ZodTypeAny | undefined;
259
- objectCache.set(
260
- simpleNodeSchema,
261
- z.lazy(() => type ?? fail("Recursive type used before creation")),
262
- );
263
- switch (simpleNodeSchema.kind) {
264
- case NodeKind.Object: {
265
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
266
- const properties: Record<string, z.ZodTypeAny> = Object.fromEntries(
267
- [...simpleNodeSchema.fields]
268
- .map(([key, field]) => {
269
- return [
270
- key,
271
- getOrCreateTypeForField(definitionMap, field, objectCache, bindableSchemas),
272
- ];
273
- })
274
- .filter(([, value]) => value !== undefined),
275
- );
37
+ walkFieldSchema(rootSchema, {
38
+ node: (node) => {
39
+ if (isBindableSchema(node)) {
40
+ bindableSchemas.set(node.identifier, node);
276
41
 
277
- for (const [name, zodMember] of getBoundMembers(definition, bindableSchemas)) {
278
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
279
- const kind = (zodMember as any).method === undefined ? "Property" : "Method";
280
- if (properties[name] !== undefined) {
281
- throw new UsageError(
282
- `${kind} ${name} conflicts with field of the same name in schema ${definition}`,
283
- );
42
+ const exposedMethods = getExposedMethods(node);
43
+ for (const referenced of exposedMethods.referencedTypes) {
44
+ if (isBindableSchema(referenced)) {
45
+ bindableSchemas.set(referenced.identifier, referenced);
284
46
  }
285
- properties[name] = zodMember;
286
47
  }
287
48
 
288
- return (type = z
289
- .object(properties)
290
- .describe(simpleNodeSchema.metadata?.description ?? ""));
291
- }
292
- case NodeKind.Map: {
293
- const zodType = z.map(
294
- z.string(),
295
- getTypeForAllowedTypes(
296
- definitionMap,
297
- new Set(simpleNodeSchema.simpleAllowedTypes.keys()),
298
- objectCache,
299
- bindableSchemas,
300
- ),
301
- );
302
- return (type = addBindingIntersectionIfNeeded(
303
- "map",
304
- zodType,
305
- definition,
306
- simpleNodeSchema,
307
- bindableSchemas,
308
- ));
309
- }
310
- case NodeKind.Record: {
311
- const zodType = z.record(
312
- getTypeForAllowedTypes(
313
- definitionMap,
314
- new Set(simpleNodeSchema.simpleAllowedTypes.keys()),
315
- objectCache,
316
- bindableSchemas,
317
- ),
318
- );
319
- return (type = addBindingIntersectionIfNeeded(
320
- "record",
321
- zodType,
322
- definition,
323
- simpleNodeSchema,
324
- bindableSchemas,
325
- ));
326
- }
327
- case NodeKind.Array: {
328
- const zodType = z.array(
329
- getTypeForAllowedTypes(
330
- definitionMap,
331
- new Set(simpleNodeSchema.simpleAllowedTypes.keys()),
332
- objectCache,
333
- bindableSchemas,
334
- ),
335
- );
336
- return (type = addBindingIntersectionIfNeeded(
337
- "array",
338
- zodType,
339
- definition,
340
- simpleNodeSchema,
341
- bindableSchemas,
342
- ));
343
- }
344
- case NodeKind.Leaf: {
345
- switch (simpleNodeSchema.leafKind) {
346
- case ValueSchema.Boolean: {
347
- return (type = z.boolean());
348
- }
349
- case ValueSchema.Number: {
350
- return (type = z.number());
351
- }
352
- case ValueSchema.String: {
353
- return (type = z.string());
354
- }
355
- case ValueSchema.Null: {
356
- return (type = z.null());
357
- }
358
- default: {
359
- throw new Error(`Unsupported leaf kind ${NodeKind[simpleNodeSchema.leafKind]}.`);
49
+ const exposedProperties = getExposedProperties(node);
50
+ for (const referenced of exposedProperties.referencedTypes) {
51
+ if (isBindableSchema(referenced)) {
52
+ bindableSchemas.set(referenced.identifier, referenced);
360
53
  }
361
54
  }
362
55
  }
363
- default: {
364
- return unreachableCase(simpleNodeSchema, "Unknown node kind");
365
- }
366
- }
56
+ },
367
57
  });
368
- }
369
-
370
- function getOrCreateTypeForField(
371
- definitionMap: ReadonlyMap<string, SimpleNodeSchema>,
372
- fieldSchema: SimpleFieldSchema,
373
- objectCache: MapGetSet<SimpleNodeSchema, ZodTypeAny>,
374
- bindableSchemas: Map<string, BindableSchema>,
375
- ): ZodTypeAny {
376
- const customMetadata = fieldSchema.metadata.custom as
377
- | Record<string | symbol, unknown>
378
- | undefined;
379
- const getDefault = customMetadata?.[llmDefault];
380
- if (getDefault !== undefined) {
381
- if (typeof getDefault !== "function") {
382
- throw new UsageError(
383
- `Expected value of ${llmDefault.description} property to be a function, but got ${typeof getDefault}`,
384
- );
385
- }
386
-
387
- if (fieldSchema.kind !== FieldKind.Optional) {
388
- throw new UsageError(
389
- `The ${llmDefault.description} property is only permitted on optional fields.`,
390
- );
391
- }
392
- }
393
-
394
- const field = getTypeForAllowedTypes(
395
- definitionMap,
396
- new Set(fieldSchema.simpleAllowedTypes.keys()),
397
- objectCache,
398
- bindableSchemas,
399
- ).describe(
400
- getDefault === undefined
401
- ? (fieldSchema.metadata?.description ?? "")
402
- : "Do not populate this field. It will be automatically supplied by the system after insertion.",
403
- );
404
-
405
- switch (fieldSchema.kind) {
406
- case FieldKind.Required: {
407
- return field;
408
- }
409
- case FieldKind.Optional: {
410
- return field.optional();
411
- }
412
- case FieldKind.Identifier: {
413
- return field
414
- .optional()
415
- .describe(
416
- "This is an ID automatically generated by the system. Do not supply it when constructing a new object.",
417
- );
418
- }
419
- default: {
420
- throw new Error(`Unsupported field kind ${NodeKind[fieldSchema.kind]}.`);
421
- }
422
- }
423
- }
424
58
 
425
- function getTypeForAllowedTypes(
426
- definitionMap: ReadonlyMap<string, SimpleNodeSchema>,
427
- allowedTypes: ReadonlySet<string>,
428
- objectCache: MapGetSet<SimpleNodeSchema, ZodTypeAny>,
429
- bindableSchemas: Map<string, BindableSchema>,
430
- ): ZodTypeAny {
431
- const single = tryGetSingleton(allowedTypes);
432
- if (single === undefined) {
433
- const types = [
434
- ...mapIterable(allowedTypes, (name) => {
435
- return getOrCreateType(definitionMap, name, objectCache, bindableSchemas);
436
- }),
437
- ];
438
- assert(hasAtLeastTwo(types), 0xa7e /* Expected at least two types */);
439
- return z.union(types);
440
- } else {
441
- return getOrCreateType(definitionMap, single, objectCache, bindableSchemas);
442
- }
59
+ const simpleTreeSchema = getSimpleSchema(rootSchema);
60
+ return renderSchemaTypeScript(simpleTreeSchema.definitions, bindableSchemas);
443
61
  }