Function: aggregate()
@kortexya/reasoninglayer / aggregate
Function: aggregate()
Section titled “Function: aggregate()”aggregate(
spec):RuleAggregatorDto
Defined in: src/builders/psi.ts:291
Declare engine-side aggregation on a rule head.
Parameters
Section titled “Parameters”The group key, the operator and the feature to aggregate.
groupBy
Section titled “groupBy”string[]
Feature names that define the aggregation group. Proofs agreeing on all of them are merged into one conclusion.
One of the five operators in RuleAggregatorOp.
target
Section titled “target”string
The feature whose value is aggregated inside each group.
Returns
Section titled “Returns”The descriptor to pass as addRule’s aggregator.
Throws
Section titled “Throws”ValidationError when groupBy is empty, when a groupBy
entry or target is blank, or when target also appears in groupBy —
a feature cannot be both the group key and the aggregated value.
Remarks
Section titled “Remarks”Serialization format: snake_case wire keys. The descriptor rides on the
REQUEST, not on the head: POST /api/v1/inference/rules takes it as a
top-level aggregator and the listing lifts it back onto the rule entry as
{"group_by":["customer_id"],"op":"sum","target":"amount"}, never as a
feature of the head. Measured on 2026-09-18 — the returned head’s features
map holds only customer_id, amount and when.
An aggregating rule has NO OSFQL spelling: the same listing omits osfql
entirely for it, while the negation rule beside it carried one. A UI must
disable “edit as text” for such a rule rather than guess.
Without the descriptor the rule derives one conclusion PER PROOF; with it,
one per group. Measured: three order facts (cust-a 10.0, cust-a 32.5,
cust-b 7.25) under
DERIVE total_spend(customer_id: ?C, amount: ?A) WHEN order(customer_id: ?C, amount: ?A)
with aggregate({ groupBy: ['customer_id'], op: 'sum', target: 'amount' })
produced exactly two rows — cust-a 42.5 and cust-b 7.25.
Example
Section titled “Example”import { aggregate, psi } from '@kortexya/reasoninglayer';
await client.inference.addRule({ term: psi('total_spend', { customer_id: '?C', amount: '?A' }), antecedents: [psi('order', { customer_id: '?C', amount: '?A' })], aggregator: aggregate({ groupBy: ['customer_id'], op: 'sum', target: 'amount' }),});