Skip to content

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.

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' },
});

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 features
const 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" feature
const 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" }

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 term
console.log(alice.term.features); // { name: {type: "String", value: "Alice"}, ... }
console.log(alice.state); // "complete" | "residuated" | "no_witnesses"

Inference operates on a separate homoiconic representation using the untagged format. Use the psi() builder for terms and plain literals for feature values.

Facts are ground terms – concrete data the inference engine can reason about.

// Add facts about employees
await 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,
}),
});

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 > 100000
await 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.

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`);

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}`);
}
}

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}`);
}
}
  • Configuration – customize timeouts, retries, interceptors, and per-call overrides
  • API Reference – full reference for all resource clients, types, and builders