Builders
The SDK provides builder namespaces that construct the correct serialization format for each API context. Builders are the primary developer ergonomics layer — they ensure type safety, prevent format mismatches, and provide IntelliSense guidance.
Which builder to use where
| Context | Builder to use | Produces |
|---|---|---|
| Term CRUD features | Plain JS values or Value.* | Tagged ValueDto |
| Query features | Plain JS values or Value.* | Tagged ValueDto |
| Fuzzy operation features | Plain JS values or Value.* | Tagged ValueDto |
| Fuzzy membership shapes | FuzzyShape.* | FuzzyShapeDto |
| Inference rule/fact inputs | psi(), plain values | Untagged TermInputDto |
| Guard constraints | guard() | Untagged TermInputDto |
| Constrained variables | constrained() | ConstrainedPlainVar |
| Sort creation | SortBuilder | CreateSortRequest |
| Psi-term shorthand | psi() | PsiTermInput |
| Allen temporal constraints | allen() | Tagged ConstraintInputDto |
| LP optimization | LP.* | LinearProgramDefinition |
Value
The Value namespace builds complex tagged ValueDto values that have no plain JS equivalent. For simple types, pass plain JS values directly — the SDK auto-converts them.
Plain JS values (no builder needed):
// These are auto-converted to tagged ValueDto by the SDK:"hello" // -> {"type": "String", "value": "hello"}42 // -> {"type": "Integer", "value": 42}3.14 // -> {"type": "Real", "value": 3.14}true // -> {"type": "Boolean", "value": true}null // -> {"type": "Uninstantiated"}[...] // -> {"type": "List", "value": [...]}Use with: client.terms.*, client.query.*, fuzzy operations.
import { Value, FuzzyShape } from '@kortexya/reasoninglayer';
// Plain values (no builder needed):const features = { name: "Alice", age: 30, active: true };
// Complex types (require Value.*):Value.reference(termUuid) // {"type": "Reference", "value": "uuid"}
// Fuzzy valuesValue.fuzzyScalar(0.5, 0.8) // {"type": "FuzzyScalar", "value": {"value": 0.5, "membership": 0.8}}Value.fuzzyNumber( FuzzyShape.triangular(20, 22, 24)) // {"type": "FuzzyNumber", "value": {"shape": {"kind": "Triangular", ...}}}
// Sets (Smyth powerdomain)Value.set( ["uuid1"], // lower bound (definite members) ["uuid1", "uuid2", "uuid3"], // upper bound (possible members) "person_sort_id" // optional sort constraint) // {"type": "Set", "value": {"lower": [...], "upper": [...], ...}}Complete variant reference
| Method | Return type | JSON "type" value |
|---|---|---|
Value.reference(id) | ReferenceValue | "Reference" |
Value.fuzzyScalar(v,m) | FuzzyScalarValue | "FuzzyScalar" |
Value.fuzzyNumber(s) | FuzzyNumberValue | "FuzzyNumber" |
Value.set(lo,hi,sort?) | SetValue | "Set" |
Simple types (string, number, boolean, null, arrays) are passed as plain JS values and auto-converted by the SDK.
FuzzyShape
The FuzzyShape namespace builds fuzzy membership function shapes. These use "kind" as the discriminator field, not "type":
import { FuzzyShape } from '@kortexya/reasoninglayer';
// Triangular: peak at b, zero at a and cFuzzyShape.triangular(20, 22, 24)// {"kind": "Triangular", "a": 20, "b": 22, "c": 24}
// Trapezoidal: flat top between b and cFuzzyShape.trapezoidal(18, 20, 24, 26)// {"kind": "Trapezoidal", "a": 18, "b": 20, "c": 24, "d": 26}
// Gaussian: bell curve centered at meanFuzzyShape.gaussian(100, 15)// {"kind": "Gaussian", "mean": 100, "stdDev": 15}
// Cyclic Gaussian: wraps around a period (e.g., compass directions)FuzzyShape.cyclicGaussian(180, 30, 360)// {"kind": "CyclicGaussian", "mean": 180, "stdDev": 30, "period": 360}Combine with Value.fuzzyNumber() for use in term features:
const features = { temperature: Value.fuzzyNumber(FuzzyShape.triangular(20, 22, 24)), iqScore: Value.fuzzyNumber(FuzzyShape.gaussian(100, 15)),};psi
The psi() function creates format-agnostic psi-terms that the SDK converts to the correct wire format automatically. It is the primary way to build term inputs for inference.
Use with: client.inference.* for goals, rules, facts.
import { psi, constrained, guard } from '@kortexya/reasoninglayer';
// Simple term — plain JS values are used directlypsi("person", { name: "Alice", age: 30, active: true })// {"sortName": "person", "features": {"name": "Alice", "age": 30, "active": true}}
// Variables — strings starting with "?" are auto-detected as variablespsi("person", { name: "?Name", age: "?Age" })
// Nested psi-termspsi("employee", { person: psi("person", { name: "Bob" }), salary: 100000,})
// With constrained variablepsi("employee", { salary: constrained("?Salary", guard("gt", 50000)),})
// Term reference — strings starting with "!" are auto-detected as referencespsi("department", { manager: "!550e8400-..." })
// Uninstantiated values — null is acceptedpsi("employee", { name: "Alice", salary: null })
// No featurespsi("person")// {"sortName": "person"}Coercion rules
| JavaScript value | Coerced to |
|---|---|
string | Raw string |
"?X" (starts with ?) | Variable (auto-detected) |
"!uuid" (starts with !) | Term reference (auto-detected) |
number | Raw number |
boolean | Raw boolean |
null | Uninstantiated |
psi("sort", features) | Nested psi-term (recursive) |
constrained("?X", guard) | Constrained variable |
{termId: "uuid"} | Term reference |
{sortName: "x", features} | Inline term by name (recursive) |
{sortId: "x", features} | Inline term by sort ID (recursive) |
{name: "?X", constraint} | Constrained variable |
{name: "?X"} | Variable |
[...] | List (each element is coerced) |
constrained
The constrained() function creates a constrained variable for use in psi() features. It pairs a variable name with a guard or type constraint.
import { psi, constrained, guard } from '@kortexya/reasoninglayer';
// Constrained variable with a guardconstrained("?Salary", guard("gt", 100000))
// Use within psi():psi("employee", { name: "?Name", salary: constrained("?Salary", guard("gt", 100000)), age: constrained("?Age", guard("gte", 40)),})guard
The guard() function creates a TermInputDto representing a guard constraint. It is the primary way to create value constraints for variables.
import { guard } from '@kortexya/reasoninglayer';
// Create a guard constraintguard("gt", 100000)// {"sortName": "guard_constraint", "features": {"op": "gt", "right": 100000}}Available operators (GuardOp):
| Op | Meaning |
|---|---|
"lt" | Less than |
"lte" | Less than or equal |
"gt" | Greater than |
"gte" | Greater than or equal |
"eq" | Equal |
"ne" | Not equal |
The right argument accepts number, string, or boolean:
guard("gt", 100000) // numeric comparisonguard("eq", "active") // string comparisonguard("ne", false) // boolean comparisonCombining guard with inference
import { psi, constrained, guard } from '@kortexya/reasoninglayer';
// Find high earners over 40const goal = psi("employee", { name: "?Name", salary: constrained("?Salary", guard("gt", 100000)), age: constrained("?Age", guard("gte", 40)),});
const result = await client.inference.backwardChain({ goal });SortBuilder
The SortBuilder class provides a fluent API for constructing CreateSortRequest objects.
import { SortBuilder } from '@kortexya/reasoninglayer';
const request = SortBuilder.create("employee") .withId("550e8400-e29b-41d4-a716-446655440000") // optional deterministic UUID .parent(personSortId) // add a parent sort .parents([organizationMemberSortId]) // add multiple parents .feature({ name: "salary", required: true, constraint: { type: "IntegerRange", value: { min: 0, max: 10000000 } }, }) .feature({ name: "department", required: true }) .boundConstraint({ constraintType: "upper", target: "end_date", sourcePath: "company.dissolution_date", }) .description("An employee at a company") .build();
const sort = await client.sorts.createSort(request);SortBuilder methods
| Method | Description |
|---|---|
SortBuilder.create(name) | Start building a sort with the given name |
.withId(uuid) | Set a deterministic UUID |
.parent(parentId) | Add a single parent sort UUID |
.parents(parentIds) | Add multiple parent sort UUIDs |
.feature(descriptor) | Add a feature descriptor |
.boundConstraint(constraint) | Add an inter-feature bound constraint |
.description(text) | Set a human-readable description |
.build() | Build the CreateSortRequest |
allen
The allen() function creates Allen temporal constraint inputs (ConstraintInputDto of type "Allen").
import { allen } from '@kortexya/reasoninglayer';
// Event A must happen before Event Ballen("before", "event_a_time", "event-b-term-uuid")// {"type": "Allen", "intervalA": "event_a_time", "intervalBTermId": "event-b-term-uuid", "relation": "before"}
// Meeting overlaps with work hoursallen("overlaps", "meeting_time", "work-hours-term-uuid")
// Lecture starts at the same time as the examallen("starts", "lecture_time", "exam-term-uuid")The 13 Allen relations
| Relation | Inverse | Meaning |
|---|---|---|
before | after | A ends before B starts |
meets | met_by | A ends exactly when B starts |
overlaps | overlapped_by | A starts before B, ends during B |
starts | started_by | Same start time, A ends first |
during | contains | A entirely within B |
finishes | finished_by | Same end time, A starts later |
equals | equals | Same start and end |
Using Allen constraints in backward chaining
Allen constraints can be passed in the constraints field of a BackwardChainRequest:
const result = await client.inference.backwardChain({ goal: psi("scheduled_event", { name: "?Event", time: "?Time", }), constraints: [ allen("before", "?Time", meetingTermId), allen("after", "?Time", lunchTermId), ],});Complete example: combining builders
Here is a complete example that uses multiple builder namespaces together:
import { ReasoningLayerClient, SortBuilder, Value, FuzzyShape, psi, constrained, guard,} from '@kortexya/reasoninglayer';
const client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: 'your-tenant-uuid', auth: { mode: 'cookie' },});
// 1. Create sorts (using SortBuilder)const personSort = await client.sorts.createSort( SortBuilder.create("person") .feature({ name: "name", required: true }) .feature({ name: "age", required: false, constraint: { type: "Integer" } }) .build());
const employeeSort = await client.sorts.createSort( SortBuilder.create("employee") .parent(personSort.id) .feature({ name: "salary", required: true, constraint: { type: "IntegerRange", value: { min: 0, max: 10000000 } }, }) .build());
// 2. Create terms (using plain values — tagged format auto-converted)await client.terms.createTerm({ sortId: personSort.id, ownerId: "user-uuid", features: { name: "Alice", age: 30, },});
// 3. Add inference facts (using psi() — untagged format)await client.inference.addFact({ term: psi("employee", { name: "Alice", salary: 120000, }),});
// 4. Add rules with guardsawait client.inference.addRule({ term: psi("high_earner", { name: "?Name", }), antecedents: [ psi("employee", { name: "?Name", salary: constrained("?Salary", guard("gt", 100000)), }), ],});
// 5. Query (using backward chaining)const result = await client.inference.backwardChain({ goal: psi("high_earner", { name: "?Name", }),});
for (const solution of result.solutions) { for (const binding of solution.substitution.bindings) { console.log(`${binding.variableName} = ${binding.boundToDisplay}`); }}