@littlebigbrain/create 0.1.0 → 0.3.0
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/README.md +6 -2
- package/lib/create.mjs +33 -5
- package/package.json +1 -1
- package/templates/_shared/testing.ts +53 -9
- package/templates/documents-link/README.md +9 -7
- package/templates/documents-link/workflow.test.ts +28 -1
- package/templates/documents-link/workflow.ts +24 -14
- package/templates/documents-map/README.md +212 -0
- package/templates/documents-map/map.ts +1135 -0
- package/templates/documents-map/samples/ausstattungsliste.pdf +76 -0
- package/templates/documents-map/samples/baubeschreibung.pdf +54 -0
- package/templates/documents-map/samples/expected.json +50 -0
- package/templates/documents-map/samples/pages/raumblaetter.json +1580 -0
- package/templates/documents-map/samples/raumblaetter.pdf +109 -0
- package/templates/documents-map/samples/trace.json +613 -0
- package/templates/documents-map/samples.ts +182 -0
- package/templates/documents-map/workflow.test.ts +770 -0
- package/templates/documents-map/workflow.ts +1199 -0
- package/templates/link/README.md +203 -0
- package/templates/link/candidates.ts +432 -0
- package/templates/link/decide.test.ts +368 -0
- package/templates/link/decide.ts +340 -0
- package/templates/link/fixture.ts +727 -0
- package/templates/link/grade-run.ts +53 -0
- package/templates/link/grade.test.ts +113 -0
- package/templates/link/grade.ts +335 -0
- package/templates/link/items.ts +333 -0
- package/templates/link/names.test.ts +260 -0
- package/templates/link/names.ts +590 -0
- package/templates/link/reads.ts +302 -0
- package/templates/link/samples/gold.json +108 -0
- package/templates/link/samples/records.json +48 -0
- package/templates/link/samples.ts +64 -0
- package/templates/link/settings.ts +162 -0
- package/templates/link/setup.ts +18 -0
- package/templates/link/source-turn.ts +894 -0
- package/templates/link/sources/records.ts +429 -0
- package/templates/link/things-turn.ts +984 -0
- package/templates/link/things.test.ts +204 -0
- package/templates/link/things.ts +426 -0
- package/templates/link/try.ts +28 -0
- package/templates/link/worker.ts +24 -0
- package/templates/link/workflow.test.ts +484 -0
- package/templates/link/workflow.ts +202 -0
- package/templates/ontology-fit/workflow.test.ts +7 -1
- package/templates/ontology-fit/workflow.ts +5 -2
- package/templates/review/README.md +158 -0
- package/templates/review/decision.ts +483 -0
- package/templates/review/samples/expected.json +37 -0
- package/templates/review/samples/proposals.json +35 -0
- package/templates/review/samples.ts +154 -0
- package/templates/review/workflow.test.ts +438 -0
- package/templates/review/workflow.ts +459 -0
- package/templates/values-extract/workflow.test.ts +24 -1
- package/templates/values-extract/workflow.ts +11 -3
- package/templates/versions.json +1 -1
|
@@ -13,6 +13,7 @@ import { ENTITY_IRI_NAMESPACE, type Schemas } from "@littlebigbrain/client";
|
|
|
13
13
|
import {
|
|
14
14
|
MockActivities,
|
|
15
15
|
mockId,
|
|
16
|
+
phases,
|
|
16
17
|
readJson,
|
|
17
18
|
runMessage,
|
|
18
19
|
samplesDir,
|
|
@@ -195,7 +196,7 @@ function rules(more: MockRules = {}): MockRules {
|
|
|
195
196
|
|
|
196
197
|
test("the samples give the changes of expected.md, one review item per key", async () => {
|
|
197
198
|
const lbb = new MockActivities(rules());
|
|
198
|
-
const { state } = await runMessage(
|
|
199
|
+
const { state, turns } = await runMessage(
|
|
199
200
|
createWorkflow(lbb),
|
|
200
201
|
triggerMessage(
|
|
201
202
|
"berichte",
|
|
@@ -203,6 +204,11 @@ test("the samples give the changes of expected.md, one review item per key", asy
|
|
|
203
204
|
),
|
|
204
205
|
);
|
|
205
206
|
|
|
207
|
+
assert.deepEqual(phases(turns), ["reading texts", "filing review items"]);
|
|
208
|
+
assert.deepEqual(
|
|
209
|
+
turns[0]!.progress.filter((p) => p.phase === "filing review items").map((p) => p.done),
|
|
210
|
+
[0, 1, 2, 3, 4],
|
|
211
|
+
);
|
|
206
212
|
assert.equal(EXPECTED_KEYS.length, 5);
|
|
207
213
|
assert.deepEqual([...lbb.reviews.keys()].sort(), [...EXPECTED_KEYS].sort());
|
|
208
214
|
assert.deepEqual(state, { texts: 5, proposals: 3 + 4 + 5 + 5 + 2, filed: 5 });
|
|
@@ -300,6 +300,7 @@ export function createWorkflow(lbb: LbbActivities, settings: Settings = defaults
|
|
|
300
300
|
for (const [i, iri] of batch.entries()) {
|
|
301
301
|
kept.push(
|
|
302
302
|
await ctx.step(`propose-${i}`, async (s) => {
|
|
303
|
+
s.progress("reading texts", { done: i, total: batch.length, unit: "instances" });
|
|
303
304
|
const record = (await texts())[iri];
|
|
304
305
|
if (!record?.text.trim()) return [];
|
|
305
306
|
const ontology = await (onto ??= lbb.ontology().then(describe));
|
|
@@ -346,7 +347,7 @@ export function createWorkflow(lbb: LbbActivities, settings: Settings = defaults
|
|
|
346
347
|
}),
|
|
347
348
|
);
|
|
348
349
|
}
|
|
349
|
-
const filed = await ctx.step("review", async () => {
|
|
350
|
+
const filed = await ctx.step("review", async (s) => {
|
|
350
351
|
const records = await texts();
|
|
351
352
|
const byKey = new Map<
|
|
352
353
|
string,
|
|
@@ -362,7 +363,8 @@ export function createWorkflow(lbb: LbbActivities, settings: Settings = defaults
|
|
|
362
363
|
});
|
|
363
364
|
byKey.set(change.key, group);
|
|
364
365
|
}
|
|
365
|
-
for (const { change, samples } of byKey.values())
|
|
366
|
+
for (const [n, { change, samples }] of [...byKey.values()].entries()) {
|
|
367
|
+
s.progress("filing review items", { done: n, total: byKey.size, unit: "items" });
|
|
366
368
|
await lbb.review({
|
|
367
369
|
workflow: message.workflow,
|
|
368
370
|
key: change.key,
|
|
@@ -372,6 +374,7 @@ export function createWorkflow(lbb: LbbActivities, settings: Settings = defaults
|
|
|
372
374
|
change: change.change,
|
|
373
375
|
evidence: { records: samples.length, samples: samples.slice(0, 20) },
|
|
374
376
|
});
|
|
377
|
+
}
|
|
375
378
|
return byKey.size;
|
|
376
379
|
});
|
|
377
380
|
const next = {
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# review
|
|
2
|
+
|
|
3
|
+
Decides which fact proposals become facts. A proposer (the
|
|
4
|
+
[`documents-map`](../documents-map/README.md) template, LBB's
|
|
5
|
+
`documents.map`, or a workflow of yours) does not write the facts of the
|
|
6
|
+
graph's own ontology itself: it writes one `FactProposal` per statement,
|
|
7
|
+
with its confidence and its evidence. This workflow writes a proposal at or
|
|
8
|
+
above the gate as a fact, with the proposal as its evidence, and gives the
|
|
9
|
+
others to a person: one review item per instance.
|
|
10
|
+
|
|
11
|
+
The LBB-run version is the starter workflow `review@v1`
|
|
12
|
+
(`docs/reference/starter-workflows.md`, "Review"; design
|
|
13
|
+
`docs/architecture/document-output-mapping.md`). This template is the same
|
|
14
|
+
decision in TypeScript: the workflow in `workflow.ts`, the rules and the
|
|
15
|
+
facts in `decision.ts`. It calls no model.
|
|
16
|
+
|
|
17
|
+
## How it runs
|
|
18
|
+
|
|
19
|
+
The triggered workflow watches the class `FactProposal` and the fields of
|
|
20
|
+
its statement (`fact_subject`, `fact_predicate`, `fact_object`), so a new
|
|
21
|
+
proposal, or one whose statement changed, sends a message. A turn decides at
|
|
22
|
+
most 20 proposals, two steps each, and files at most one review item per
|
|
23
|
+
instance; the rest of a message goes to a new turn (`ctx.continue`). Each
|
|
24
|
+
proposal is decided once: one that has a `fact_status` already is skipped.
|
|
25
|
+
|
|
26
|
+
1. **Read** the proposal (`fact_subject`, `fact_predicate`, `fact_object`,
|
|
27
|
+
`fact_datatype`, `fact_confidence`, `fact_evidence`, `proposed_by`), the
|
|
28
|
+
entities its statement names, and the ontology (`GET /v1/ontology`). An
|
|
29
|
+
entity the graph does not have is a new instance: its class comes from an
|
|
30
|
+
`rdf:type` proposal of it, its name from an `rdfs:label` proposal.
|
|
31
|
+
2. **What the statement is** (`classify`): an `rdf:type` of a class, an
|
|
32
|
+
`rdfs:label`, a relation between two instances (the entry's `DENOTES`
|
|
33
|
+
included), or a property value in its type. The ends of a relation must fit
|
|
34
|
+
its domain and range, superclasses included.
|
|
35
|
+
3. **The decision** (`decide`), in this order:
|
|
36
|
+
- **refused** when the graph cannot hold the statement: a predicate that
|
|
37
|
+
is no relation or property of the records ontology (nor `rdf:type` or
|
|
38
|
+
`rdfs:label`), a value of another type, an object that is no instance,
|
|
39
|
+
an instance without a class, an end its relation does not take, a second
|
|
40
|
+
class, or a statement about a proposal;
|
|
41
|
+
- **to a person** when a rule of `alwaysReview` names it (`new_instance`:
|
|
42
|
+
a statement whose subject or object the graph does not have yet; or a
|
|
43
|
+
predicate IRI), when it would replace another writer's value (a records
|
|
44
|
+
graph holds one value per property) or name, or when its confidence is
|
|
45
|
+
below the gate. The proposal goes into the **review item of its
|
|
46
|
+
instance** (`groupOf`): its subject; the object of a link whose subject
|
|
47
|
+
the graph has and whose object is new; and always the object of the
|
|
48
|
+
trace (`DENOTES`). One item holds every proposal of its instance that
|
|
49
|
+
the turn sends to a person, in order: the type, the label, values,
|
|
50
|
+
links, the trace (`fileItem`). Its title names the instance (`New
|
|
51
|
+
Equipment M 7 Medienwagen`, or `Room Raum 0.12 Klassenraum: 1 fact to
|
|
52
|
+
check`), its rationale says why, and it has one evidence sample per
|
|
53
|
+
fact (what it states, its confidence, its line). Its facts are each
|
|
54
|
+
statement and its proposal's `fact_status: accepted`, and each sample
|
|
55
|
+
names the rows and triplets of its fact (`row`, `rows`, `triplets`), so
|
|
56
|
+
a person can leave facts out of the accept. The type of a new instance
|
|
57
|
+
is a required row: a person keeps it or dismisses the item;
|
|
58
|
+
- **written** otherwise: a relation as an edge whose evidence names the
|
|
59
|
+
proposal (`source_id` is the proposal's IRI, the text its evidence); a
|
|
60
|
+
property as its value; a new instance's `rdf:type` as the instance of
|
|
61
|
+
that class with `proposed_in` (a records graph holds an instance through
|
|
62
|
+
its facts); a label as the entity's name. A new instance takes the name
|
|
63
|
+
of its label proposal (the first in order) from its first write, so its
|
|
64
|
+
name does not depend on which statement is written first. A label is a
|
|
65
|
+
text: an IRI or an empty label is refused. A statement the graph holds
|
|
66
|
+
already is not written again. Nothing is taken back, a name included: a
|
|
67
|
+
records instance keeps one.
|
|
68
|
+
4. **Write** (`writeOwned`): the proposal's `fact_status` (`written`,
|
|
69
|
+
`in_review` or `refused`) and `review_note`, owned by this workflow, and in
|
|
70
|
+
the same commit the facts of a written statement.
|
|
71
|
+
|
|
72
|
+
## Settings
|
|
73
|
+
|
|
74
|
+
`defaults` in `workflow.ts`, those of LBB's `review`:
|
|
75
|
+
|
|
76
|
+
| Setting | Default | Meaning |
|
|
77
|
+
| --- | --- | --- |
|
|
78
|
+
| `className` | `FactProposal` | The class of the proposals |
|
|
79
|
+
| `gate` | 0.8 | A proposal below this confidence goes to a person |
|
|
80
|
+
| `alwaysReview` | `[]` | What goes to a person whatever its confidence: `new_instance`, and predicate IRIs (`https://littlebigbrain.com/r/has_equipment`). Empty: the gate decides everything, a new instance too |
|
|
81
|
+
|
|
82
|
+
With `["new_instance"]` every statement about a new instance goes to a
|
|
83
|
+
person, whatever its confidence. That was the default of LBB's `review`
|
|
84
|
+
until 2026-10-12.
|
|
85
|
+
|
|
86
|
+
`trigger(settings)` names the triggered workflow `review` and owns
|
|
87
|
+
`fact_status` and `review_note`. `ontology(settings)` adds the class and the
|
|
88
|
+
properties of a proposal and of the review (`proposed_in` too). Do not run it
|
|
89
|
+
beside an LBB-run `review` on the same graph: they own the same properties.
|
|
90
|
+
|
|
91
|
+
## What the LBB-run version adds
|
|
92
|
+
|
|
93
|
+
- **It writes the accepted facts itself.** Its item carries the statuses
|
|
94
|
+
only; its sweep writes each kept fact, the type first, and dismisses the
|
|
95
|
+
facts a person left out. A link to another new instance waits until that
|
|
96
|
+
instance's item is accepted, and is dismissed with it. The template's item
|
|
97
|
+
commits its facts on accept, so such a link makes the other instance too.
|
|
98
|
+
- **One item per instance over time.** Its items follow the open proposals
|
|
99
|
+
of an instance across messages: when they change, it supersedes the open
|
|
100
|
+
item with a new one, and an accept of the old item fails (409). The
|
|
101
|
+
template files one item per instance and turn: the key carries the
|
|
102
|
+
proposals, so a later turn with other proposals of the instance files
|
|
103
|
+
another item.
|
|
104
|
+
- **Withdrawn proposals.** When a proposer withdraws a proposal, it takes
|
|
105
|
+
the proposal out of its item and retracts the fact it wrote, unless a
|
|
106
|
+
person accepted it or the fact held before; a record per proposal says
|
|
107
|
+
which fact. The template keeps both: dismiss the item, or retract the fact
|
|
108
|
+
yourself.
|
|
109
|
+
- **Dismissed items.** It checks the open review items and sets
|
|
110
|
+
`dismissed` on the proposals of a dismissed one. The template leaves
|
|
111
|
+
`in_review`.
|
|
112
|
+
- **Predicates another workflow owns.** It refuses a statement whose
|
|
113
|
+
predicate another workflow owns by predicate, since that workflow's owned
|
|
114
|
+
write would delete it.
|
|
115
|
+
- **RDF-native graphs by IRI.** On an RDF-native graph it writes each
|
|
116
|
+
statement as one owned triple: `lbb:denotes`, the asserted schema's
|
|
117
|
+
properties (the OWL object, symmetric, transitive, functional,
|
|
118
|
+
inverse-functional and asymmetric properties take an IRI) and classes
|
|
119
|
+
(the ends of `rdfs:subClassOf` count), the records ontology's terms and
|
|
120
|
+
`rdfs:label`. There a subject or object is new when it is the subject of
|
|
121
|
+
no statement. The template writes through `POST /v1/graph/commit`, on both
|
|
122
|
+
graph kinds: the records ontology's relations (the records `DENOTES`
|
|
123
|
+
included), properties and classes, and `rdfs:label`. An instance is new
|
|
124
|
+
when it has no class of the records ontology. It refuses any other
|
|
125
|
+
predicate with a note that says so (`owl:sameAs`, `lbb:denotes`).
|
|
126
|
+
- **Ownership by statement.** Its facts count as owned by statement, so it
|
|
127
|
+
conflicts with no other workflow on the predicates it writes. The template
|
|
128
|
+
owns `fact_status` and `review_note` only and writes the facts without
|
|
129
|
+
owning them.
|
|
130
|
+
- **The graph refuses.** It lets the commit refuse a statement (a domain, a
|
|
131
|
+
range) and marks the proposal `refused`. The template checks domain and
|
|
132
|
+
range before it writes; another refusal stops the turn.
|
|
133
|
+
- **Status and preview.** Counters per outcome, the open items it follows,
|
|
134
|
+
and a preview of what it would decide.
|
|
135
|
+
|
|
136
|
+
## Samples and test
|
|
137
|
+
|
|
138
|
+
`samples/proposals.json` holds a small invented graph (two rooms of the
|
|
139
|
+
school building and five pieces of equipment, one `HAS_EQUIPMENT` edge) and
|
|
140
|
+
14 proposals about it, one for each case; `samples/expected.json` gives the
|
|
141
|
+
status each one gets with `alwaysReview` `["new_instance"]`, the facts the
|
|
142
|
+
graph holds after the run, and the three review items with the proposals
|
|
143
|
+
each one holds
|
|
144
|
+
([`../_samples/README.md`](../_samples/README.md)).
|
|
145
|
+
|
|
146
|
+
`npm run try` adds the sample ontology (`Room`, `Equipment`,
|
|
147
|
+
`HAS_EQUIPMENT`, `identifier`, `area_m2`), commits the instances, reads
|
|
148
|
+
their IRIs back, and commits the proposals as a proposer would
|
|
149
|
+
(`proposed_by` is `samples/review`). This is the simplest input: the review
|
|
150
|
+
calls no model and needs no proposer to run first. To review the proposals
|
|
151
|
+
of `documents-map`, run it (or LBB's `documents.map`) on the same graph.
|
|
152
|
+
|
|
153
|
+
`workflow.test.ts` runs the workflow on a graph in memory with the samples:
|
|
154
|
+
the statuses and facts of the answer key, the evidence of a written fact,
|
|
155
|
+
the notes, one item per instance (the order of its facts and its required
|
|
156
|
+
type), the facts of an accepted item, the default settings (the gate
|
|
157
|
+
decides a new instance), a predicate rule, a second message that changes
|
|
158
|
+
nothing, and a message larger than a turn.
|