factbag 0.0.0-alpha.2 → 0.0.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +4 -4
- package/dist/core/{chunk-5J3WGMN2.js → chunk-N2RRSOEN.js} +2 -1
- package/dist/core/{chunk-X3LSDJL4.js → chunk-QOMFY3XB.js} +4 -1
- package/dist/core/dsl/enumeration.d.ts +5 -1
- package/dist/core/dsl/index.d.ts +4 -4
- package/dist/core/dsl/index.js +76 -7
- package/dist/core/dsl/operators/arithmetic.d.ts +69 -2
- package/dist/core/dsl/operators/comparison.d.ts +11 -1
- package/dist/core/dsl/types.d.ts +2 -0
- package/dist/core/exceptions/programmer-errors.d.ts +4 -4
- package/dist/core/explanation/explain.d.ts +5 -1
- package/dist/core/explanation/index.js +45 -5
- package/dist/core/knowledge-base/index.js +2 -2
- package/dist/core/query/index.d.ts +1 -1
- package/dist/core/query/index.js +406 -67
- package/dist/core/solver/calendar.d.ts +4 -0
- package/dist/core/solver/candidate-search.d.ts +3 -1
- package/dist/core/solver/program-index.d.ts +17 -0
- package/dist/core/solver/query-validation/utils.d.ts +2 -0
- package/dist/core/types/atom.d.ts +7 -2
- package/dist/core/types/goal.d.ts +23 -5
- package/dist/core/types/judgment.d.ts +2 -2
- package/dist/core/types/schema.d.ts +2 -2
- package/dist/jev/chunk-QEZ4O3YS.js +203 -0
- package/dist/jev/chunk-ZAAXPTJJ.js +105 -0
- package/dist/jev/classifier/classifiable-predicates.d.ts +12 -0
- package/dist/jev/classifier/classifier.d.ts +16 -0
- package/dist/jev/classifier/extract-evidence.d.ts +17 -0
- package/dist/jev/classifier/filter-predicates.d.ts +9 -0
- package/dist/jev/classifier/filter-symbols.d.ts +14 -0
- package/dist/jev/classifier/index.d.ts +6 -0
- package/dist/jev/classifier/index.js +246 -0
- package/dist/jev/classifier/types.d.ts +13 -0
- package/dist/jev/config.d.ts +17 -0
- package/dist/jev/config.js +18 -0
- package/dist/jev/jev/model.d.ts +11 -0
- package/dist/jev/jev/prompts.d.ts +29 -0
- package/dist/jev/resolve.d.ts +5 -72
- package/dist/jev/resolve.js +24 -47
- package/dist/jev/settings.d.ts +11 -0
- package/dist/jev/types/config.d.ts +18 -0
- package/dist/jev/types/model.d.ts +58 -0
- package/dist/jev/types/run.d.ts +9 -0
- package/dist/jev/utils.d.ts +9 -0
- package/package.json +7 -3
- package/dist/jev/chunk-PISXBJOT.js +0 -206
- package/dist/jev/classifier.d.ts +0 -28
- package/dist/jev/classifier.js +0 -171
- package/dist/jev/client.d.ts +0 -62
- package/dist/jev/jobs.d.ts +0 -61
- package/dist/jev/tools.d.ts +0 -9
- package/dist/jev/types.d.ts +0 -63
- /package/dist/core/{chunk-A6ZZJWIO.js → chunk-NDUIQAH4.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# factbag
|
|
2
2
|
|
|
3
|
+
## 0.0.0-alpha.4
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2814aab: Allow enumerations to hold numbers or dates as well as strings, with one member
|
|
8
|
+
type per enumeration. Members must be unique; dates compare by instant and are
|
|
9
|
+
copied on construction. NaN and invalid dates are rejected. Enumeration membership
|
|
10
|
+
validation at assertion and query time is unchanged.
|
|
11
|
+
- 1f5bb1b: Classify predicates whose fields are number or Date enumerations, alongside
|
|
12
|
+
string enumerations. Dates are sent to the model as ISO strings and retained as
|
|
13
|
+
`Date` values in evidence. Open-ended `String`, `Number`, and `Date` fields
|
|
14
|
+
remain unsupported.
|
|
15
|
+
- 9ca1766: Reorganize classification and resolution to separate evidence workflows from model requests. The workflows own evidence admission and knowledge-base updates; the Jev implementation owns prompts, encoding, batching, and response validation.
|
|
16
|
+
|
|
17
|
+
**Breaking changes:** call the classifier with `classifier({ corpus, input, config })`. Classification and resolution jobs now expose only `execute(): Promise<void>`; read the resulting knowledge base from `run.kb`. Job metadata and assessment and resolution reports are removed. Replace `RequestConfig`, `ResolutionConfig`, and nested `AdmissionPolicy` settings with the shared, flat `Config`. Pass an SDK client directly instead of implementing the removed custom client interfaces.
|
|
18
|
+
|
|
19
|
+
Classification now executes one job per chunk across all permitted predicates. All responses for the chunk must validate before its evidence is written. Failed jobs can be retried without partial evidence, repeating the chunk's requests; successful repeats add fresh evidence.
|
|
20
|
+
|
|
21
|
+
Optionally filter candidates by passage relevance with `config: { routing: true }`. Each chunk first asks which predicates the passage may bear on. For predicates that make claims about supplied entities or values, it can also filter those symbols before combining them into complete candidate facts. `candidateProductCutoff` controls when this extra filtering runs. Filtering defaults to off: false negatives can suppress evidence, including denials, and the extra requests do not guarantee cost savings.
|
|
22
|
+
|
|
23
|
+
Expose `createConfig`, `Config`, and shared `DEFAULT_*` constants through `factbag/jev/config`. Normalize settings once and preserve a shared lazy client when a factory-created config is reused across workflows.
|
|
24
|
+
|
|
25
|
+
Export `fieldEquals` from `factbag/dsl` and use core equality in Jev: entities compare by constructor and ID, dates by instant. Resolution now groups contradictory evidence about separate instances of the same entity.
|
|
26
|
+
|
|
27
|
+
Encode candidate values when building requests, using the original entities and dates instead of construction-time JSON snapshots. Candidate domains remain fixed at run construction.
|
|
28
|
+
|
|
29
|
+
## 0.0.0-alpha.3
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- f8f2712: Add `eq` and `neq` rule-body comparisons for numbers, dates, strings, booleans, and entities. Entities compare by type and id rather than instance; dates compare by instant.
|
|
34
|
+
- 900b104: Add min, max, roundTo, floorTo, and ceilTo numeric arithmetic terms for rule bodies. Rounding supports whole-number decimal places (including negative places), with halves rounded away from zero. Decimal exponent shifting avoids multiplication artifacts at decimal rounding boundaries.
|
|
35
|
+
- 4f040e3: Add `daysBetween(later, earlier)` and `yearsBetween(later, earlier)` as number-valued arithmetic terms over dates. They count signed UTC calendar days and whole anniversary years, ignoring time of day; February 29 anniversaries fall on March 1 in common years. Numeric subtraction on variables now composes with other numeric operators.
|
|
36
|
+
|
|
37
|
+
### Patch Changes
|
|
38
|
+
|
|
39
|
+
- 01cc338: Speed up queries over large rulesets with linear-time recursive predicate grouping,
|
|
40
|
+
per-query rule and evidence indexes, and judgment reuse during candidate discovery.
|
|
41
|
+
Preserve candidate pruning and distinguish same-named predicate declarations when
|
|
42
|
+
tracking active atoms, avoiding incorrect recursion cutoffs.
|
|
43
|
+
- 80d1e75: Share explanation nodes for reused proof judgments so explanation construction no longer grows with the number of paths through a shared proof. Identical sub-explanations are still listed once, and rendered content is unchanged.
|
|
44
|
+
- a074dab: Judge evidence-free, closed-world recursive components together as a least fixed
|
|
45
|
+
point instead of enumerating recursive proof paths. Doubly recursive transitive
|
|
46
|
+
closures and symmetric compositions no longer require exponential proof search.
|
|
47
|
+
Other components retain the existing search semantics. Variable queries recognize
|
|
48
|
+
a non-recursive disjunctive base case within an exclusive definition.
|
|
49
|
+
|
|
3
50
|
## 0.0.0-alpha.2
|
|
4
51
|
|
|
5
52
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ class Person {
|
|
|
79
79
|
|
|
80
80
|
const hasIncomeOver200k = predicate(
|
|
81
81
|
"has_income_over_200k",
|
|
82
|
-
{ person: Person, year: enumeration(
|
|
82
|
+
{ person: Person, year: enumeration(2024, 2025) },
|
|
83
83
|
{
|
|
84
84
|
description:
|
|
85
85
|
"The person's total individual annual income exceeded USD 200,000 in the " +
|
|
@@ -117,8 +117,8 @@ const kb = new KnowledgeBase();
|
|
|
117
117
|
|
|
118
118
|
kb.rule(isQualifiedInvestor({ person })).if(
|
|
119
119
|
and(
|
|
120
|
-
hasIncomeOver200k({ person, year:
|
|
121
|
-
hasIncomeOver200k({ person, year:
|
|
120
|
+
hasIncomeOver200k({ person, year: 2024 }),
|
|
121
|
+
hasIncomeOver200k({ person, year: 2025 }),
|
|
122
122
|
expectsSameIncome({ person }),
|
|
123
123
|
),
|
|
124
124
|
);
|
|
@@ -170,7 +170,7 @@ const incomeEvidence = guard(kb)
|
|
|
170
170
|
### 6. Ask Jev to assess the income evidence
|
|
171
171
|
|
|
172
172
|
```ts
|
|
173
|
-
const run = classifier(corpus, incomeEvidence);
|
|
173
|
+
const run = classifier({ corpus, input: incomeEvidence });
|
|
174
174
|
|
|
175
175
|
for (const job of run) {
|
|
176
176
|
await job.execute(); // Makes a live Jev request.
|
|
@@ -13,7 +13,10 @@ var PrimitiveArithmeticProgrammerError = class extends ProgrammerError {
|
|
|
13
13
|
/** The arithmetic goal being evaluated when the failure occurred. */
|
|
14
14
|
goal;
|
|
15
15
|
constructor(operation, operands, goal) {
|
|
16
|
-
|
|
16
|
+
const rounding = operation === "roundTo" || operation === "floorTo" || operation === "ceilTo";
|
|
17
|
+
super(
|
|
18
|
+
`${operation} expected ${operation === "daysBetween" || operation === "yearsBetween" ? "date" : "numeric"} operands${rounding ? " and a whole number of decimal places" : ""}`
|
|
19
|
+
);
|
|
17
20
|
this.name = "PrimitiveArithmeticProgrammerError";
|
|
18
21
|
this.operation = operation;
|
|
19
22
|
this.operands = operands;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import type { EnumType } from "../types/schema.js";
|
|
2
|
-
/** Declare a nonempty finite
|
|
2
|
+
/** Declare a nonempty finite domain of one member type. Values must be unique. */
|
|
3
3
|
export declare function enumeration<const Value extends string>(first: Value, ...rest: readonly Value[]): EnumType<Value>;
|
|
4
|
+
/** Declare a nonempty finite number domain. NaN and duplicate values are rejected. */
|
|
5
|
+
export declare function enumeration<const Value extends number>(first: Value, ...rest: readonly Value[]): EnumType<Value>;
|
|
6
|
+
/** Declare a nonempty finite date domain. Dates are copied and must have unique, valid instants. */
|
|
7
|
+
export declare function enumeration(first: Date, ...rest: readonly Date[]): EnumType<Date>;
|
package/dist/core/dsl/index.d.ts
CHANGED
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { and, exists, letIn, not, or } from "./formula.js";
|
|
8
8
|
export { count, sum } from "./operators/aggregate.js";
|
|
9
|
-
export type { AggregateBuilder, ComparableTerm, DateTerm, NumericTerm } from "./types.js";
|
|
10
|
-
export { add, div, mul, sub } from "./operators/arithmetic.js";
|
|
11
|
-
export { gt, gte, lte } from "./operators/comparison.js";
|
|
9
|
+
export type { AggregateBuilder, ComparableTerm, DateTerm, EqualityTerm, NumericTerm, } from "./types.js";
|
|
10
|
+
export { add, ceilTo, daysBetween, div, floorTo, max, min, mul, roundTo, sub, yearsBetween, } from "./operators/arithmetic.js";
|
|
11
|
+
export { eq, gt, gte, lte, neq } from "./operators/comparison.js";
|
|
12
12
|
export { closedPredicate, predicate } from "./predicate.js";
|
|
13
13
|
export { enumeration } from "./enumeration.js";
|
|
14
14
|
export type { ClosedWorldPredicate, OpenWorldPredicate, Predicate, PredicateBase, PredicateDeclaration, PredicateMetadata, PredicateWorld, } from "../types/predicate.js";
|
|
15
15
|
export type { Annotations, AnnotationValue } from "../types/annotations.js";
|
|
16
16
|
export { variable } from "./variable.js";
|
|
17
17
|
export type { Formula, Scalar, Term } from "./types.js";
|
|
18
|
-
export { assertAtomLike, assertGroundAtom } from "../types/atom.js";
|
|
18
|
+
export { assertAtomLike, assertGroundAtom, fieldEquals } from "../types/atom.js";
|
|
19
19
|
export type { AnyAtom, Atom, GroundAtom, GroundFields, NonGroundAtom, PatternFields, } from "../types/atom.js";
|
|
20
20
|
export type { Entity, EnumType, InferRuntime, PredicateSchema, RuntimeConstructor, RuntimeType, } from "../types/schema.js";
|
|
21
21
|
export { Var } from "../types/variable.js";
|
package/dist/core/dsl/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
assertAtomLike,
|
|
3
|
-
assertGroundAtom
|
|
4
|
-
|
|
3
|
+
assertGroundAtom,
|
|
4
|
+
fieldEquals
|
|
5
|
+
} from "../chunk-N2RRSOEN.js";
|
|
5
6
|
import {
|
|
6
7
|
Var
|
|
7
8
|
} from "../chunk-UVOCOH2I.js";
|
|
@@ -68,8 +69,53 @@ function div(left, right) {
|
|
|
68
69
|
assertSupportedValue(right);
|
|
69
70
|
return { kind: "div", args: [left, right] };
|
|
70
71
|
}
|
|
72
|
+
function daysBetween(later, earlier) {
|
|
73
|
+
assertSupportedValue(later);
|
|
74
|
+
assertSupportedValue(earlier);
|
|
75
|
+
return { kind: "daysBetween", args: [later, earlier] };
|
|
76
|
+
}
|
|
77
|
+
function yearsBetween(later, earlier) {
|
|
78
|
+
assertSupportedValue(later);
|
|
79
|
+
assertSupportedValue(earlier);
|
|
80
|
+
return { kind: "yearsBetween", args: [later, earlier] };
|
|
81
|
+
}
|
|
82
|
+
function min(left, right) {
|
|
83
|
+
assertSupportedValue(left);
|
|
84
|
+
assertSupportedValue(right);
|
|
85
|
+
return { kind: "min", args: [left, right] };
|
|
86
|
+
}
|
|
87
|
+
function max(left, right) {
|
|
88
|
+
assertSupportedValue(left);
|
|
89
|
+
assertSupportedValue(right);
|
|
90
|
+
return { kind: "max", args: [left, right] };
|
|
91
|
+
}
|
|
92
|
+
function roundTo(value, places) {
|
|
93
|
+
assertSupportedValue(value);
|
|
94
|
+
assertSupportedValue(places);
|
|
95
|
+
return { kind: "roundTo", args: [value, places] };
|
|
96
|
+
}
|
|
97
|
+
function floorTo(value, places) {
|
|
98
|
+
assertSupportedValue(value);
|
|
99
|
+
assertSupportedValue(places);
|
|
100
|
+
return { kind: "floorTo", args: [value, places] };
|
|
101
|
+
}
|
|
102
|
+
function ceilTo(value, places) {
|
|
103
|
+
assertSupportedValue(value);
|
|
104
|
+
assertSupportedValue(places);
|
|
105
|
+
return { kind: "ceilTo", args: [value, places] };
|
|
106
|
+
}
|
|
71
107
|
|
|
72
108
|
// src/dsl/operators/comparison.ts
|
|
109
|
+
function eq(left, right) {
|
|
110
|
+
assertSupportedValue(left);
|
|
111
|
+
assertSupportedValue(right);
|
|
112
|
+
return { kind: "eq", args: [left, right] };
|
|
113
|
+
}
|
|
114
|
+
function neq(left, right) {
|
|
115
|
+
assertSupportedValue(left);
|
|
116
|
+
assertSupportedValue(right);
|
|
117
|
+
return { kind: "neq", args: [left, right] };
|
|
118
|
+
}
|
|
73
119
|
function gt(left, right) {
|
|
74
120
|
assertSupportedValue(left);
|
|
75
121
|
assertSupportedValue(right);
|
|
@@ -121,10 +167,23 @@ function predicate(name, params, metadata) {
|
|
|
121
167
|
|
|
122
168
|
// src/dsl/enumeration.ts
|
|
123
169
|
function enumeration(first, ...rest) {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
170
|
+
const kind = first instanceof Date ? "date" : typeof first;
|
|
171
|
+
const seen = /* @__PURE__ */ new Set();
|
|
172
|
+
const values = [first, ...rest].map((value) => {
|
|
173
|
+
const memberKind = value instanceof Date ? "date" : typeof value;
|
|
174
|
+
if (memberKind !== kind || kind !== "string" && kind !== "number" && kind !== "date") {
|
|
175
|
+
throw new Error("Enum values must all be strings, numbers, or dates of one type");
|
|
176
|
+
}
|
|
177
|
+
const key = value instanceof Date ? value.getTime() : value;
|
|
178
|
+
if (typeof key === "number" && Number.isNaN(key)) {
|
|
179
|
+
throw new Error("Enum values must not be NaN or invalid dates");
|
|
180
|
+
}
|
|
181
|
+
if (seen.has(key)) {
|
|
182
|
+
throw new Error("Enum values must be unique (dates compare by instant)");
|
|
183
|
+
}
|
|
184
|
+
seen.add(key);
|
|
185
|
+
return value instanceof Date ? new Date(key) : value;
|
|
186
|
+
});
|
|
128
187
|
return Object.freeze({ kind: "enum", values: Object.freeze(values) });
|
|
129
188
|
}
|
|
130
189
|
|
|
@@ -138,20 +197,30 @@ export {
|
|
|
138
197
|
and,
|
|
139
198
|
assertAtomLike,
|
|
140
199
|
assertGroundAtom,
|
|
200
|
+
ceilTo,
|
|
141
201
|
closedPredicate,
|
|
142
202
|
count,
|
|
203
|
+
daysBetween,
|
|
143
204
|
div,
|
|
144
205
|
enumeration,
|
|
206
|
+
eq,
|
|
145
207
|
exists,
|
|
208
|
+
fieldEquals,
|
|
209
|
+
floorTo,
|
|
146
210
|
gt,
|
|
147
211
|
gte,
|
|
148
212
|
letIn,
|
|
149
213
|
lte,
|
|
214
|
+
max,
|
|
215
|
+
min,
|
|
150
216
|
mul,
|
|
217
|
+
neq,
|
|
151
218
|
not,
|
|
152
219
|
or,
|
|
153
220
|
predicate,
|
|
221
|
+
roundTo,
|
|
154
222
|
sub,
|
|
155
223
|
sum,
|
|
156
|
-
variable
|
|
224
|
+
variable,
|
|
225
|
+
yearsBetween
|
|
157
226
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { DateTerm, Formula, NumericTerm } from "../types.js";
|
|
2
2
|
/** Add two numeric terms, producing a numeric rule-body expression. */
|
|
3
3
|
export declare function add(left: NumericTerm, right: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
|
|
4
|
+
/** Subtract one numeric term from another. */
|
|
5
|
+
export declare function sub(left: NumericTerm, right: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
|
|
4
6
|
/**
|
|
5
7
|
* Subtract a numeric duration in milliseconds from a date term, producing a
|
|
6
8
|
* date expression.
|
|
@@ -19,9 +21,74 @@ export declare function add(left: NumericTerm, right: NumericTerm): Formula<numb
|
|
|
19
21
|
* ```
|
|
20
22
|
*/
|
|
21
23
|
export declare function sub(left: DateTerm, right: NumericTerm): Formula<Date, readonly [DateTerm, NumericTerm]>;
|
|
22
|
-
/** Subtract one numeric term from another. */
|
|
23
|
-
export declare function sub(left: NumericTerm, right: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
|
|
24
24
|
/** Multiply two numeric terms, producing a numeric rule-body expression. */
|
|
25
25
|
export declare function mul(left: NumericTerm, right: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
|
|
26
26
|
/** Divide one numeric term by another, producing a numeric rule-body expression. */
|
|
27
27
|
export declare function div(left: NumericTerm, right: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
|
|
28
|
+
/**
|
|
29
|
+
* Count signed UTC calendar days from `earlier` to `later`, ignoring time of day.
|
|
30
|
+
* Add one to count both the first and last day of a stay.
|
|
31
|
+
*/
|
|
32
|
+
export declare function daysBetween(later: DateTerm, earlier: DateTerm): Formula<number, readonly [DateTerm, DateTerm]>;
|
|
33
|
+
/**
|
|
34
|
+
* Count signed whole years from `earlier` to `later` by UTC calendar anniversaries,
|
|
35
|
+
* ignoring time of day. February 29 anniversaries fall on March 1 in common years.
|
|
36
|
+
* A person's age on a day is `yearsBetween(day, birthday)`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function yearsBetween(later: DateTerm, earlier: DateTerm): Formula<number, readonly [DateTerm, DateTerm]>;
|
|
39
|
+
/**
|
|
40
|
+
* The lesser of two numeric terms.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* // Deduction reduction cannot exceed 80% of the deductions.
|
|
45
|
+
* letIn(R, min(mul(0.03, Excess), mul(0.8, Deductions)), gte(R, 0))
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function min(left: NumericTerm, right: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
|
|
49
|
+
/**
|
|
50
|
+
* The greater of two numeric terms.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* // The remaining allowance cannot be negative.
|
|
55
|
+
* letIn(A, max(sub(2000, Reduction), 0), gte(A, 0))
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function max(left: NumericTerm, right: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
|
|
59
|
+
/**
|
|
60
|
+
* Round to a whole number of decimal places, with halves away from zero.
|
|
61
|
+
* Negative places round to tens, hundreds, and so on. Decimal exponent shifting
|
|
62
|
+
* avoids multiplication artifacts such as `1.005 * 100`, so `roundTo(1.005, 2)`
|
|
63
|
+
* yields `1.01`. Inputs and results still have JavaScript number precision;
|
|
64
|
+
* this does not recover precision already lost in an earlier expression.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* // Tax is 15% of taxable income, in whole dollars.
|
|
69
|
+
* letIn(Tax, roundTo(mul(Income, 0.15), 0), gte(Tax, 0))
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare function roundTo(value: NumericTerm, places: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
|
|
73
|
+
/**
|
|
74
|
+
* Round toward negative infinity at a whole number of decimal places.
|
|
75
|
+
* Negative places and decimal exponent shifting work as in {@link roundTo}.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* // Only complete thousands of eligible expenses count.
|
|
80
|
+
* letIn(Eligible, floorTo(Expenses, -3), gte(Eligible, 0))
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function floorTo(value: NumericTerm, places: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
|
|
84
|
+
/**
|
|
85
|
+
* Round toward positive infinity at a whole number of decimal places.
|
|
86
|
+
* Negative places and decimal exponent shifting work as in {@link roundTo}.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* // Two percentage points for each $2,500, or fraction thereof, of the excess.
|
|
91
|
+
* letIn(Points, mul(2, ceilTo(div(Excess, 2500), 0)), lte(Points, 100))
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare function ceilTo(value: NumericTerm, places: NumericTerm): Formula<number, readonly [NumericTerm, NumericTerm]>;
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import type { ComparableTerm, Formula } from "../types.js";
|
|
1
|
+
import type { ComparableTerm, EqualityTerm, Formula } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Require equal values: numbers, strings, and booleans by value, dates by instant,
|
|
4
|
+
* and entities by type and id rather than instance.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Comparisons gate whether a rule fires but contribute no proof children.
|
|
8
|
+
*/
|
|
9
|
+
export declare function eq(left: EqualityTerm, right: EqualityTerm): Formula<boolean, readonly [EqualityTerm, EqualityTerm]>;
|
|
10
|
+
/** Require the two terms to differ, in the sense of {@link eq}. */
|
|
11
|
+
export declare function neq(left: EqualityTerm, right: EqualityTerm): Formula<boolean, readonly [EqualityTerm, EqualityTerm]>;
|
|
2
12
|
/**
|
|
3
13
|
* Require the left term to be strictly greater than the right.
|
|
4
14
|
*
|
package/dist/core/dsl/types.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ export type NumericTerm = number | Var | Formula<number, readonly unknown[]>;
|
|
|
44
44
|
export type DateTerm = Date | Var | Formula<Date, readonly unknown[]>;
|
|
45
45
|
/** Term accepted by comparison helpers. */
|
|
46
46
|
export type ComparableTerm = NumericTerm | DateTerm;
|
|
47
|
+
/** Term accepted by equality: a comparable term, a string, a boolean, or an entity. */
|
|
48
|
+
export type EqualityTerm = ComparableTerm | string | boolean | Entity;
|
|
47
49
|
/** Aggregate builder returned by sum(...) and count(...). */
|
|
48
50
|
export type AggregateBuilder<Value extends number = number> = Readonly<{
|
|
49
51
|
where: (<const Body>(body: Body & RuleBodyInput<Body>) => Formula<Value, readonly [Body]>) & (<const Body, const CompleteWhen>(body: Body & RuleBodyInput<Body>, options: Readonly<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ArithmeticGoal } from "../types/goal.js";
|
|
1
|
+
import type { ArithmeticGoal, ArithmeticKind } from "../types/goal.js";
|
|
2
2
|
import type { PredicateDeclaration } from "../types/predicate.js";
|
|
3
3
|
import type { AnyRule } from "../types/rule.js";
|
|
4
4
|
import type { Var } from "../types/variable.js";
|
|
@@ -9,12 +9,12 @@ export declare class ProgrammerError extends Error {
|
|
|
9
9
|
/** Arithmetic primitive with operands the solver cannot legally evaluate. */
|
|
10
10
|
export declare class PrimitiveArithmeticProgrammerError extends ProgrammerError {
|
|
11
11
|
/** The arithmetic operation that failed. */
|
|
12
|
-
readonly operation:
|
|
12
|
+
readonly operation: ArithmeticKind;
|
|
13
13
|
/** The evaluated left and right operand values. */
|
|
14
14
|
readonly operands: readonly [unknown, unknown];
|
|
15
15
|
/** The arithmetic goal being evaluated when the failure occurred. */
|
|
16
|
-
readonly goal: ArithmeticGoal<
|
|
17
|
-
constructor(operation:
|
|
16
|
+
readonly goal: ArithmeticGoal<ArithmeticKind>;
|
|
17
|
+
constructor(operation: ArithmeticKind, operands: readonly [unknown, unknown], goal: ArithmeticGoal<ArithmeticKind>);
|
|
18
18
|
}
|
|
19
19
|
/** Rule body uses variables before any constructive source can bind them. */
|
|
20
20
|
export declare class UnsafeUnboundVariableProgrammerError extends ProgrammerError {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { Judgment } from "../types/judgment.js";
|
|
2
2
|
import type { Explanation } from "./types.js";
|
|
3
3
|
/**
|
|
4
|
-
* Convert a judgment's proof-support
|
|
4
|
+
* Convert a judgment's proof-support graph into a compact, LLM-readable
|
|
5
5
|
* {@link Explanation} of what was concluded and why.
|
|
6
6
|
*
|
|
7
|
+
* The result is a directed acyclic graph (DAG): reused judgment objects share
|
|
8
|
+
* explanation nodes within this call. Serializing the result as JSON still
|
|
9
|
+
* expands shared nodes at every path to them.
|
|
10
|
+
*
|
|
7
11
|
* @example
|
|
8
12
|
* ```ts
|
|
9
13
|
* const explanation = explainJudgment(query(kb, doctor({ person: alice })));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isFormula
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-NDUIQAH4.js";
|
|
4
4
|
import {
|
|
5
5
|
AssertionError,
|
|
6
6
|
isVar
|
|
@@ -8,6 +8,21 @@ import {
|
|
|
8
8
|
|
|
9
9
|
// src/explanation/explain.ts
|
|
10
10
|
function explainJudgment(judgment) {
|
|
11
|
+
return explainWithContext(judgment, {
|
|
12
|
+
explanations: /* @__PURE__ */ new WeakMap(),
|
|
13
|
+
contentKeys: /* @__PURE__ */ new WeakMap(),
|
|
14
|
+
keyIds: /* @__PURE__ */ new Map()
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function explainWithContext(judgment, context) {
|
|
18
|
+
let explanation = context.explanations.get(judgment);
|
|
19
|
+
if (explanation === void 0) {
|
|
20
|
+
explanation = explainOnce(judgment, context);
|
|
21
|
+
context.explanations.set(judgment, explanation);
|
|
22
|
+
}
|
|
23
|
+
return explanation;
|
|
24
|
+
}
|
|
25
|
+
function explainOnce(judgment, context) {
|
|
11
26
|
if (isClosedWorldAbsence(judgment)) {
|
|
12
27
|
const atom = judgment.goal.goal.atom;
|
|
13
28
|
return {
|
|
@@ -21,7 +36,7 @@ function explainJudgment(judgment) {
|
|
|
21
36
|
return {
|
|
22
37
|
claim: claimForJudgment(judgment),
|
|
23
38
|
because: formatRuleApplication(ruleSupport.rule, ruleSupport.body.bindings),
|
|
24
|
-
given: [
|
|
39
|
+
given: [explainWithContext(ruleSupport.body, context)]
|
|
25
40
|
};
|
|
26
41
|
}
|
|
27
42
|
const evidence = supports.flatMap(
|
|
@@ -41,7 +56,10 @@ function explainJudgment(judgment) {
|
|
|
41
56
|
if (subgoals.length > 0) {
|
|
42
57
|
return {
|
|
43
58
|
claim: claimForJudgment(judgment),
|
|
44
|
-
given: uniqueExplanations(
|
|
59
|
+
given: uniqueExplanations(
|
|
60
|
+
subgoals.map((subgoal) => explainWithContext(subgoal, context)),
|
|
61
|
+
context
|
|
62
|
+
)
|
|
45
63
|
};
|
|
46
64
|
}
|
|
47
65
|
const closedWorldAbsence = supports.find((support) => support.source === "closed-world-absence");
|
|
@@ -56,11 +74,11 @@ function explainJudgment(judgment) {
|
|
|
56
74
|
because: "no supporting evidence or rule was found"
|
|
57
75
|
};
|
|
58
76
|
}
|
|
59
|
-
function uniqueExplanations(explanations) {
|
|
77
|
+
function uniqueExplanations(explanations, context) {
|
|
60
78
|
const seen = /* @__PURE__ */ new Set();
|
|
61
79
|
const unique = [];
|
|
62
80
|
for (const explanation of explanations) {
|
|
63
|
-
const key =
|
|
81
|
+
const key = contentKey(explanation, context);
|
|
64
82
|
if (!seen.has(key)) {
|
|
65
83
|
seen.add(key);
|
|
66
84
|
unique.push(explanation);
|
|
@@ -68,6 +86,21 @@ function uniqueExplanations(explanations) {
|
|
|
68
86
|
}
|
|
69
87
|
return unique;
|
|
70
88
|
}
|
|
89
|
+
function contentKey(explanation, context) {
|
|
90
|
+
let id = context.contentKeys.get(explanation);
|
|
91
|
+
if (id === void 0) {
|
|
92
|
+
const given = "given" in explanation ? explanation.given.map((child) => contentKey(child, context)) : void 0;
|
|
93
|
+
const because = "because" in explanation ? explanation.because : void 0;
|
|
94
|
+
const key = JSON.stringify([explanation.claim, because, given]);
|
|
95
|
+
id = context.keyIds.get(key);
|
|
96
|
+
if (id === void 0) {
|
|
97
|
+
id = context.keyIds.size;
|
|
98
|
+
context.keyIds.set(key, id);
|
|
99
|
+
}
|
|
100
|
+
context.contentKeys.set(explanation, id);
|
|
101
|
+
}
|
|
102
|
+
return id;
|
|
103
|
+
}
|
|
71
104
|
function supportsForValue(judgment) {
|
|
72
105
|
if (judgment.value === "true") {
|
|
73
106
|
return judgment.positive;
|
|
@@ -144,6 +177,13 @@ function formatGoal(goal) {
|
|
|
144
177
|
case "sub":
|
|
145
178
|
case "mul":
|
|
146
179
|
case "div":
|
|
180
|
+
case "daysBetween":
|
|
181
|
+
case "yearsBetween":
|
|
182
|
+
case "min":
|
|
183
|
+
case "max":
|
|
184
|
+
case "roundTo":
|
|
185
|
+
case "floorTo":
|
|
186
|
+
case "ceilTo":
|
|
147
187
|
return `${goal.bind.name} = ${goal.kind}(${formatValue(goal.left)}, ${formatValue(goal.right)})`;
|
|
148
188
|
case "eq":
|
|
149
189
|
case "neq":
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
PrimitiveArithmeticProgrammerError,
|
|
4
4
|
ProgrammerError,
|
|
5
5
|
UnsafeUnboundVariableProgrammerError
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-QOMFY3XB.js";
|
|
7
7
|
import {
|
|
8
8
|
isAtomLike
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-N2RRSOEN.js";
|
|
10
10
|
import "../chunk-UVOCOH2I.js";
|
|
11
11
|
|
|
12
12
|
// src/knowledge-base/assertions/exclusive-definitions.ts
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { query, queryAll } from "./query.js";
|
|
8
8
|
export type { Binding, Bindings, GoalValue } from "../types/common.js";
|
|
9
|
-
export type { AddGoal, AndGoal, AnyAtomGoal, ArithmeticGoal, AtomGoal, ComparisonGoal, CompoundGoal, CountGoal, DivGoal, EqGoal, ExistsGoal, Goal, GoalExpression, GroundAtomGoal, GtGoal, GteGoal, LetGoal, LtGoal, LteGoal, MulGoal, NeqGoal, NotGoal, OrGoal, PrimitiveGoal, SubGoal, SumGoal, } from "../types/goal.js";
|
|
9
|
+
export type { AddGoal, AndGoal, AnyAtomGoal, ArithmeticGoal, ArithmeticKind, AtomGoal, CeilToGoal, ComparisonGoal, CompoundGoal, CountGoal, DaysBetweenGoal, DivGoal, EqGoal, ExistsGoal, FloorToGoal, Goal, GoalExpression, GroundAtomGoal, GtGoal, GteGoal, LetGoal, LtGoal, LteGoal, MaxGoal, MinGoal, MulGoal, NeqGoal, NotGoal, OrGoal, PrimitiveGoal, RoundToGoal, SubGoal, SumGoal, YearsBetweenGoal, } from "../types/goal.js";
|
|
10
10
|
export type { ArithmeticPrimitiveSupport, ClosedWorldAbsenceSupport, ComparisonPrimitiveSupport, CountPrimitiveSupport, ErasedEvidenceSupport, ErasedRuleSupport, EvidenceSupport, Judgment, JudgmentBase, JudgmentSupportFor, MissingBinding, NumericRange, PrimitiveEvaluation, PrimitiveSupport, PrimitiveSupportFor, PrimitiveUncertainty, QueryAllJudgment, QueryJudgment, RuleSupport, SubgoalsSupport, SumPrimitiveSupport, } from "../types/judgment.js";
|