@takeshape/schema 11.40.3 → 11.43.0

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.
@@ -3,8 +3,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getInspectAgentSessionQueryName = exports.getAgentEndStates = exports.getAgentEndTransitions = exports.BUILT_IN_CHAT_ARG_NAMES = exports.BUILT_IN_CHAT_ARGS = exports.END_AGENT_EXECUTION = void 0;
6
+ exports.addAiQueries = exports.createArgs = exports.removeBuiltInArgs = exports.getInspectAgentSessionQueryName = exports.getAgentEndStates = exports.getAgentEndTransitions = exports.BUILT_IN_CHAT_ARG_NAMES = exports.BUILT_IN_CHAT_ARGS = exports.END_AGENT_EXECUTION = void 0;
7
7
  const upperFirst_js_1 = __importDefault(require("lodash/upperFirst.js"));
8
+ const uniq_js_1 = __importDefault(require("lodash/uniq.js"));
9
+ const uniqBy_js_1 = __importDefault(require("lodash/uniqBy.js"));
8
10
  exports.END_AGENT_EXECUTION = 'endAgentExecution';
9
11
  exports.BUILT_IN_CHAT_ARGS = [
10
12
  {
@@ -56,3 +58,87 @@ const getInspectAgentSessionQueryName = (agentName) => {
56
58
  return `inspect${(0, upperFirst_js_1.default)(agentName)}`;
57
59
  };
58
60
  exports.getInspectAgentSessionQueryName = getInspectAgentSessionQueryName;
61
+ const removeBuiltInArgs = (args) => {
62
+ return args.filter(arg => !exports.BUILT_IN_CHAT_ARG_NAMES.includes(arg.argName));
63
+ };
64
+ exports.removeBuiltInArgs = removeBuiltInArgs;
65
+ const createArgs = (agent) => {
66
+ let apiArguments = (0, uniqBy_js_1.default)(agent.api.arguments ?? [], arg => arg.argName);
67
+ if (agent.api.type === 'chat') {
68
+ apiArguments = (0, exports.removeBuiltInArgs)(apiArguments).concat(exports.BUILT_IN_CHAT_ARGS);
69
+ }
70
+ return apiArguments.length > 0
71
+ ? {
72
+ type: 'object',
73
+ properties: apiArguments.reduce((acc, argument) => {
74
+ acc[argument.argName] = {
75
+ type: argument.argType === 'sessionId' ? 'string' : argument.argType
76
+ };
77
+ return acc;
78
+ }, {}),
79
+ required: apiArguments.filter(arg => arg.required).map(arg => arg.argName)
80
+ }
81
+ : undefined;
82
+ };
83
+ exports.createArgs = createArgs;
84
+ function addAiQueries(projectSchema) {
85
+ const agents = projectSchema['ai-experimental']?.agents;
86
+ if (!agents) {
87
+ return projectSchema;
88
+ }
89
+ const newSchema = {
90
+ ...projectSchema,
91
+ queries: { ...projectSchema.queries },
92
+ mutations: { ...projectSchema.mutations }
93
+ };
94
+ for (const [agentName, agent] of Object.entries(agents)) {
95
+ // Get valid return types based on states that could possibly be the end state
96
+ // TODO In the future the agent will have a return type defined and we will instead validate
97
+ // that all the return states will return the correct shape.
98
+ const returnStates = [...(0, exports.getAgentEndStates)(agent, true)];
99
+ const returnTypes = (0, uniq_js_1.default)(returnStates.map(stateId => {
100
+ const { execution } = agent.states[stateId];
101
+ if (execution.type === 'chat') {
102
+ return 'TSChatResponse';
103
+ }
104
+ if (execution.type === 'generate') {
105
+ return execution.outputShape ? execution.outputShape : 'string';
106
+ }
107
+ return 'JSON';
108
+ }));
109
+ const shape = returnTypes.length === 0 ? 'string' : returnTypes.length === 1 ? returnTypes[0] : 'JSON';
110
+ if (newSchema.mutations[agentName] !== undefined) {
111
+ throw new Error(`Schema already has a mutation with the name ${agentName}`);
112
+ }
113
+ newSchema.mutations[agentName] = {
114
+ description: agent.description,
115
+ args: (0, exports.createArgs)(agent),
116
+ shape,
117
+ resolver: {
118
+ name: 'ai:runAgent',
119
+ agentName
120
+ }
121
+ };
122
+ if (shape === 'TSChatResponse') {
123
+ newSchema.queries[(0, exports.getInspectAgentSessionQueryName)(agentName)] ||= {
124
+ description: `Inspect a session for the ${agentName} agent`,
125
+ args: {
126
+ type: 'object',
127
+ properties: {
128
+ sessionId: {
129
+ type: 'string'
130
+ }
131
+ },
132
+ required: ['sessionId']
133
+ },
134
+ shape: 'TSAgentSession',
135
+ resolver: {
136
+ name: 'ai:inspectAgentSession',
137
+ agentName
138
+ }
139
+ };
140
+ }
141
+ }
142
+ return newSchema;
143
+ }
144
+ exports.addAiQueries = addAiQueries;
@@ -58,3 +58,5 @@ __exportStar(require("./util/merge"), exports);
58
58
  __exportStar(require("./util/patch-schema"), exports);
59
59
  __exportStar(require("./util/shapes"), exports);
60
60
  __exportStar(require("./constants"), exports);
61
+ __exportStar(require("./runtime-schema"), exports);
62
+ __exportStar(require("./service-dependencies"), exports);
@@ -3,9 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.hasUnequalRelationships = exports.isEqualRelationship = exports.findExistingRelationships = exports.getRelationship = exports.getLegacyRelationship = exports.getRelationshipShapeIds = exports.getRelationshipShapes = exports.getRelationshipShapeRefs = exports.getRelationshipSchema = void 0;
6
+ exports.addRelatedFields = exports.hasUnequalRelationships = exports.isEqualRelationship = exports.findExistingRelationships = exports.getRelationship = exports.getLegacyRelationship = exports.getRelationshipShapeIds = exports.getRelationshipShapes = exports.getRelationshipShapeRefs = exports.getRelationshipSchema = void 0;
7
7
  const util_1 = require("@takeshape/util");
8
8
  const find_js_1 = __importDefault(require("lodash/find.js"));
9
+ const camelCase_js_1 = __importDefault(require("lodash/camelCase.js"));
10
+ const uniq_js_1 = __importDefault(require("lodash/uniq.js"));
9
11
  const types_1 = require("./types");
10
12
  const refs_1 = require("./refs");
11
13
  const unions_1 = require("./unions");
@@ -235,3 +237,86 @@ function hasUnequalRelationships(relationships) {
235
237
  }));
