factbag 0.0.0-stage → 0.1.0-alpha.7
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 +108 -0
- package/LICENSE +202 -0
- package/README.md +308 -2
- package/dist/corpus.d.ts +112 -0
- package/dist/corpus.js +266 -0
- package/dist/dsl.d.ts +309 -0
- package/dist/dsl.js +476 -0
- package/dist/exceptions.d.ts +44 -0
- package/dist/exceptions.js +1 -0
- package/dist/explain.d.ts +225 -0
- package/dist/explain.js +415 -0
- package/dist/goal-BBoSfPUN.d.ts +333 -0
- package/dist/judgment-v8i3ks5N.d.ts +140 -0
- package/dist/kb.d.ts +71 -0
- package/dist/kb.js +298 -0
- package/dist/knowledge-base-CkU_xzmB.d.ts +76 -0
- package/dist/programmer-errors-DLE1xWJ_.js +77 -0
- package/dist/query-CN6EOPwp.js +2062 -0
- package/dist/rolldown-runtime-D8jQpDG7.js +12 -0
- package/dist/types-DeYyybXd.js +46 -0
- package/package.json +92 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# factbag
|
|
2
|
+
|
|
3
|
+
## 0.1.0-alpha.7
|
|
4
|
+
|
|
5
|
+
No changes in this release.
|
|
6
|
+
|
|
7
|
+
## 0.0.0-alpha.6
|
|
8
|
+
|
|
9
|
+
### Minor Changes
|
|
10
|
+
|
|
11
|
+
- 7225a51: Add `firstOfMonth(year, month)`, a date-valued arithmetic term for the first day of a month at UTC midnight, and `yearOf(date)`, a number-valued term for a date's UTC calendar year. Rules can now derive a year's bounds, such as `firstOfMonth(Y, 1)` and `sub(firstOfMonth(add(Y, 1), 1), 86_400_000)`, instead of looking them up in asserted calendar facts. A month outside 1 to 12, or a year that is not a whole number, throws `PrimitiveArithmeticProgrammerError` rather than rolling over. The query API adds `FirstOfMonthGoal` and the `"firstOfMonth"` arithmetic kind.
|
|
12
|
+
- 5114b2f: Show the `citation` and `justification` recorded with a piece of evidence in its explanation, beside its `source`. An evidence reason no longer repeats the claim of the step it supports; it names its own claim only when that differs, as in a conflict, or when the evidence has no provenance. `EvidenceReason.quote`, which was never filled, is replaced by `citation`.
|
|
13
|
+
- eab3679: Explain a rule application with the rule alone, `eligible(P) iff not(active_sanction(P))`, without the trailing `, with P = alice`. The values a rule was applied to are already written into the claim it supports and the claims it rests on.
|
|
14
|
+
- e4b343e: Add a `budget` option to `explainJudgment`: the most claims to explain. The conclusion is always explained, then the claims that decided it before those that were evaluated and changed nothing, and those near the conclusion before those far from it. A claim that rests on claims left out names them, with their values, under `notShown`. Without a budget the whole proof is explained, as before; written as JSON a deep proof repeats each shared claim on every path to it, so give a budget when explaining a judgment in text.
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 7f26b45: Write explanation claims with the values their variables hold: `lte(1000, 36900) = true` instead of `lte(I, 36900) = true`, and `spouse(alice, S) = false` instead of `spouse(P, S) = false`. A variable with no value keeps its name. A term that computes a value states it, such as `H = max(1000, 500) = 1000`, instead of ending in `= true`.
|
|
19
|
+
- b7c85ff: Explain a comparison, arithmetic term or aggregate as `computed` instead of `no supporting evidence or rule was found`. When one could not be evaluated because a variable has no value, the explanation names the variable, such as `X has no value`.
|
|
20
|
+
- ed6ec84: Write a date at UTC midnight as a day in explanations, `2015-12-31` instead of `2015-12-31T00:00:00.000Z`. A date with a time of day keeps its full timestamp.
|
|
21
|
+
- 15391f2: Rest a rule application directly on the subgoals of its body in explanations. When a rule's body is an `and` or an `or`, the explanation no longer has a node that restates the body between the rule and its subgoals: the rule's `given` lists the subgoals themselves. A compound nested inside a body is still its own node, and so is a body that is a single atom or a negation.
|
|
22
|
+
- ecbd8d9: Stop naming the solver's temporaries, such as `$tmp0`, in explanations. A nested arithmetic term is written as the term and its value, `mul(1000, 0.15) = 150`; a term that could not be computed stands in for its result wherever that is used, `gte(add(X, 1), 18) = unknown`; and a rule's `with ...` list gives only the rule's own variables.
|
|
23
|
+
|
|
24
|
+
## 0.0.0-alpha.5
|
|
25
|
+
|
|
26
|
+
### Minor Changes
|
|
27
|
+
|
|
28
|
+
- 19ac508: **Breaking:** errors have their own entry point, `factbag/exceptions`. `ProgrammerError`, `ExclusiveDefinitionProgrammerError`, `PrimitiveArithmeticProgrammerError`, and `UnsafeUnboundVariableProgrammerError` move there from `factbag/kb`. `UnconstructiveRecursionProgrammerError`, which the solver raised but no entry point exported, is now exported too. `UnsupportedPredicateError` moves there from `factbag/jev/errors` and now extends `ProgrammerError`.
|
|
29
|
+
- 19ac508: **Breaking:** Jev moves from `factbag/jev` to its own package, `@factbag/jev`. Install it alongside `factbag` and `@typesafe-ai/sdk`, which is now a required peer dependency at `^0.7.0`, and import `classifier`, `resolver`, `createConfig`, `Config`, and the `DEFAULT_*` constants from `@factbag/jev`. `factbag` no longer has a Jev entry point or an SDK peer dependency. `@factbag/jev` and `@factbag/clef` are released together with `factbag` and require the same `factbag` version.
|
|
30
|
+
|
|
31
|
+
**Breaking:** `@factbag/jev` has a single entry point. `factbag/jev/classifier`, `factbag/jev/resolve`, `factbag/jev/config`, and `factbag/jev/errors` have no replacement subpaths.
|
|
32
|
+
|
|
33
|
+
Add `batchSize` to Jev's `Config`: how many questions each request holds. It defaults to 64 and has no upper limit. An invalid `batchSize` throws when the config is created or a run is built.
|
|
34
|
+
|
|
35
|
+
Jev's `createConfig` now returns `Normalized & Config`, so `client` is no longer guaranteed on the result.
|
|
36
|
+
|
|
37
|
+
- dd3730b: **Breaking:** `factbag/guards` and `factbag/query` are removed. Import `guard`, `GuardedKB`, and the other guard exports from `factbag/kb`, and `query`, `queryAll`, `Judgment`, and the goal and support types from `factbag/dsl`.
|
|
38
|
+
|
|
39
|
+
## 0.0.0-alpha.4
|
|
40
|
+
|
|
41
|
+
### Minor Changes
|
|
42
|
+
|
|
43
|
+
- 2814aab: Allow enumerations to hold numbers or dates as well as strings, with one member
|
|
44
|
+
type per enumeration. Members must be unique; dates compare by instant and are
|
|
45
|
+
copied on construction. NaN and invalid dates are rejected. Enumeration membership
|
|
46
|
+
validation at assertion and query time is unchanged.
|
|
47
|
+
- 1f5bb1b: Classify predicates whose fields are number or Date enumerations, alongside
|
|
48
|
+
string enumerations. Dates are sent to the model as ISO strings and retained as
|
|
49
|
+
`Date` values in evidence. Open-ended `String`, `Number`, and `Date` fields
|
|
50
|
+
remain unsupported.
|
|
51
|
+
- 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.
|
|
52
|
+
|
|
53
|
+
**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.
|
|
54
|
+
|
|
55
|
+
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.
|
|
56
|
+
|
|
57
|
+
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.
|
|
58
|
+
|
|
59
|
+
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.
|
|
60
|
+
|
|
61
|
+
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.
|
|
62
|
+
|
|
63
|
+
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.
|
|
64
|
+
|
|
65
|
+
## 0.0.0-alpha.3
|
|
66
|
+
|
|
67
|
+
### Minor Changes
|
|
68
|
+
|
|
69
|
+
- 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.
|
|
70
|
+
- 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.
|
|
71
|
+
- 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.
|
|
72
|
+
|
|
73
|
+
### Patch Changes
|
|
74
|
+
|
|
75
|
+
- 01cc338: Speed up queries over large rulesets with linear-time recursive predicate grouping,
|
|
76
|
+
per-query rule and evidence indexes, and judgment reuse during candidate discovery.
|
|
77
|
+
Preserve candidate pruning and distinguish same-named predicate declarations when
|
|
78
|
+
tracking active atoms, avoiding incorrect recursion cutoffs.
|
|
79
|
+
- 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.
|
|
80
|
+
- a074dab: Judge evidence-free, closed-world recursive components together as a least fixed
|
|
81
|
+
point instead of enumerating recursive proof paths. Doubly recursive transitive
|
|
82
|
+
closures and symmetric compositions no longer require exponential proof search.
|
|
83
|
+
Other components retain the existing search semantics. Variable queries recognize
|
|
84
|
+
a non-recursive disjunctive base case within an exclusive definition.
|
|
85
|
+
|
|
86
|
+
## 0.0.0-alpha.2
|
|
87
|
+
|
|
88
|
+
### Major Changes
|
|
89
|
+
|
|
90
|
+
- e212a94: Remove bigint support from predicate schemas, numeric expressions, aggregates, and numeric proof ranges. Numeric values now use JavaScript number exclusively, and an empty sum returns 0. The DSL rejects BigInt schemas and bigint operands rather than coercing them.
|
|
91
|
+
|
|
92
|
+
Migrate BigInt fields to Number and bigint literals to numbers. Division now uses number semantics (fractional results rather than bigint truncation); values outside the safe integer range can lose precision. Date support is unchanged.
|
|
93
|
+
|
|
94
|
+
### Patch Changes
|
|
95
|
+
|
|
96
|
+
- bc114ed: Add npm alpha version, CI status, and license badges to the README.
|
|
97
|
+
- a3583ab: Publish the full project README on npm, including the document-to-conclusion walkthrough.
|
|
98
|
+
|
|
99
|
+
## 0.0.0-alpha.1
|
|
100
|
+
|
|
101
|
+
### Patch Changes
|
|
102
|
+
|
|
103
|
+
- ebc4a8a: A `letIn` or aggregate whose variable is already bound now constrains that variable instead of rebinding it. Before, a rule such as `tax(P, R) iff letIn(R, ...)` concluded `tax(alice, 99)` as true when its body computed 98, and an `or` branch evaluated under another branch's bindings reported those bindings as true. Proofs for such queries now carry a temporary variable and an `eq` comparison.
|
|
104
|
+
- eca65c2: An `or` is now judged per binding environment, so a branch that fails without binding a variable can no longer borrow the truth of a sibling branch whose bindings were discarded. Before, `and(or(p(X), r()), s(X))` could be judged true when `p(x1)` and `s(x3)` held but nothing satisfied both: the false `r()` branch produced an environment without `X`, the `or` was judged true there via `p(x1)`, and `s(X)` then ranged over every candidate. The recursion validator's edge collector also now carries variables bound in every `or` branch forward, matching the variable-use checker, so a comparison after such an `or` is no longer reported as unconstructive recursion.
|
|
105
|
+
|
|
106
|
+
## 0.0.0-alpha.0
|
|
107
|
+
|
|
108
|
+
Initial experimental distribution of core, corpus, guards, and Jev functionality.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright 2026 Daniel du Plessis
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,309 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Factbag
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/factbag)
|
|
4
|
+
[](https://github.com/factbag/factbag/actions/workflows/ci.yml?query=branch%3Amain)
|
|
5
|
+
[](https://github.com/factbag/factbag/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
<img src="https://raw.githubusercontent.com/factbag/factbag/main/docs/assets/factbag.png" alt="An open canvas bag" align="right" width="180" />
|
|
8
|
+
|
|
9
|
+
Turn documents into evidence and conclusions with [Jev](https://typesafe.ai/) and TypeScript.
|
|
10
|
+
|
|
11
|
+
Factbag uses TypeSafe AI’s Jev model to read passages and assess specific claims.
|
|
12
|
+
Those assessments become evidence in a knowledge base, alongside facts from your
|
|
13
|
+
application. You write the rules that connect them and query what follows.
|
|
14
|
+
|
|
15
|
+
Each piece of documentary evidence points back to its source passage. Conclusions
|
|
16
|
+
can be **true**, **false**, **conflicted**, or **unknown**. When documents disagree,
|
|
17
|
+
you can inspect the conflicting evidence, correct a particular fact, and query
|
|
18
|
+
the rules again.
|
|
19
|
+
|
|
20
|
+
> **Important:** Factbag is an early alpha release and still pretty rough - APIs will definitely change.
|
|
21
|
+
|
|
22
|
+
## How it works
|
|
23
|
+
|
|
24
|
+
1. **Define the claims** you want Jev to assess, with descriptions of what counts
|
|
25
|
+
as evidence.
|
|
26
|
+
2. **Add documents** to a corpus and choose which predicates Jev may assert or deny.
|
|
27
|
+
3. **Execute classification jobs.** Jev assesses whether each passage supports,
|
|
28
|
+
contradicts, or is irrelevant to each candidate claim.
|
|
29
|
+
4. **Query the result.** Factbag combines admitted evidence with your existing
|
|
30
|
+
facts and rules, retaining the sources behind the conclusion.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
npm install factbag@alpha @factbag/jev@alpha @typesafe-ai/sdk@^0.7.0
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`@factbag/jev` is released with `factbag` and needs the same version. To use
|
|
39
|
+
Cloudflare's Clef models on Workers AI instead, install `@factbag/clef@alpha`, which
|
|
40
|
+
does not need the TypeSafe SDK. To let an OpenAI-compatible model read and write a
|
|
41
|
+
knowledge base through tool calls, install `@factbag/openai@alpha`.
|
|
42
|
+
|
|
43
|
+
ESM only. Requires Node 22.18+ on the 22.x line, or Node 24 or 26.
|
|
44
|
+
|
|
45
|
+
Set your TypeSafe API key in the environment:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
export TYPESAFE_API_KEY="your-api-key"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## From a document to a conclusion
|
|
52
|
+
|
|
53
|
+
### 1. Decide on your ruleset
|
|
54
|
+
|
|
55
|
+
Start with the criteria your application needs to apply. These might be your own
|
|
56
|
+
business rules or requirements set out in legislation or regulations.
|
|
57
|
+
|
|
58
|
+
Here, we're translating the individual-income route in the SEC's
|
|
59
|
+
[accredited-investor definition, 17 CFR § 230.501(a)(6)](<https://www.ecfr.gov/current/title-17/chapter-II/part-230/section-230.501#p-230.501(a)(6)>),
|
|
60
|
+
into three conditions:
|
|
61
|
+
|
|
62
|
+
- Individual income exceeded $200,000 in the first of the two most recent years.
|
|
63
|
+
- Individual income exceeded $200,000 in the second year.
|
|
64
|
+
- The person reasonably expects to reach the same income level in the current year.
|
|
65
|
+
|
|
66
|
+
### 2. Define the claims
|
|
67
|
+
|
|
68
|
+
Declare the facts we'll combine, and describe the claims Jev will assess.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { enumeration, predicate } from "factbag/dsl";
|
|
72
|
+
|
|
73
|
+
class Person {
|
|
74
|
+
constructor(
|
|
75
|
+
readonly id: string,
|
|
76
|
+
readonly name: string,
|
|
77
|
+
) {}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const hasIncomeOver200k = predicate(
|
|
81
|
+
"has_income_over_200k",
|
|
82
|
+
{ person: Person, year: enumeration(2024, 2025) },
|
|
83
|
+
{
|
|
84
|
+
description:
|
|
85
|
+
"The person's total individual annual income exceeded USD 200,000 in the " +
|
|
86
|
+
"specified year. Joint household income and forecasts do not count.",
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const expectsSameIncome = predicate(
|
|
91
|
+
"expects_same_income",
|
|
92
|
+
{ person: Person },
|
|
93
|
+
{
|
|
94
|
+
description:
|
|
95
|
+
"The person reasonably expects their individual income in 2026 to remain " +
|
|
96
|
+
"at least at the level earned in each of 2024 and 2025.",
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const isQualifiedInvestor = predicate("is_qualified_investor", {
|
|
101
|
+
person: Person,
|
|
102
|
+
});
|
|
103
|
+
const hasVerifiedIdentity = predicate("has_verified_identity", {
|
|
104
|
+
person: Person,
|
|
105
|
+
});
|
|
106
|
+
const isReadyForReview = predicate("is_ready_for_review", { person: Person });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 3. Encode the ruleset
|
|
110
|
+
|
|
111
|
+
All three income conditions must hold for this route to qualification. Our
|
|
112
|
+
application also requires verified identity before sending an investor for review.
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { and, variable } from "factbag/dsl";
|
|
116
|
+
import { KnowledgeBase } from "factbag/kb";
|
|
117
|
+
|
|
118
|
+
const person = variable("person");
|
|
119
|
+
const kb = new KnowledgeBase();
|
|
120
|
+
|
|
121
|
+
kb.rule(isQualifiedInvestor({ person })).if(
|
|
122
|
+
and(
|
|
123
|
+
hasIncomeOver200k({ person, year: 2024 }),
|
|
124
|
+
hasIncomeOver200k({ person, year: 2025 }),
|
|
125
|
+
expectsSameIncome({ person }),
|
|
126
|
+
),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
kb.rule(isReadyForReview({ person })).if(
|
|
130
|
+
and(isQualifiedInvestor({ person }), hasVerifiedIdentity({ person })),
|
|
131
|
+
);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 4. Import a known fact and add documents
|
|
135
|
+
|
|
136
|
+
Identity verification comes directly from our onboarding system. Income evidence
|
|
137
|
+
goes into the corpus for Jev to assess.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { Corpus } from "factbag/corpus";
|
|
141
|
+
|
|
142
|
+
const alice = new Person("alice", "Alice");
|
|
143
|
+
kb.entities.add(alice);
|
|
144
|
+
|
|
145
|
+
kb.asserts(hasVerifiedIdentity({ person: alice }), {
|
|
146
|
+
source: "onboarding:alice",
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const corpus = new Corpus();
|
|
150
|
+
|
|
151
|
+
// Tax returns provide evidence of past annual income.
|
|
152
|
+
corpus.add({ documentId: "alice-2024-federal-tax-return", text: "…" });
|
|
153
|
+
corpus.add({ documentId: "alice-2025-federal-tax-return", text: "…" });
|
|
154
|
+
|
|
155
|
+
// An employer letter supports the expectation of continued income.
|
|
156
|
+
corpus.add({
|
|
157
|
+
documentId: "alice-2026-employment-confirmation-letter",
|
|
158
|
+
text: "…",
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 5. Set Jev's permissions
|
|
163
|
+
|
|
164
|
+
The guard starts with no permissions. Permit Jev to assert or deny the two income
|
|
165
|
+
predicates.
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { guard } from "factbag/kb";
|
|
169
|
+
|
|
170
|
+
const incomeEvidence = guard(kb)
|
|
171
|
+
.permit("assert", hasIncomeOver200k)
|
|
172
|
+
.permit("deny", hasIncomeOver200k)
|
|
173
|
+
.permit("assert", expectsSameIncome)
|
|
174
|
+
.permit("deny", expectsSameIncome);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 6. Ask Jev to assess the income evidence
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { classifier } from "@factbag/jev";
|
|
181
|
+
|
|
182
|
+
const run = classifier({ corpus, input: incomeEvidence });
|
|
183
|
+
|
|
184
|
+
for (const job of run) {
|
|
185
|
+
await job.execute(); // Makes a live Jev request.
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### 7. Query the conclusion
|
|
190
|
+
|
|
191
|
+
The output knowledge base combines the imported fact and rules with the admitted
|
|
192
|
+
evidence.
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
import { query } from "factbag/dsl";
|
|
196
|
+
|
|
197
|
+
const qualification = query(run.kb, isQualifiedInvestor({ person: alice }));
|
|
198
|
+
const reviewReadiness = query(run.kb, isReadyForReview({ person: alice }));
|
|
199
|
+
|
|
200
|
+
// Both are "true" if Jev admits supporting evidence for all three income claims.
|
|
201
|
+
qualification.value; // "true"
|
|
202
|
+
reviewReadiness.value; // "true"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Results can be `true`, `false`, `conflicted`, or
|
|
206
|
+
`unknown`, depending on the evidence admitted.
|
|
207
|
+
|
|
208
|
+
### 8. Inspect the proof tree
|
|
209
|
+
|
|
210
|
+
Use `explainJudgment()` to follow a conclusion through its rule to the supporting
|
|
211
|
+
evidence and source passages.
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
import { explainJudgment } from "factbag/explain";
|
|
215
|
+
|
|
216
|
+
const proof = explainJudgment(qualification);
|
|
217
|
+
JSON.stringify(proof, null, 2); // JSON shown below.
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
An abbreviated example with supporting evidence for all three claims:
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"claim": "is_qualified_investor(alice) = true",
|
|
225
|
+
"because": "is_qualified_investor(person) if and(...)",
|
|
226
|
+
"given": [
|
|
227
|
+
{
|
|
228
|
+
"claim": "has_income_over_200k(alice, 2024) = true",
|
|
229
|
+
"because": {
|
|
230
|
+
"source": "alice-2024-federal-tax-return#0-1"
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"claim": "has_income_over_200k(alice, 2025) = true",
|
|
235
|
+
"because": {
|
|
236
|
+
"source": "alice-2025-federal-tax-return#0-1"
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"claim": "expects_same_income(alice) = true",
|
|
241
|
+
"because": {
|
|
242
|
+
"source": "alice-2026-employment-confirmation-letter#0-1"
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Source IDs identify document passages; their ranges depend on the document text.
|
|
250
|
+
Use `corpus.resolve(source)` to retrieve the passage behind a piece of evidence.
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
const source = "alice-2024-federal-tax-return#0-1";
|
|
254
|
+
corpus.resolve(source); // Object shown below.
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The result includes the exact passage and its containing document:
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"chunk": {
|
|
262
|
+
"id": "alice-2024-federal-tax-return#0-1",
|
|
263
|
+
"documentId": "alice-2024-federal-tax-return",
|
|
264
|
+
"range": { "start": 0, "end": 1 },
|
|
265
|
+
"text": "…"
|
|
266
|
+
},
|
|
267
|
+
"document": {
|
|
268
|
+
"documentId": "alice-2024-federal-tax-return",
|
|
269
|
+
"text": "…",
|
|
270
|
+
"metadata": {}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### 9. Review conflicting evidence (optional)
|
|
276
|
+
|
|
277
|
+
Suppose an original tax return puts Alice below the income threshold, but an
|
|
278
|
+
amended return puts her above it. Jev admits both accounts, leaving qualification
|
|
279
|
+
`conflicted`. In this example, the other income conditions have supporting evidence.
|
|
280
|
+
|
|
281
|
+
You could pass the conflicting evidence to a human for review, or ask Jev to
|
|
282
|
+
compare the sources using the `resolver`.
|
|
283
|
+
|
|
284
|
+
Evidence judged superseded is retracted from the resolver's output
|
|
285
|
+
knowledge base.
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
import { resolver } from "@factbag/jev";
|
|
289
|
+
|
|
290
|
+
query(run.kb, isQualifiedInvestor({ person: alice })).value; // "conflicted"
|
|
291
|
+
|
|
292
|
+
const review = resolver(corpus, run.kb);
|
|
293
|
+
|
|
294
|
+
for (const job of review) {
|
|
295
|
+
await job.execute();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// If the resolver retracts the superseded return's denial:
|
|
299
|
+
query(review.kb, isQualifiedInvestor({ person: alice })).value; // "true"
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Go further
|
|
303
|
+
|
|
304
|
+
- [Introducing Factbag](https://daniel.du-pless.is/introducing-factbag): how and why
|
|
305
|
+
to use it.
|
|
306
|
+
- [Jev and the Art of Truth Maintenance](https://daniel.du-pless.is/jev-and-the-art-of-truth-maintenance):
|
|
307
|
+
the thinking behind the project.
|
|
308
|
+
|
|
309
|
+
Apache-2.0 licensed.
|