Quick Start
This guide walks through a complete workflow: defining a sort hierarchy, creating terms, adding inference rules and facts, and running backward chaining to answer a query.
1. Create the client
Every interaction starts by creating a ReasoningLayerClient. The two required options are baseUrl (where the Reasoning Layer backend is running) and tenantId (a UUID identifying your tenant).
import { ReasoningLayerClient, Value, psi, constrained,} from '@kortexya/reasoninglayer';
const client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: '550e8400-e29b-41d4-a716-446655440000', auth: { mode: 'cookie' },});2. Create a sort hierarchy
Sorts are types in a multiple-inheritance lattice. Create a person sort and an employee sort that inherits from it.
// Create the "person" sort with name and age featuresconst person = await client.sorts.createSort({ name: 'person', features: [ { name: 'name', required: true }, { name: 'age', required: false }, ],});
// Create "employee" as a child of "person", adding a "department" featureconst employee = await client.sorts.createSort({ name: 'employee', parents: [person.id], features: [ { name: 'department', required: true }, { name: 'salary', required: false }, ],});You can also create an entire hierarchy in a single request using bulkCreateSorts, which accepts sort names as parent references instead of UUIDs:
const bulk = await client.sorts.bulkCreateSorts({ sorts: [ { name: 'person', features: [{ name: 'name', required: true }] }, { name: 'employee', parents: ['person'], features: [{ name: 'department', required: true }] }, { name: 'manager', parents: ['employee'], features: [{ name: 'reports', required: false }] }, ],});
console.log(bulk.sortIds);// { person: "uuid-1", employee: "uuid-2", manager: "uuid-3" }3. Create terms
Terms are instances of sorts. For simple string and integer values, pass plain literals. For complex values (references, fuzzy scalars, fuzzy numbers, sets), use the Value.* builders.
const alice = await client.terms.createTerm({ sortId: employee.id, ownerId: '00000000-0000-0000-0000-000000000001', features: { name: 'Alice', age: 30, department: 'Engineering', salary: 120000, },});
console.log(alice.term.id); // UUID of the created termconsole.log(alice.term.features); // { name: {type: "String", value: "Alice"}, ... }console.log(alice.state); // "complete" | "residuated" | "no_witnesses"4. Add rules and facts for inference
Inference operates on a separate homoiconic representation using the untagged format. Use the psi() builder for terms and plain literals for feature values.
Add facts
Facts are ground terms — concrete data the inference engine can reason about.
// Add facts about employeesawait client.inference.addFact({ term: psi('employee', { name: 'Alice', department: 'Engineering', salary: 120000, }),});
await client.inference.addFact({ term: psi('employee', { name: 'Bob', department: 'Engineering', salary: 95000, }),});
await client.inference.addFact({ term: psi('employee', { name: 'Carol', department: 'Sales', salary: 110000, }),});Add rules
Rules define logical relationships. A rule has a head (conclusion) and antecedents (conditions).
import { guard } from '@kortexya/reasoninglayer';
// Rule: A "senior_employee" is an employee with salary > 100000await client.inference.addRule({ term: psi('senior_employee', { name: '?Name', department: '?Dept', }), antecedents: [ psi('employee', { name: '?Name', department: '?Dept', salary: constrained('?Salary', guard('gt', 100000)), }), ], certainty: 1.0,});Variables (prefixed with ?) unify across the head and body of a rule. The constrained('?Salary', guard("gt", 100000)) constrains ?Salary to values greater than 100,000.
5. Run backward chaining
Backward chaining searches for matching data by working backwards through rules from your goal.
const result = await client.inference.backwardChain({ goal: psi('senior_employee', { name: '?Name', department: '?Dept', }), maxSolutions: 10,});
console.log(`Found ${result.solutions.length} solutions in ${result.queryTimeMs}ms`);6. Interpret solutions
Each solution contains variable bindings showing how the goal was satisfied.
for (const solution of result.solutions) { console.log(`Certainty: ${solution.certainty}`);
for (const binding of solution.substitution.bindings) { console.log(` ${binding.variableName} = ${binding.boundToDisplay}`); } // Output: // ?Name = Alice // ?Dept = Engineering // // ?Name = Carol // ?Dept = Sales
// Optional: inspect the proof tree if (solution.proof) { console.log(` Proved via rule: ${solution.proof.ruleTermId}`); }}Error handling
All SDK errors extend ReasoningLayerError, enabling structured error handling:
import { ReasoningLayerError, ApiError, AuthenticationError, ForbiddenError, NotFoundError, RateLimitError, TimeoutError,} from '@kortexya/reasoninglayer';
try { const sort = await client.sorts.getSort('nonexistent-uuid');} catch (error) { if (error instanceof AuthenticationError) { console.log('Invalid or missing credentials'); } else if (error instanceof ForbiddenError) { console.log('Insufficient permissions'); } else if (error instanceof NotFoundError) { console.log('Sort not found'); } else if (error instanceof RateLimitError) { console.log(`Rate limited. Retry after ${error.retryAfter}s`); } else if (error instanceof TimeoutError) { console.log(`Request timed out after ${error.timeoutMs}ms`); } else if (error instanceof ApiError) { console.log(`API error ${error.status}: ${error.message}`); } else if (error instanceof ReasoningLayerError) { console.log(`SDK error: ${error.message}`); }}Next steps
- Configuration — customize timeouts, retries, interceptors, and per-call overrides
- API Reference — full reference for all resource clients, types, and builders