Creating Sorts
Sorts are the type system of the Reasoning Layer. They form a lattice with multiple inheritance, feature declarations, and inter-feature constraints. Every Psi-term (data instance) belongs to a sort.
Creating a single sort
Section titled “Creating a single sort”Use client.sorts.createSort() to create a sort. The method accepts a CreateSortRequest and returns the created SortDto.
import { ReasoningLayerClient } from '@kortexya/reasoninglayer';
const client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: 'my-tenant-uuid', auth: { mode: 'cookie' },});
const person = await client.sorts.createSort({ name: 'person', description: 'A human being',});
console.log(person.id); // UUID assigned by the serverconsole.log(person.name); // "person"Using the SortBuilder
Section titled “Using the SortBuilder”The SortBuilder provides a fluent API for constructing complex sort definitions with features, parents, and constraints.
import { SortBuilder } from '@kortexya/reasoninglayer';
const request = SortBuilder.create('employee') .parent(personSortId) .feature({ name: 'salary', required: true, constraint: { type: 'IntegerRange', value: { min: 0, max: 10_000_000 } }, }) .feature({ name: 'department', required: true, expectedSort: 'department', }) .feature({ name: 'email', required: false, constraint: { type: 'StringPattern', value: '^[^@]+@[^@]+$' }, }) .feature({ name: 'bio', required: false, description: 'A short biography, written by the employee', index: 'text_and_vector', }) .boundConstraint({ constraintType: 'upper', target: 'endDate', sourcePath: 'company.dissolutionDate', }) .description('An employee at a company') .build();
const employee = await client.sorts.createSort(request);A feature description is prose for semantic embeddings and documentation. A
feature index states how the feature takes part in the retrieval indexes:
not_indexed, text, vector or text_and_vector. When you create a sort
that already exists, a feature that does not state description or index
keeps its stored value.
Deterministic UUIDs
Section titled “Deterministic UUIDs”You can assign a specific UUID to a sort, which is useful for homoiconic cross-references between sorts, rules, and facts.
const request = SortBuilder.create('sensor_reading') .withId('550e8400-e29b-41d4-a716-446655440000') .feature({ name: 'value', required: true }) .build();Multiple inheritance
Section titled “Multiple inheritance”Sorts support multiple parents. Call .parent() multiple times or use .parents() with an array.
const request = SortBuilder.create('teaching_assistant') .parent(studentSortId) .parent(staffSortId) .feature({ name: 'courses_assisting', required: true }) .build();
// Equivalent:const request2 = SortBuilder.create('teaching_assistant') .parents([studentSortId, staffSortId]) .feature({ name: 'courses_assisting', required: true }) .build();Bulk creation with name-based parents
Section titled “Bulk creation with name-based parents”When defining an entire hierarchy at once, use bulkCreateSorts. Unlike createSort, parent references use sort names (not UUIDs), which are resolved server-side.
const result = await client.sorts.bulkCreateSorts({ sorts: [ { name: 'animal', description: 'Base animal sort' }, { name: 'mammal', parents: ['animal'] }, { name: 'bird', parents: ['animal'] }, { name: 'bat', parents: ['mammal'], description: 'A flying mammal' }, { name: 'penguin', parents: ['bird'], features: [{ name: 'colony_size', required: false }], }, ],});
console.log(result.createdCount); // 5console.log(result.sortIds); // { animal: "uuid1", mammal: "uuid2", ... }
// Check for partial failuresif (result.errors && result.errors.length > 0) { for (const err of result.errors) { console.error(`Failed to create ${err.name}: ${err.error}`); }}GLB and LUB computation
Section titled “GLB and LUB computation”The Greatest Lower Bound (GLB) is the most specific common sort of two sorts. The Least Upper Bound (LUB) is the most general common supersort.
// Compute GLBconst glb = await client.sorts.computeGlb({ sort1Id: employeeSortId, sort2Id: customerSortId,});
if (glb.glb) { console.log(`GLB sort: ${glb.glb}`);} else { console.log('No common lower bound exists');}
// Compute LUBconst lub = await client.sorts.computeLub({ sort1Id: employeeSortId, sort2Id: customerSortId,});
if (lub.lub) { console.log(`LUB sort: ${lub.lub}`);}Decoding a GLB as a type disjunction
Section titled “Decoding a GLB as a type disjunction”When the GLB does not correspond to a single named sort, decodeGlb returns a human-readable disjunction.
const decoded = await client.sorts.decodeGlb({ sort1Id: sortA, sort2Id: sortB,});
console.log(decoded.formatted); // "int | real"console.log(decoded.isBottom); // falseconsole.log(decoded.sortNames); // ["int", "real"]console.log(decoded.sortIds); // ["uuid1", "uuid2"]Convenience aliases
Section titled “Convenience aliases”You can also use convenience aliases with simpler positional arguments:
const glb = await client.sorts.findCommonSubtype(employeeSortId, customerSortId);const lub = await client.sorts.findCommonSupertype(employeeSortId, customerSortId);const decoded = await client.sorts.explainCommonSubtype(sortA, sortB);Sort comparison
Section titled “Sort comparison”Compare two sorts using one of six comparison operators.
// Check if employee is a subtype of personconst result = await client.sorts.compareSorts({ operator: 'isa_le', sortA: 'employee', sortB: 'person',});
console.log(result.result); // trueAvailable operators:
| Operator | Meaning |
|---|---|
isa_le |
A is a subtype of or equal to B |
isa_lt |
A is a strict subtype of B |
isa_ge |
A is a supertype of or equal to B |
isa_gt |
A is a strict supertype of B |
isa_eq |
A and B are the same sort |
isa_cmp |
General comparison (returns comparison result) |
There is also a convenience method for subtype checking:
const isSub = await client.sorts.isSubtype(employeeSortId, personSortId);console.log(isSub); // trueSort similarity
Section titled “Sort similarity”Set and retrieve fuzzy similarity between two sorts. Similarity is a symmetric value from 0.0 to 1.0.
// Set similarity between two sortsawait client.sorts.setSortSimilarity({ sort1Id: catSortId, sort2Id: dogSortId, degree: 0.7,});
// Retrieve similarityconst sim = await client.sorts.getSortSimilarity({ sort1Id: catSortId, sort2Id: dogSortId,});
console.log(sim.degree); // 0.7Navigating the hierarchy
Section titled “Navigating the hierarchy”// Direct childrenconst children = await client.sorts.getChildren(animalSortId);
// Direct parentsconst parents = await client.sorts.getParents(mammalSortId);
// All ancestors (transitive)const ancestors = await client.sorts.getAncestors(batSortId);
// All descendants (transitive)const descendants = await client.sorts.getDescendants(animalSortId);
// Compatible sortsconst compatible = await client.sorts.getCompatible(mammalSortId);Listing and deleting sorts
Section titled “Listing and deleting sorts”// List all sorts in the tenantconst allSorts = await client.sorts.listSorts();
// Delete a sortawait client.sorts.deleteSort(sortId);A delete refuses by default when the sort holds data terms, when a rule names it,
or when a child sort would be left with no parent. Each of the three has its own
option, and each option defaults to refuse. Preview the delete first to see
what it would remove and why it would be refused. The preview removes nothing.
import { sortDeleteRefusalOf } from '@kortexya/reasoninglayer';
const options = { terms: 'delete', descendants: 'delete' } as const;const preview = await client.sorts.previewSortDelete(animalSortId, options);if (preview.deletable) { // Removes the sort and every sort below it, leaves first, with their terms. const removed = await client.sorts.deleteSort(animalSortId, options); console.log(removed?.sortsDeleted);} else { console.log(preview.refusal?.code, preview.refusal?.message);}
// A refused delete throws an ApiError with a coded body.try { await client.sorts.deleteSort(mammalSortId);} catch (error) { console.log(sortDeleteRefusalOf(error)?.code); // e.g. 'sort_would_orphan_children'}Moving a sort
Section titled “Moving a sort”reparentSort replaces the parent set of a sort. The sort keeps its id, its
features and its subtree. The response reports the inherited features that
each moved sort gained and lost.
const moved = await client.sorts.reparentSort(batSortId, { parents: [mammalSortId, flyingSortId],});console.log(moved.previousParents, moved.parents, moved.inheritanceChanges);The OSFQL form is ALTER SORT bat PARENTS (mammal, flying);.