236
238
  }
237
239
  exports.hasUnequalRelationships = hasUnequalRelationships;
240
+ function getRelatedShapeIds(relationships) {
241
+ return (0, uniq_js_1.default)(relationships.map(rel => (rel.hasBackreference ? rel.shapeId : undefined)).filter(util_1.isDefined));
242
+ }
243
+ function getShapes(projectSchema, shapeIds) {
244
+ return shapeIds.map(shapeId => (0, shapes_1.getShapeById)(projectSchema, shapeId)).filter(util_1.isDefined);
245
+ }
246
+ /**
247
+ * Adds backreference fields to the schema.
248
+ */
249
+ function addRelatedFields(projectSchema, allRelationships) {
250
+ for (const [shapeId, shapeRelationships] of Object.entries(allRelationships)) {
251
+ const shape = (0, shapes_1.getShapeById)(projectSchema, shapeId);
252
+ if (shape && (0, types_1.isObjectSchema)(shape.schema)) {
253
+ const relatedShapeIds = getRelatedShapeIds(shapeRelationships);
254
+ const relatedShapeNames = getShapes(projectSchema, relatedShapeIds).map(shape => shape.name);
255
+ if (relatedShapeNames.length) {
256
+ let shapeName;
257
+ if (relatedShapeNames.length === 1) {
258
+ // If only one back reference exists, _references is a regular shape
259
+ shapeName = relatedShapeNames[0];
260
+ }
261
+ else {
262
+ // If many back references exist, _references will be a union of referring shapes
263
+ shapeName = `${shape.name}Reference`;
264
+ projectSchema.shapes[shapeName] = {
265
+ id: shapeName,
266
+ name: shapeName,
267
+ title: shapeName,
268
+ schema: {
269
+ oneOf: relatedShapeNames.map(name => ({ '@ref': `local:${name}` }))
270
+ }
271
+ };
272
+ }
273
+ // _references, a list of all backreferences across all fields on this shape.
274
+ // It lists all the items that have references to the current item.
275
+ // It is for convenience.
276
+ if (shapeRelationships.some(rel => rel.hasBackreference)) {
277
+ shape.schema.properties._references = {
278
+ '@args': `TSListArgs<local:${shapeName}>`,
279
+ '@ref': `PaginatedList<local:${shapeName}>`,
280
+ '@resolver': {
281
+ name: 'shapedb:list',
282
+ service: 'shapedb',
283
+ args: {
284
+ ops: [
285
+ { path: '$', mapping: '$args' },
286
+ { path: `baseWhere._references.eq`, mapping: '$source._id' },
287
+ { path: `baseWhere._shapeId.in`, value: relatedShapeIds }
288
+ ]
289
+ }
290
+ }
291
+ };
292
+ }
293
+ }
294
+ // Create a schema for the backreference on the referred-to shape
295
+ for (const relationship of shapeRelationships) {
296
+ const relatedShape = (0, shapes_1.getShapeById)(projectSchema, relationship.shapeId);
297
+ if (relatedShape && relationship.hasBackreference) {
298
+ const { relatedName } = relationship;
299
+ const relatedFieldName = relatedName ? relatedName : `${(0, camelCase_js_1.default)(relatedShape.name)}Set`;
300
+ const filterField = relatedName ? relationship.path.concat('_id').join('.') : '_references';
301
+ shape.schema.properties[relatedFieldName] = {
302
+ '@args': `TSListArgs<local:${relatedShape.name}>`,
303
+ '@ref': `PaginatedList<local:${relatedShape.name}>`,
304
+ '@resolver': {
305
+ name: 'shapedb:list',
306
+ service: 'shapedb',
307
+ args: {
308
+ ops: [
309
+ { path: '$', mapping: '$args' },
310
+ { path: `baseWhere.${filterField}.eq`, mapping: '$source._id' },
311
+ { path: `baseWhere._shapeId.eq`, value: relatedShape.id }
312
+ ]
313
+ }
314
+ }
315
+ };
316
+ }
317
+ }
318
+ }
319
+ }
320
+ return projectSchema;
321
+ }
322
+ exports.addRelatedFields = addRelatedFields;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildRuntimeSchema = exports.applyDefaultsAndFlatten = exports.applyLegacyCompatibilityTweaks = void 0;
7
+ const errors_1 = require("@takeshape/errors");
8
+ const compose_js_1 = __importDefault(require("lodash/fp/compose.js"));
9
+ const util_1 = require("@takeshape/util");
10
+ const set_js_1 = __importDefault(require("lodash/set.js"));
11
+ const isError_js_1 = __importDefault(require("lodash/isError.js"));
12
+ const types_1 = require("./types");
13
+ const schema_util_1 = require("./schema-util");
14
+ const relationships_1 = require("./relationships");
15
+ const flatten_templates_1 = require("./flatten-templates");
16
+ const agents_1 = require("./agents");
17
+ const service_dependencies_1 = require("./service-dependencies");
18
+ function applyLegacyCompatibilityTweaks(projectSchema) {
19
+ const newSchema = (0, util_1.deepClone)(projectSchema);
20
+ let hasSearchableShapes = false;
21
+ for (const [shapeName, shape] of Object.entries(newSchema.shapes)) {
22
+ if ((0, types_1.isModelShape)(shape)) {
23
+ hasSearchableShapes = true;
24
+ // Magic _contentTypeId field used for V1 compatibility
25
+ (0, set_js_1.default)(shape, ['schema', 'properties', '_contentTypeId'], { type: 'string' });
26
+ (0, set_js_1.default)(shape, ['schema', 'properties', '_contentTypeName'], { type: 'string' });
27
+ if (shape.model?.type !== 'single') {
28
+ newSchema.queries[`search${shapeName}Index`] = {
29
+ shape: `SearchResults<${shapeName}>`,
30
+ resolver: {
31
+ name: 'takeshape:search',
32
+ service: 'takeshape',
33
+ shapeName
34
+ },
35
+ args: `TSSearchArgs<${shapeName}>`
36
+ };
37
+ }
38
+ }
39
+ if (shapeName === 'Asset') {
40
+ // Magic s3Key field to provide V1 compatibility with old projects until the field is
41
+ // formally removed and users are notified
42
+ (0, set_js_1.default)(shape, ['schema', 'properties', 's3Key'], {
43
+ title: 's3 key',
44
+ type: 'string',
45
+ '@mapping': 'shapedb:Asset.Hk6FQuz5',
46
+ '@deprecationReason': 'Use path instead'
47
+ });
48
+ }
49
+ }
50
+ if (hasSearchableShapes) {
51
+ // A placeholder shape for the TSSearchable interface
52
+ newSchema.shapes.TSSearchable = (0, schema_util_1.createShape)('TSSearchable', { type: 'object', properties: {} });
53
+ newSchema.queries.search = {
54
+ shape: 'SearchResults<TSSearchable>',
55
+ resolver: {
56
+ name: 'takeshape:search',
57
+ service: 'takeshape'
58
+ },
59
+ args: 'TSSearchArgs<TSSearchable>'
60
+ };
61
+ }
62
+ (0, relationships_1.addRelatedFields)(newSchema, (0, relationships_1.findExistingRelationships)(projectSchema, projectSchema.shapes));
63
+ return newSchema;
64
+ }
65
+ exports.applyLegacyCompatibilityTweaks = applyLegacyCompatibilityTweaks;
66
+ exports.applyDefaultsAndFlatten = (0, compose_js_1.default)(flatten_templates_1.flattenTemplates, applyLegacyCompatibilityTweaks, schema_util_1.applyDefaultsToSchema, agents_1.addAiQueries);
67
+ function buildRuntimeSchema(projectSchema, serviceLayers, log) {
68
+ try {
69
+ const projectSchemaWithDependencies = (0, service_dependencies_1.resolveSchemaShapeDependencies)(projectSchema, serviceLayers);
70
+ return (0, exports.applyDefaultsAndFlatten)(projectSchemaWithDependencies);
71
+ }
72
+ catch (err) {
73
+ const error = new errors_1.SchemaBuildError('An error occurred while building the schema', {
74
+ cause: (0, isError_js_1.default)(err) ? err : undefined,
75
+ schema: projectSchema
76
+ });
77
+ log('build runtime error', error);
78
+ throw error;
79
+ }
80
+ }
81
+ exports.buildRuntimeSchema = buildRuntimeSchema;
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveSchemaShapeDependencies = exports.collectReferencedShapeNames = void 0;
7
+ const util_1 = require("@takeshape/util");
8
+ const get_js_1 = __importDefault(require("lodash/get.js"));
9
+ const set_js_1 = __importDefault(require("lodash/set.js"));
10
+ const isObject_js_1 = __importDefault(require("lodash/isObject.js"));
11
+ const pick_js_1 = __importDefault(require("lodash/pick.js"));
12
+ const types_1 = require("./types");
13
+ const refs_1 = require("./refs");
14
+ const interfaces_1 = require("./interfaces");
15
+ const schema_util_1 = require("./schema-util");
16
+ function getGraphQLServiceShapeMap(layers) {
17
+ return Object.assign({}, ...Object.values(layers).map(layer => layer.schema?.shapes ?? {}));
18
+ }
19
+ function schemaExtendsShape(context, shapeName, schema) {
20
+ return ((0, types_1.isExtendsSchema)(schema) &&
21
+ schema.extends.some((item) => (0, refs_1.getRefShapeName)(context, item) === shapeName));
22
+ }
23
+ function getMissingPropertyRefs(projectSchema, getNamespace) {
24
+ const propertyRefs = (0, refs_1.getAllPropertyRefs)(projectSchema);
25
+ return propertyRefs.filter(ref => {
26
+ const path = (0, refs_1.propertyRefItemToPath)(getNamespace, ref);
27
+ return !(0, get_js_1.default)(projectSchema, path);
28
+ });
29
+ }
30
+ function refToQuery(serviceSchemas, ref) {
31
+ const serviceSchema = serviceSchemas[ref.serviceId].schema;
32
+ if (serviceSchema) {
33
+ if (ref.shapeName === 'Query' || ref.shapeName === 'Mutation') {
34
+ const operation = ref.shapeName === 'Query' ? 'queries' : 'mutations';
35
+ return serviceSchema[operation][ref.propertyName];
36
+ }
37
+ }
38
+ }
39
+ function getShapeRefs(services, serviceSchemas, propertyRefs) {
40
+ const results = [];
41
+ for (const ref of propertyRefs) {
42
+ const query = refToQuery(serviceSchemas, ref);
43
+ const layerSchema = serviceSchemas[ref.serviceId]?.schema;
44
+ if (query && layerSchema) {
45
+ const layerWithService = {
46
+ ...layerSchema,
47
+ services: (0, pick_js_1.default)(services, [ref.serviceId]) // include own service so refs have isValidService: true
48
+ };
49
+ results.push(...(0, schema_util_1.getAllRefsInQuery)(layerWithService, [], query));
50
+ }
51
+ }
52
+ return results;
53
+ }
54
+ function interfacesToShapeNames(projectSchema, shapeRefArray) {
55
+ return shapeRefArray.map(shapeRef => (0, refs_1.refItemToNamespacedShapeName)((0, refs_1.refExpressionToRefItem)(projectSchema, shapeRef)));
56
+ }
57
+ /**
58
+ * Add all referenced shape names to the refSet meant to be used as a callback to visitSchemaProperties
59
+ */
60
+ function collectReferencedShapeNames(context, shapeNames) {
61
+ const addRef = (refItem) => {
62
+ shapeNames.add((0, refs_1.refItemToNamespacedShapeName)(refItem));
63
+ };
64
+ const collect = (schema) => {
65
+ const args = schema['@args'];
66
+ if (args && (0, isObject_js_1.default)(args) && (0, types_1.isObjectSchema)(args)) {
67
+ Object.values(args.properties).forEach(collect);
68
+ }
69
+ const argsRefItem = (0, schema_util_1.getArgsReference)(context, schema);
70
+ if (argsRefItem) {
71
+ addRef(argsRefItem);
72
+ }
73
+ const refItem = (0, refs_1.getRef)(context, schema);
74
+ if (refItem) {
75
+ addRef(refItem);
76
+ }
77
+ };
78
+ return collect;
79
+ }
80
+ exports.collectReferencedShapeNames = collectReferencedShapeNames;
81
+ /**
82
+ * Returns a schema with all of its dependencies resolved.
83
+ * SHOULD only return the minimum set of remote dependencies required to enable
84
+ * the local shape definitions.
85
+ */
86
+ function resolveSchemaShapeDependencies(projectSchema, serviceLayers) {
87
+ const newSchema = (0, util_1.deepClone)(projectSchema);
88
+ if ((0, types_1.isProjectSchemaWithServices)(projectSchema)) {
89
+ const newShapeMap = {};
90
+ const requiredShapeNames = new Set((0, schema_util_1.getAllNamespaceShapes)(projectSchema));
91
+ const getNamespace = (0, refs_1.createGetNamespace)(projectSchema);
92
+ const missingPropertyRefs = getMissingPropertyRefs(projectSchema, getNamespace);
93
+ if (requiredShapeNames.size || missingPropertyRefs.length) {
94
+ const remoteShapeMap = getGraphQLServiceShapeMap(serviceLayers);
95
+ // Add shape references from queries
96
+ (0, util_1.addAll)(requiredShapeNames, getShapeRefs(projectSchema.services, serviceLayers, missingPropertyRefs)
97
+ .filter(schema_util_1.isValidRefItem)
98
+ .map(refs_1.refItemToNamespacedShapeName));
99
+ // Add referenced queries and mutations
100
+ for (const ref of missingPropertyRefs) {
101
+ const query = refToQuery(serviceLayers, ref);
102
+ if (query) {
103
+ (0, set_js_1.default)(newSchema, (0, refs_1.propertyRefItemToPath)(getNamespace, ref), query);
104
+ }
105
+ }
106
+ // Gather all the remote dependencies that our local schema requires.
107
+ // If the required shape exists in the remote shape map, visit the
108
+ // properties on the remote shape's schema and ensure they are required
109
+ // as well.
110
+ const collectDependencies = (shapeNames) => {
111
+ for (const name of shapeNames) {
112
+ const localShape = newSchema.shapes[name];
113
+ const remoteShape = remoteShapeMap[name];
114
+ // Don't check for dependencies if the shape is already in our schema unless it extends a remote shape
115
+ // getNamespacedShapeNameList already collected those dependencies
116
+ if ((!localShape || (localShape && schemaExtendsShape(projectSchema, name, localShape.schema))) &&
117
+ remoteShape) {
118
+ (0, schema_util_1.visitSchemaProperties)(remoteShape.schema, [remoteShape.name, 'schema'], collectReferencedShapeNames({ services: newSchema.services }, shapeNames));
119
+ }
120
+ }
121
+ };
122
+ collectDependencies(requiredShapeNames);
123
+ // Collect interfaces that requiredShapeNames implement
124
+ const interfaceNames = new Set();
125
+ const requiredShapes = (0, util_1.mapSet)(requiredShapeNames, shapeName => newSchema.shapes[shapeName] ?? remoteShapeMap[shapeName]);
126
+ for (const shape of requiredShapes) {
127
+ if (shape?.interfaces) {
128
+ (0, util_1.addAll)(interfaceNames, interfacesToShapeNames(projectSchema, shape.interfaces));
129
+ }
130
+ }
131
+ // Collect implementations for interfaces in requiredShapeNames
132
+ const implementationNames = new Set();
133
+ const implementations = (0, interfaces_1.getImplementationShapeNameMap)({ ...newSchema.shapes, ...remoteShapeMap });
134
+ for (const shape of requiredShapes) {
135
+ if ((0, interfaces_1.isInterfaceShape)(shape) && implementations[shape.name]) {
136
+ (0, util_1.addAll)(implementationNames, implementations[shape.name]);
137
+ }
138
+ }
139
+ // Collect the dependencies for the interfaces and implementations
140
+ collectDependencies(interfaceNames);
141
+ collectDependencies(implementationNames);
142
+ (0, util_1.addAll)(requiredShapeNames, interfaceNames);
143
+ (0, util_1.addAll)(requiredShapeNames, implementationNames);
144
+ // Populate a ShapeMap with the remote shapes we've gathered
145
+ // A combined context which prefers remote shapes for use when merging/inlining
146
+ const mergingContext = { services: newSchema.services, shapes: { ...newSchema.shapes, ...remoteShapeMap } };
147
+ for (const name of requiredShapeNames) {
148
+ const localShape = newSchema.shapes[name];
149
+ const remoteShape = remoteShapeMap[name];
150
+ if (localShape && remoteShape && schemaExtendsShape(mergingContext, name, localShape.schema)) {
151
+ // Merge and inline combined shape when a local shape extends a remote shape
152
+ newShapeMap[name] = {
153
+ ...localShape,
154
+ schema: (0, refs_1.dereferenceObjectSchema)(mergingContext, newSchema.shapes[name].schema)
155
+ };
156
+ }
157
+ else if (localShape ?? remoteShape) {
158
+ newShapeMap[name] = localShape ?? remoteShape;
159
+ }
160
+ }
161
+ }
162
+ // Assign remote shapes to the new schema, mutating the new schema
163
+ Object.assign(newSchema.shapes, (0, interfaces_1.pruneUnusedInterfaces)(newSchema, newShapeMap));
164
+ }
165
+ return newSchema;
166
+ }
167
+ exports.resolveSchemaShapeDependencies = resolveSchemaShapeDependencies;
@@ -3,48 +3,53 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.patchSchema = exports.shallowMerge = void 0;
7
- const forIn_js_1 = __importDefault(require("lodash/forIn.js"));
6
+ exports.patchSchema = void 0;
8
7
  const omit_js_1 = __importDefault(require("lodash/omit.js"));
