Skip to content

Terms and Features

Terms are the core data structures of the Reasoning Layer. A term is a typed feature structure: it belongs to a sort and carries named features with values.

What is a term?

A term has:

  • A sort (its type in the lattice)
  • Named features with typed values
  • An identity (UUID) assigned by the server
  • A validation state indicating whether it satisfies its sort’s type witnesses

The key insight is that rules, facts, goals, and constraints are all represented as terms — data and logic share the same structure.

TermDto and TermResponse

Term CRUD endpoints return TermResponse, not raw TermDto. This is a critical distinction. TermResponse wraps the term with validation state:

interface TermResponse {
term: TermDto; // The underlying term data
state?: TermState; // Validation state
witnessProofs?: WitnessProofDto[]; // Proofs of satisfied witnesses
residualWitnesses?: ResidualWitnessDto[]; // Suspended witnesses
}
interface TermDto {
id: string; // Term UUID
sortId: string; // Sort (type) UUID
tenantId: string; // Owning tenant UUID
ownerId: string; // Owner user UUID
features: Record<string, ValueDto>; // Named features (tagged format)
}

Note that TermDto.features uses the tagged ValueDto format ({"type": "String", "value": "hello"}). For the untagged format used in inference, see Values and Formats.

Creating terms

Use plain JS values to construct feature values — the SDK auto-converts them to the tagged format:

import { Value, ReasoningLayerClient } from '@kortexya/reasoninglayer';
const client = new ReasoningLayerClient({
baseUrl: 'https://platform.ovh.reasoninglayer.ai',
tenantId: 'your-tenant-uuid',
auth: { mode: 'cookie' },
});
const response = await client.terms.createTerm({
sortId: personSortId,
ownerId: 'user-uuid',
features: {
name: "Alice",
age: 30,
active: true,
score: 0.95,
supervisor: Value.reference(supervisorTermId),
skills: ["TypeScript", "Rust"],
pending_review: null,
},
});
// response is TermResponse, not TermDto
console.log(response.term.id); // Term UUID
console.log(response.state); // "complete" | "residuated" | "no_witnesses"
console.log(response.term.features); // Features with tagged ValueDto values

Term state

The TermState field on TermResponse indicates whether the term satisfies its sort’s type witnesses:

StateMeaning
"complete"All type witnesses are satisfied
"residuated"Some witnesses are suspended, awaiting more information
"no_witnesses"The sort has no witness requirements

Witness proofs

When a term satisfies its type witnesses, TermResponse includes proof objects:

interface WitnessProofDto {
witnessId: string; // ID of the satisfied witness
bindings: Record<string, string>; // Variable bindings that satisfy it
certainty: number; // Confidence (0.0 to 1.0)
}

Example:

const response = await client.terms.createTerm({
sortId: employeeSortId,
ownerId: userId,
features: {
name: "Bob",
salary: 85000,
},
});
if (response.witnessProofs) {
for (const proof of response.witnessProofs) {
console.log(`Witness ${proof.witnessId} satisfied with certainty ${proof.certainty}`);
console.log("Bindings:", proof.bindings);
}
}

Residuation

Residuation is the suspension of computation when information is missing. It is not deferral, lazy evaluation, or an error. It is the system’s way of saying “I cannot determine this yet, but I will revisit it when the missing information arrives.”

When witnesses residuate, TermResponse includes residual witness objects:

interface ResidualWitnessDto {
witnessId: string; // The witness that could not be satisfied
partialBindings: Record<string, string>; // Bindings found so far
trigger: string; // What would trigger re-evaluation
}
const response = await client.terms.getTerm(termId);
if (response.state === "residuated" && response.residualWitnesses) {
for (const residual of response.residualWitnesses) {
console.log(`Witness ${residual.witnessId} is waiting for: ${residual.trigger}`);
console.log("Partial bindings:", residual.partialBindings);
}
}

Updating terms

Only the provided features are updated; omitted features remain unchanged:

const updated = await client.terms.updateTerm(termId, {
features: {
salary: 95000,
department: "Engineering",
},
});

Checking existence and deletion

const exists = await client.terms.termExists(termId);
// returns true or false
await client.terms.deleteTerm(termId);

Bulk creation

Create multiple terms in a single request:

const result = await client.terms.bulkCreateTerms({
terms: [
{
sortId: personSortId,
ownerId: userId,
features: { name: "Alice", age: 30 },
},
{
sortId: personSortId,
ownerId: userId,
features: { name: "Bob", age: 25 },
},
],
});
// result.termIds: string[] — UUIDs in the same order as the request
// result.processingTimeMs: number

Feature value types

Features on TermDto use the tagged ValueDto format. The 10 variants are:

VariantBuilderJSON
String"hello" (plain){"type": "String", "value": "hello"}
Integer42 (plain){"type": "Integer", "value": 42}
Real3.14 (plain){"type": "Real", "value": 3.14}
Booleantrue (plain){"type": "Boolean", "value": true}
Uninstantiatednull (plain){"type": "Uninstantiated"}
ReferenceValue.reference(uuid){"type": "Reference", "value": "uuid"}
List[...] (plain){"type": "List", "value": [...]}
FuzzyScalarValue.fuzzyScalar(0.5, 0.8){"type": "FuzzyScalar", "value": {...}}
FuzzyNumberValue.fuzzyNumber(shape){"type": "FuzzyNumber", "value": {"shape": {...}}}
SetValue.set(lower, upper){"type": "Set", "value": {"lower": [...], ...}}

For a detailed comparison of tagged vs. untagged formats, see Values and Formats.