9
- const schemaUpdateMergedKeys = ['queries', 'mutations', 'shapes', 'workflows', 'forms', 'services'];
8
+ const get_js_1 = __importDefault(require("lodash/get.js"));
9
+ const set_js_1 = __importDefault(require("lodash/fp/set.js"));
10
+ const unset_js_1 = __importDefault(require("lodash/unset.js"));
11
+ /**
12
+ * Array of lodash.get paths to keys that are merged when applying a schema update.
13
+ */
14
+ const schemaUpdateMergedKeys = [
15
+ 'queries',
16
+ 'mutations',
17
+ 'shapes',
18
+ 'workflows',
19
+ 'forms',
20
+ 'services',
21
+ 'ai-experimental.agents'
22
+ ];
10
23
  function shallowMerge(original, update) {
11
24
  const result = { ...original };
12
- if (update) {
13
- (0, forIn_js_1.default)(update, (obj, key) => {
14
- // Null is a sentinel value to indicate delete
15
- if (obj === null) {
16
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
17
- delete result[key];
18
- }
19
- else {
20
- result[key] = obj;
21
- }
22
- });
25
+ for (const [key, obj] of Object.entries(update)) {
26
+ // Null is a sentinel value to indicate delete
27
+ if (obj === null) {
28
+ (0, unset_js_1.default)(result, key);
29
+ }
30
+ else {
31
+ result[key] = obj;
32
+ }
23
33
  }
24
34
  return result;
25
35
  }
26
- exports.shallowMerge = shallowMerge;
27
36
  /**
28
- * Apply a schema update to a schema.
29
- * Resulting schema is not assumed to be valid and should be validated if appropriate.
37
+ * Apply a schema update to a schema. This can operate on a schema with frozen properties,
38
+ * such as one produced by immer. Resulting schema is not assumed to be valid and should
39
+ * be validated if appropriate.
30
40
  */
31
- function patchSchema(projectSchema, update) {
41
+ function patchSchema(projectSchema, schemaUpdate) {
32
42
  // Patch old schema with update
33
- const updatedSchema = {
43
+ let updatedSchema = {
34
44
  ...projectSchema,
35
- ...(0, omit_js_1.default)(update, [...schemaUpdateMergedKeys, 'ai-experimental'])
45
+ ...(0, omit_js_1.default)(schemaUpdate, schemaUpdateMergedKeys)
36
46
  };
37
47
  for (const key of schemaUpdateMergedKeys) {
38
- if (update[key]) {
39
- updatedSchema[key] = shallowMerge(projectSchema[key], update[key]);
48
+ const update = (0, get_js_1.default)(schemaUpdate, key);
49
+ if (update) {
50
+ updatedSchema = (0, set_js_1.default)(key, shallowMerge((0, get_js_1.default)(projectSchema, key) ?? {}, update), updatedSchema);
40
51
  }
41
52
  }
42
- if (update['ai-experimental']?.agents) {
43
- const ai = {
44
- agents: shallowMerge(projectSchema['ai-experimental']?.agents, update['ai-experimental'].agents)
45
- };
46
- updatedSchema['ai-experimental'] = ai;
47
- }
48
53
  return updatedSchema;
49
54
  }
50
55
  exports.patchSchema = patchSchema;
@@ -141,6 +141,9 @@ function enumerateBasicResolvers(resolver, path) {
141
141
  visit(resolver, path);
142
142
  return results;
143
143
  }
144
+ const isValidAgentMutation = (projectSchema, mutationName) => {
145
+ return Boolean(projectSchema['ai-experimental']?.agents?.[mutationName]);
146
+ };
144
147
  const validateAIToolConfig = (projectSchema, getNamespace, tool, basePath) => {
145
148
  const toolRef = (0, ai_tools_1.getToolRef)(tool);
146
149
  const path = typeof tool === 'string' ? basePath : basePath.concat('ref');
@@ -164,6 +167,9 @@ const validateAIToolConfig = (projectSchema, getNamespace, tool, basePath) => {
164
167
  };
165
168
  }
166
169
  const property = (0, get_js_1.default)(projectSchema, propertyPath);
170
+ if (propertyPath[0] === 'mutations' && isValidAgentMutation(projectSchema, propertyPath[1])) {
171
+ return;
172
+ }
167
173
  if (!property) {
168
174
  return {
169
175
  type: 'notFound',
@@ -1,4 +1,6 @@
1
1
  import upperFirst from 'lodash/upperFirst.js';
2
+ import uniq from 'lodash/uniq.js';
3
+ import uniqBy from 'lodash/uniqBy.js';
2
4
  export const END_AGENT_EXECUTION = 'endAgentExecution';
3
5
  export const BUILT_IN_CHAT_ARGS = [
4
6
  {
@@ -47,3 +49,84 @@ export const getAgentEndStates = (agent, includeSuspend = false) => {
47
49
  export const getInspectAgentSessionQueryName = (agentName) => {
48
50
  return `inspect${upperFirst(agentName)}`;
49
51
  };
52
+ export const removeBuiltInArgs = (args) => {
53
+ return args.filter(arg => !BUILT_IN_CHAT_ARG_NAMES.includes(arg.argName));
54
+ };
55
+ export const createArgs = (agent) => {
56
+ let apiArguments = uniqBy(agent.api.arguments ?? [], arg => arg.argName);
57
+ if (agent.api.type === 'chat') {
58
+ apiArguments = removeBuiltInArgs(apiArguments).concat(BUILT_IN_CHAT_ARGS);
59
+ }
60
+ return apiArguments.length > 0
61
+ ? {
62
+ type: 'object',
63
+ properties: apiArguments.reduce((acc, argument) => {
64
+ acc[argument.argName] = {
65
+ type: argument.argType === 'sessionId' ? 'string' : argument.argType
66
+ };
67
+ return acc;
68
+ }, {}),
69
+ required: apiArguments.filter(arg => arg.required).map(arg => arg.argName)
70
+ }
71
+ : undefined;
72
+ };
73
+ export function addAiQueries(projectSchema) {
74
+ const agents = projectSchema['ai-experimental']?.agents;
75
+ if (!agents) {
76
+ return projectSchema;
77
+ }
78
+ const newSchema = {
79
+ ...projectSchema,
80
+ queries: { ...projectSchema.queries },
81
+ mutations: { ...projectSchema.mutations }
82
+ };
83
+ for (const [agentName, agent] of Object.entries(agents)) {
84
+ // Get valid return types based on states that could possibly be the end state
85
+ // TODO In the future the agent will have a return type defined and we will instead validate
86
+ // that all the return states will return the correct shape.
87
+ const returnStates = [...getAgentEndStates(agent, true)];
88
+ const returnTypes = uniq(returnStates.map(stateId => {
89
+ const { execution } = agent.states[stateId];
90
+ if (execution.type === 'chat') {
91
+ return 'TSChatResponse';
92
+ }
93
+ if (execution.type === 'generate') {
94
+ return execution.outputShape ? execution.outputShape : 'string';
95
+ }
96
+ return 'JSON';
97
+ }));
98
+ const shape = returnTypes.length === 0 ? 'string' : returnTypes.length === 1 ? returnTypes[0] : 'JSON';
99
+ if (newSchema.mutations[agentName] !== undefined) {
100
+ throw new Error(`Schema already has a mutation with the name ${agentName}`);
101
+ }
102
+ newSchema.mutations[agentName] = {
103
+ description: agent.description,
104
+ args: createArgs(agent),
105
+ shape,
106
+ resolver: {
107
+ name: 'ai:runAgent',
108
+ agentName
109
+ }
110
+ };
111
+ if (shape === 'TSChatResponse') {
112
+ newSchema.queries[getInspectAgentSessionQueryName(agentName)] ||= {
113
+ description: `Inspect a session for the ${agentName} agent`,
114
+ args: {
115
+ type: 'object',
116
+ properties: {
117
+ sessionId: {
118
+ type: 'string'
119
+ }
120
+ },
121
+ required: ['sessionId']
122
+ },
123
+ shape: 'TSAgentSession',
124
+ resolver: {
125
+ name: 'ai:inspectAgentSession',
126
+ agentName
127
+ }
128
+ };
129
+ }
130
+ }
131
+ return newSchema;
132
+ }
@@ -42,3 +42,5 @@ export * from "./util/merge.js";
42
42
  export * from "./util/patch-schema.js";
43
43
  export * from "./util/shapes.js";
44
44
  export * from "./constants.js";
45
+ export * from "./runtime-schema.js";
46
+ export * from "./service-dependencies.js";