@wrenautomation/birdwatch 0.0.0-stage → 0.30.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/LICENSE.md +61 -0
- package/README.md +320 -2
- package/dist/app/cli.d.ts +4 -0
- package/dist/app/cli.js +149 -0
- package/dist/app/cli.js.map +1 -0
- package/dist/app/engine-cli.d.ts +44 -0
- package/dist/app/engine-cli.js +596 -0
- package/dist/app/engine-cli.js.map +1 -0
- package/dist/app/index.d.ts +6 -0
- package/dist/app/index.js +7 -0
- package/dist/app/index.js.map +1 -0
- package/dist/app/serve.d.ts +14 -0
- package/dist/app/serve.js +102 -0
- package/dist/app/serve.js.map +1 -0
- package/dist/document/body.d.ts +6 -0
- package/dist/document/body.js +18 -0
- package/dist/document/body.js.map +1 -0
- package/dist/document/check.d.ts +35 -0
- package/dist/document/check.js +68 -0
- package/dist/document/check.js.map +1 -0
- package/dist/document/extract.d.ts +81 -0
- package/dist/document/extract.js +226 -0
- package/dist/document/extract.js.map +1 -0
- package/dist/document/index.d.ts +11 -0
- package/dist/document/index.js +12 -0
- package/dist/document/index.js.map +1 -0
- package/dist/document/model.d.ts +10 -0
- package/dist/document/model.js +2 -0
- package/dist/document/model.js.map +1 -0
- package/dist/document/readers.d.ts +24 -0
- package/dist/document/readers.js +87 -0
- package/dist/document/readers.js.map +1 -0
- package/dist/document/types.d.ts +99 -0
- package/dist/document/types.js +93 -0
- package/dist/document/types.js.map +1 -0
- package/dist/engine/agent.d.ts +475 -0
- package/dist/engine/agent.js +990 -0
- package/dist/engine/agent.js.map +1 -0
- package/dist/engine/canonical.d.ts +4 -0
- package/dist/engine/canonical.js +55 -0
- package/dist/engine/canonical.js.map +1 -0
- package/dist/engine/context.d.ts +9 -0
- package/dist/engine/context.js +141 -0
- package/dist/engine/context.js.map +1 -0
- package/dist/engine/cost.d.ts +13 -0
- package/dist/engine/cost.js +31 -0
- package/dist/engine/cost.js.map +1 -0
- package/dist/engine/index.d.ts +16 -0
- package/dist/engine/index.js +17 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/judge.d.ts +158 -0
- package/dist/engine/judge.js +323 -0
- package/dist/engine/judge.js.map +1 -0
- package/dist/engine/people.d.ts +34 -0
- package/dist/engine/people.js +125 -0
- package/dist/engine/people.js.map +1 -0
- package/dist/engine/plan.d.ts +6 -0
- package/dist/engine/plan.js +7 -0
- package/dist/engine/plan.js.map +1 -0
- package/dist/engine/plans/index.d.ts +19 -0
- package/dist/engine/plans/index.js +104 -0
- package/dist/engine/plans/index.js.map +1 -0
- package/dist/engine/ports.d.ts +76 -0
- package/dist/engine/ports.js +28 -0
- package/dist/engine/ports.js.map +1 -0
- package/dist/engine/recall.d.ts +42 -0
- package/dist/engine/recall.js +71 -0
- package/dist/engine/recall.js.map +1 -0
- package/dist/engine/state.d.ts +96 -0
- package/dist/engine/state.js +162 -0
- package/dist/engine/state.js.map +1 -0
- package/dist/engine/steps.d.ts +98 -0
- package/dist/engine/steps.js +435 -0
- package/dist/engine/steps.js.map +1 -0
- package/dist/engine/tree.d.ts +76 -0
- package/dist/engine/tree.js +148 -0
- package/dist/engine/tree.js.map +1 -0
- package/dist/engine/types.d.ts +250 -0
- package/dist/engine/types.js +168 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/engine/view.d.ts +138 -0
- package/dist/engine/view.js +188 -0
- package/dist/engine/view.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger/files.d.ts +39 -0
- package/dist/ledger/files.js +113 -0
- package/dist/ledger/files.js.map +1 -0
- package/dist/ledger/ids.d.ts +4 -0
- package/dist/ledger/ids.js +9 -0
- package/dist/ledger/ids.js.map +1 -0
- package/dist/ledger/index.d.ts +11 -0
- package/dist/ledger/index.js +12 -0
- package/dist/ledger/index.js.map +1 -0
- package/dist/ledger/ledger.d.ts +46 -0
- package/dist/ledger/ledger.js +105 -0
- package/dist/ledger/ledger.js.map +1 -0
- package/dist/ledger/memory.d.ts +19 -0
- package/dist/ledger/memory.js +19 -0
- package/dist/ledger/memory.js.map +1 -0
- package/dist/ledger/records.d.ts +1424 -0
- package/dist/ledger/records.js +292 -0
- package/dist/ledger/records.js.map +1 -0
- package/dist/sources/arxiv.d.ts +105 -0
- package/dist/sources/arxiv.js +321 -0
- package/dist/sources/arxiv.js.map +1 -0
- package/dist/sources/exa.d.ts +19 -0
- package/dist/sources/exa.js +59 -0
- package/dist/sources/exa.js.map +1 -0
- package/dist/sources/fetcher.d.ts +35 -0
- package/dist/sources/fetcher.js +67 -0
- package/dist/sources/fetcher.js.map +1 -0
- package/dist/sources/google.d.ts +16 -0
- package/dist/sources/google.js +64 -0
- package/dist/sources/google.js.map +1 -0
- package/dist/sources/index.d.ts +17 -0
- package/dist/sources/index.js +18 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/sources/junk.d.ts +10 -0
- package/dist/sources/junk.js +16 -0
- package/dist/sources/junk.js.map +1 -0
- package/dist/sources/ladder.d.ts +47 -0
- package/dist/sources/ladder.js +158 -0
- package/dist/sources/ladder.js.map +1 -0
- package/dist/sources/ledger.d.ts +7 -0
- package/dist/sources/ledger.js +35 -0
- package/dist/sources/ledger.js.map +1 -0
- package/dist/sources/paste.d.ts +8 -0
- package/dist/sources/paste.js +22 -0
- package/dist/sources/paste.js.map +1 -0
- package/dist/sources/searxng.d.ts +11 -0
- package/dist/sources/searxng.js +38 -0
- package/dist/sources/searxng.js.map +1 -0
- package/dist/sources/sites.d.ts +53 -0
- package/dist/sources/sites.js +285 -0
- package/dist/sources/sites.js.map +1 -0
- package/dist/sources/ytdlp.d.ts +43 -0
- package/dist/sources/ytdlp.js +117 -0
- package/dist/sources/ytdlp.js.map +1 -0
- package/package.json +61 -3
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An extractor draws findings out of one document for one ask, and says what the document was
|
|
3
|
+
* worth. The claim extractor is the one in v1: the model proposes sentences with the exact words
|
|
4
|
+
* they rest on; the gate keeps a finding only when its quote is on the document, word for word,
|
|
5
|
+
* and every number in it is in the quote (a year may come from the document). What the model
|
|
6
|
+
* knows without the document never becomes a finding. The verdict is the same call's word on the
|
|
7
|
+
* document (`quality`) and on the ask (`gap`); the gate corrects a `rich` that kept nothing.
|
|
8
|
+
*
|
|
9
|
+
* No cutoff: the prompt caps neither the findings nor their length, and a document is sent
|
|
10
|
+
* whole. When the model's output cap cuts an answer (a `Cut`), the whole findings before the cut
|
|
11
|
+
* go through the gate, and the model is asked again for the text after the last quote it kept,
|
|
12
|
+
* with those findings known. A cut answer with no whole finding on the document stops the
|
|
13
|
+
* extract with an error: the one case a document stays unread.
|
|
14
|
+
*/
|
|
15
|
+
import { fill, numbersQuoted, pageQuoted, words } from "@wrenautomation/read/study";
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
import { bodyOf } from "./body.js";
|
|
18
|
+
import { addUsage, isCut } from "./model.js";
|
|
19
|
+
import { Quality } from "./types.js";
|
|
20
|
+
const QUOTE_MIN_WORDS = 5;
|
|
21
|
+
export const CLAIM_PROMPT = `You pull facts out of one document for a research ask.
|
|
22
|
+
|
|
23
|
+
Return ONLY a JSON object, no prose, exactly this shape:
|
|
24
|
+
{"findings": [{"text": "...", "quote": "..."}], "quality": "rich", "gap": "..."}
|
|
25
|
+
|
|
26
|
+
Rules:
|
|
27
|
+
- Every finding that serves the ask and is not known already, in document order.
|
|
28
|
+
- text: one plain sentence a person would say out loud. Specific: a number, a named practice, a stated cause. Never "The document says" or "The page notes".
|
|
29
|
+
- Say who says it, as the page shows it: "A 2025 survey of 400 firms found ...", "One commenter says ...", "A vendor of X says ...". One person is one person: never "owners say" or "commenters" for one voice.
|
|
30
|
+
- Name a speaker's role, trade, size or place only when the page states it.
|
|
31
|
+
- Keep the quote's hedge and scope: "probably", "sometimes", "a lot of", "in my experience" stay; never turn them into "most", "often" or a rule.
|
|
32
|
+
- Advice, a question, a promise or a sales pitch is not a report of what happens. Take it only as what it is ("A seller claims ...").
|
|
33
|
+
- quote: the exact words from the document that the finding rests on, copied character for character, at least 5 words, no more than the finding needs.
|
|
34
|
+
- Every part of the text must be in the quote: no cause, ranking, detail or second fact the quote lacks. Two facts from two places are two findings.
|
|
35
|
+
- Every number in the text must appear in the quote. A table cell without its row and column labels is not a quote.
|
|
36
|
+
- A company's marketing about itself is not a fact about the market.
|
|
37
|
+
- Nothing in the document serves the ask: return {"findings": [], "quality": "off", "gap": "..."}.
|
|
38
|
+
- quality: "rich" when the document answers much of the ask, "thin" when it serves it a little, "off" when it is about something else.
|
|
39
|
+
- gap: one short sentence on what the ask still lacks after this document, or an empty string when nothing is missing.
|
|
40
|
+
|
|
41
|
+
Ask: {ask}
|
|
42
|
+
{steer}Known already (look past these):
|
|
43
|
+
{known}
|
|
44
|
+
|
|
45
|
+
DOCUMENT: {url}{part}
|
|
46
|
+
{text}
|
|
47
|
+
`;
|
|
48
|
+
export const ClaimAnswer = z.object({
|
|
49
|
+
findings: z.array(z.object({ text: z.string().nullable().optional(), quote: z.string().nullable().optional() })),
|
|
50
|
+
quality: Quality.nullable().optional(),
|
|
51
|
+
gap: z.string().nullable().optional(),
|
|
52
|
+
});
|
|
53
|
+
/** "The person says:" and their lines, or nothing: a prompt pays for the heading only when there is one. */
|
|
54
|
+
export const renderSteer = (steer) => steer?.length ? `The person says:\n${steer.map((s) => `- ${s}`).join("\n")}\n` : "";
|
|
55
|
+
export function claimPrompt(doc, ask, options = {}) {
|
|
56
|
+
const known = ask.known ?? [];
|
|
57
|
+
return fill(CLAIM_PROMPT, {
|
|
58
|
+
ask: ask.text,
|
|
59
|
+
steer: renderSteer(ask.steer),
|
|
60
|
+
known: known.length ? known.map((k) => `- ${k}`).join("\n") : "(nothing)",
|
|
61
|
+
url: doc.url,
|
|
62
|
+
part: options.rest ? " (the rest, after the last finding kept)" : "",
|
|
63
|
+
text: doc.text,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The verdict as the gate leaves it: the model's quality, unless it said `rich` and nothing
|
|
68
|
+
* survived (then `thin`); `off` when it said nothing and nothing survived; `empty` only when
|
|
69
|
+
* the document has no text. The gap is kept when it says something.
|
|
70
|
+
*/
|
|
71
|
+
export function verdictOf(answer, doc, kept) {
|
|
72
|
+
const gap = (answer.gap ?? "").replace(/\s+/g, " ").trim();
|
|
73
|
+
const said = answer.quality ?? (kept ? "thin" : "off");
|
|
74
|
+
const quality = !doc.text.trim()
|
|
75
|
+
? "empty"
|
|
76
|
+
: said === "rich" && !kept
|
|
77
|
+
? "thin"
|
|
78
|
+
: said === "empty"
|
|
79
|
+
? kept
|
|
80
|
+
? "thin"
|
|
81
|
+
: "off"
|
|
82
|
+
: said;
|
|
83
|
+
return { quality, ...(gap ? { gap } : {}) };
|
|
84
|
+
}
|
|
85
|
+
/** Every proposal through the gate: findings whose quote is on the document, dropped with why, the verdict. */
|
|
86
|
+
export function gateFindings(answer, doc) {
|
|
87
|
+
const page = { url: doc.url, text: doc.text };
|
|
88
|
+
const findings = [];
|
|
89
|
+
const dropped = [];
|
|
90
|
+
for (const p of answer.findings) {
|
|
91
|
+
const text = (p.text ?? "").replace(/\s+/g, " ").trim();
|
|
92
|
+
if (!text)
|
|
93
|
+
continue;
|
|
94
|
+
const quote = (p.quote ?? "").trim();
|
|
95
|
+
// A short quote proves little (it is on many pages); a line break inside one folds to a space.
|
|
96
|
+
const why = !quote
|
|
97
|
+
? "no_quote"
|
|
98
|
+
: words(quote) < QUOTE_MIN_WORDS
|
|
99
|
+
? "quote_short"
|
|
100
|
+
: !pageQuoted(quote, [page], doc.url)
|
|
101
|
+
? "quote_not_found"
|
|
102
|
+
: !numbersQuoted(text, quote, page)
|
|
103
|
+
? "number_not_in_quote"
|
|
104
|
+
: null;
|
|
105
|
+
if (why)
|
|
106
|
+
dropped.push({ text, why });
|
|
107
|
+
else if (!findings.some((f) => f.proof.quote === quote))
|
|
108
|
+
findings.push({ kind: "claim", text, proof: { url: doc.url, quote } });
|
|
109
|
+
}
|
|
110
|
+
return { findings, dropped, verdict: verdictOf(answer, doc, findings.length) };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The whole findings in a cut answer: each complete `{...}` of the findings array, in order, up
|
|
114
|
+
* to the first that does not parse. Braces and quotes inside strings are not structure.
|
|
115
|
+
*/
|
|
116
|
+
export function salvageFindings(partial) {
|
|
117
|
+
const findings = [];
|
|
118
|
+
const key = partial.indexOf('"findings"');
|
|
119
|
+
const open = key >= 0 ? partial.indexOf("[", key) : -1;
|
|
120
|
+
if (open < 0)
|
|
121
|
+
return { findings };
|
|
122
|
+
let i = open + 1;
|
|
123
|
+
while (i < partial.length) {
|
|
124
|
+
const start = partial.indexOf("{", i);
|
|
125
|
+
if (start < 0)
|
|
126
|
+
break;
|
|
127
|
+
let depth = 0;
|
|
128
|
+
let inString = false;
|
|
129
|
+
let end = -1;
|
|
130
|
+
for (let j = start; j < partial.length; j++) {
|
|
131
|
+
const c = partial[j];
|
|
132
|
+
if (inString) {
|
|
133
|
+
if (c === "\\")
|
|
134
|
+
j++;
|
|
135
|
+
else if (c === '"')
|
|
136
|
+
inString = false;
|
|
137
|
+
}
|
|
138
|
+
else if (c === '"')
|
|
139
|
+
inString = true;
|
|
140
|
+
else if (c === "{")
|
|
141
|
+
depth++;
|
|
142
|
+
else if (c === "}" && --depth === 0) {
|
|
143
|
+
end = j;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
else if (c === "]" && depth === 0)
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
if (end < 0)
|
|
150
|
+
break;
|
|
151
|
+
const one = ClaimAnswer.shape.findings.element.safeParse(JSON.parse(partial.slice(start, end + 1)));
|
|
152
|
+
if (!one.success)
|
|
153
|
+
break;
|
|
154
|
+
findings.push(one.data);
|
|
155
|
+
i = end + 1;
|
|
156
|
+
}
|
|
157
|
+
return { findings };
|
|
158
|
+
}
|
|
159
|
+
const RANK = { rich: 3, thin: 2, off: 1, empty: 0 };
|
|
160
|
+
/** Where a quote ends in the text, at or after `from`; -1 when the text does not hold it as written. */
|
|
161
|
+
function quoteEnd(text, quote, from) {
|
|
162
|
+
const q = quote.replace(/\s+/g, " ").trim();
|
|
163
|
+
let at = text.indexOf(q, from);
|
|
164
|
+
if (at < 0)
|
|
165
|
+
at = text.toLowerCase().indexOf(q.toLowerCase(), from);
|
|
166
|
+
return at < 0 ? -1 : at + q.length;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* The one extractor of v1: claims that quote the document, and a verdict on it. A cut answer
|
|
170
|
+
* is continued from the end of the last quote it kept, with those findings known, until the
|
|
171
|
+
* model finishes an answer.
|
|
172
|
+
*/
|
|
173
|
+
export const claimExtractor = {
|
|
174
|
+
name: "claim",
|
|
175
|
+
async extract(whole, ask, model) {
|
|
176
|
+
// the reference list holds no finding: every part and quote comes from the body
|
|
177
|
+
const doc = { ...whole, text: bodyOf(whole.text) };
|
|
178
|
+
const findings = [];
|
|
179
|
+
const dropped = [];
|
|
180
|
+
const known = [...(ask.known ?? [])];
|
|
181
|
+
let usage;
|
|
182
|
+
let from = 0;
|
|
183
|
+
let best = null;
|
|
184
|
+
let continued = 0;
|
|
185
|
+
for (;;) {
|
|
186
|
+
const part = from ? { ...doc, text: doc.text.slice(from) } : doc;
|
|
187
|
+
let answer;
|
|
188
|
+
let cut = false;
|
|
189
|
+
try {
|
|
190
|
+
const a = await model.ask(claimPrompt(part, { ...ask, known }, { rest: from > 0 }), ClaimAnswer);
|
|
191
|
+
answer = a.value;
|
|
192
|
+
usage = addUsage(usage, a.usage);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
if (!isCut(err))
|
|
196
|
+
throw err;
|
|
197
|
+
cut = true;
|
|
198
|
+
answer = salvageFindings(err.partial);
|
|
199
|
+
usage = addUsage(usage, err.usage);
|
|
200
|
+
}
|
|
201
|
+
const out = gateFindings(answer, part);
|
|
202
|
+
const fresh = out.findings.filter((f) => !findings.some((k) => k.proof.quote === f.proof.quote));
|
|
203
|
+
findings.push(...fresh);
|
|
204
|
+
dropped.push(...out.dropped);
|
|
205
|
+
if (answer.quality && (!best || RANK[answer.quality] > RANK[best]))
|
|
206
|
+
best = answer.quality;
|
|
207
|
+
if (!cut) {
|
|
208
|
+
return {
|
|
209
|
+
findings,
|
|
210
|
+
dropped,
|
|
211
|
+
verdict: verdictOf({ quality: best, gap: answer.gap }, doc, findings.length),
|
|
212
|
+
...(usage ? { usage } : {}),
|
|
213
|
+
...(continued ? { continued } : {}),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const next = fresh.reduce((end, f) => Math.max(end, quoteEnd(doc.text, f.proof.quote ?? "", from)), -1);
|
|
217
|
+
if (next <= from)
|
|
218
|
+
throw new Error(`the answer was cut and ${fresh.length ? "no kept quote places the cut" : "nothing before the cut passed the gate"} (${doc.url}, from ${from})`);
|
|
219
|
+
for (const f of fresh)
|
|
220
|
+
known.push(f.text);
|
|
221
|
+
from = next;
|
|
222
|
+
continued++;
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
//# sourceMappingURL=extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/document/extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The research engine's document side: `Document`, `Finding` and `Proof` (zod, so a ledger
|
|
3
|
+
* parses them back), the reader registry by media, the claim extractor, and the model seam an
|
|
4
|
+
* extractor takes. Who fetched a document and who kept a finding is the engine's and the ledger's.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./body.js";
|
|
7
|
+
export * from "./check.js";
|
|
8
|
+
export * from "./extract.js";
|
|
9
|
+
export * from "./model.js";
|
|
10
|
+
export * from "./readers.js";
|
|
11
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The research engine's document side: `Document`, `Finding` and `Proof` (zod, so a ledger
|
|
3
|
+
* parses them back), the reader registry by media, the claim extractor, and the model seam an
|
|
4
|
+
* extractor takes. Who fetched a document and who kept a finding is the engine's and the ledger's.
|
|
5
|
+
*/
|
|
6
|
+
export * from "./body.js";
|
|
7
|
+
export * from "./check.js";
|
|
8
|
+
export * from "./extract.js";
|
|
9
|
+
export * from "./model.js";
|
|
10
|
+
export * from "./readers.js";
|
|
11
|
+
export * from "./types.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/document/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The model seam an extractor uses: `@wrenautomation/models`' `Model`, of which an extractor
|
|
3
|
+
* calls only `ask` and reads only `id`. A model whose output cap cut the answer throws models'
|
|
4
|
+
* `Cut` with the text so far: the claim extractor keeps what is whole and asks again for the rest.
|
|
5
|
+
*/
|
|
6
|
+
import type { Model as FullModel } from "@wrenautomation/models";
|
|
7
|
+
export type { Answer, Usage } from "@wrenautomation/models";
|
|
8
|
+
export { addUsage, Cut, isCut } from "@wrenautomation/models";
|
|
9
|
+
/** What an extractor needs of a model: any `@wrenautomation/models` model meets it. */
|
|
10
|
+
export type Model = Pick<FullModel, "id" | "ask">;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/document/model.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Document, type Media } from "./types.js";
|
|
2
|
+
/** What a source fetched for one URL. `text` for markup and text, `bytes` for a file. */
|
|
3
|
+
export interface ReadInput {
|
|
4
|
+
url: string;
|
|
5
|
+
text?: string;
|
|
6
|
+
bytes?: Uint8Array;
|
|
7
|
+
/** What the source already knows: a hit's title, channel, date, length. */
|
|
8
|
+
title?: string;
|
|
9
|
+
channel?: string;
|
|
10
|
+
date?: string;
|
|
11
|
+
length?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface Reader {
|
|
14
|
+
media: Media;
|
|
15
|
+
/** Null when the input cannot be read as this media (a scanned PDF, an empty page). */
|
|
16
|
+
read(input: ReadInput): Promise<Document | null>;
|
|
17
|
+
}
|
|
18
|
+
export declare const htmlReader: Reader;
|
|
19
|
+
export declare const pdfReader: Reader;
|
|
20
|
+
export declare const textReader: Reader;
|
|
21
|
+
/** The readers by media. A caller adds its own (`video`, `audio`) or swaps one. */
|
|
22
|
+
export declare const defaultReaders: () => Map<Media, Reader>;
|
|
23
|
+
/** The media of a fetched body, from its content type first, then the URL's extension. */
|
|
24
|
+
export declare function mediaOf(contentType: string | null | undefined, url: string): Media | null;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A reader turns what a source fetched (markup, bytes or text) into one `Document`. The
|
|
3
|
+
* registry is keyed by media; a run picks the reader by the content type it got. Three ship
|
|
4
|
+
* here: html, pdf, text. Video and audio readers need tools and keys, so the caller registers
|
|
5
|
+
* those (the readers over yt-dlp in `@wrenautomation/read/video` return a `Document` directly).
|
|
6
|
+
*/
|
|
7
|
+
import { pdfText, readPage, tidy } from "@wrenautomation/read/text";
|
|
8
|
+
import { Media as MediaShape } from "./types.js";
|
|
9
|
+
const free = (input) => {
|
|
10
|
+
const out = {};
|
|
11
|
+
if (input.title)
|
|
12
|
+
out.title = input.title;
|
|
13
|
+
if (input.channel)
|
|
14
|
+
out.channel = input.channel;
|
|
15
|
+
if (input.date)
|
|
16
|
+
out.date = input.date;
|
|
17
|
+
if (input.length !== undefined)
|
|
18
|
+
out.length = input.length;
|
|
19
|
+
return out;
|
|
20
|
+
};
|
|
21
|
+
const textOf = (input) => input.text ?? (input.bytes ? new TextDecoder().decode(input.bytes) : "");
|
|
22
|
+
export const htmlReader = {
|
|
23
|
+
media: "html",
|
|
24
|
+
async read(input) {
|
|
25
|
+
const page = readPage(textOf(input), input.url);
|
|
26
|
+
if (!page.text.trim())
|
|
27
|
+
return null;
|
|
28
|
+
return {
|
|
29
|
+
...free(input),
|
|
30
|
+
...(page.title ? { title: page.title } : {}),
|
|
31
|
+
url: input.url,
|
|
32
|
+
media: "html",
|
|
33
|
+
text: page.text,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
export const pdfReader = {
|
|
38
|
+
media: "pdf",
|
|
39
|
+
async read(input) {
|
|
40
|
+
if (!input.bytes)
|
|
41
|
+
return null;
|
|
42
|
+
const text = await pdfText(input.bytes);
|
|
43
|
+
return text ? { ...free(input), url: input.url, media: "pdf", text } : null;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
export const textReader = {
|
|
47
|
+
media: "text",
|
|
48
|
+
async read(input) {
|
|
49
|
+
const text = tidy(textOf(input));
|
|
50
|
+
return text ? { ...free(input), url: input.url, media: "text", text } : null;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
/** The readers by media. A caller adds its own (`video`, `audio`) or swaps one. */
|
|
54
|
+
export const defaultReaders = () => new Map([htmlReader, pdfReader, textReader].map((r) => [r.media, r]));
|
|
55
|
+
/** The media of a fetched body, from its content type first, then the URL's extension. */
|
|
56
|
+
export function mediaOf(contentType, url) {
|
|
57
|
+
const type = (contentType ?? "").split(";")[0]?.trim().toLowerCase() ?? "";
|
|
58
|
+
if (type === "application/pdf")
|
|
59
|
+
return "pdf";
|
|
60
|
+
if (type === "text/html" || type === "application/xhtml+xml")
|
|
61
|
+
return "html";
|
|
62
|
+
if (type.startsWith("text/"))
|
|
63
|
+
return "text";
|
|
64
|
+
if (type.startsWith("video/"))
|
|
65
|
+
return "video";
|
|
66
|
+
if (type.startsWith("audio/"))
|
|
67
|
+
return "audio";
|
|
68
|
+
let path = "";
|
|
69
|
+
try {
|
|
70
|
+
path = new URL(url).pathname.toLowerCase();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
if (path.endsWith(".pdf"))
|
|
76
|
+
return "pdf";
|
|
77
|
+
if (/\.(txt|md|csv)$/.test(path))
|
|
78
|
+
return "text";
|
|
79
|
+
if (/\.(mp4|webm|mov|mkv)$/.test(path))
|
|
80
|
+
return "video";
|
|
81
|
+
if (/\.(mp3|m4a|wav|ogg)$/.test(path))
|
|
82
|
+
return "audio";
|
|
83
|
+
if (!type || type === "application/octet-stream")
|
|
84
|
+
return null;
|
|
85
|
+
return MediaShape.safeParse(type).success ? type : null;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=readers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readers.js","sourceRoot":"","sources":["../../src/document/readers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the research engine reads and keeps: a document (one URL read whole), a finding (one
|
|
3
|
+
* fact drawn from it) and its proof (where in the document). zod shapes, because a ledger
|
|
4
|
+
* parses them back. Who found it (`from`, `by`) is the ledger's envelope, not the finding's.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
export declare const Media: z.ZodEnum<{
|
|
8
|
+
html: "html";
|
|
9
|
+
pdf: "pdf";
|
|
10
|
+
text: "text";
|
|
11
|
+
video: "video";
|
|
12
|
+
audio: "audio";
|
|
13
|
+
}>;
|
|
14
|
+
export type Media = z.infer<typeof Media>;
|
|
15
|
+
/** One URL, read whole: a page's text, a video's markdown. The same free fields a hit shows. */
|
|
16
|
+
export declare const Document: z.ZodObject<{
|
|
17
|
+
url: z.ZodString;
|
|
18
|
+
media: z.ZodEnum<{
|
|
19
|
+
html: "html";
|
|
20
|
+
pdf: "pdf";
|
|
21
|
+
text: "text";
|
|
22
|
+
video: "video";
|
|
23
|
+
audio: "audio";
|
|
24
|
+
}>;
|
|
25
|
+
title: z.ZodOptional<z.ZodString>;
|
|
26
|
+
text: z.ZodString;
|
|
27
|
+
channel: z.ZodOptional<z.ZodString>;
|
|
28
|
+
date: z.ZodOptional<z.ZodString>;
|
|
29
|
+
length: z.ZodOptional<z.ZodNumber>;
|
|
30
|
+
}, z.core.$strip>;
|
|
31
|
+
export type Document = z.infer<typeof Document>;
|
|
32
|
+
/** Where a finding rests: the exact words, a second into a recording, or a frame of it. */
|
|
33
|
+
export declare const Proof: z.ZodObject<{
|
|
34
|
+
url: z.ZodString;
|
|
35
|
+
quote: z.ZodOptional<z.ZodString>;
|
|
36
|
+
timestamp: z.ZodOptional<z.ZodNumber>;
|
|
37
|
+
frame: z.ZodOptional<z.ZodString>;
|
|
38
|
+
}, z.core.$strip>;
|
|
39
|
+
export type Proof = z.infer<typeof Proof>;
|
|
40
|
+
/**
|
|
41
|
+
* One fact with its proof. `kind` names the extractor's sort of finding: `claim` in v1. Three
|
|
42
|
+
* signs this is too thin, to watch for: a finding needs two proofs, a proof needs a span inside
|
|
43
|
+
* a quote, a finding points at another finding.
|
|
44
|
+
*/
|
|
45
|
+
export declare const Finding: z.ZodObject<{
|
|
46
|
+
kind: z.ZodString;
|
|
47
|
+
text: z.ZodString;
|
|
48
|
+
proof: z.ZodObject<{
|
|
49
|
+
url: z.ZodString;
|
|
50
|
+
quote: z.ZodOptional<z.ZodString>;
|
|
51
|
+
timestamp: z.ZodOptional<z.ZodNumber>;
|
|
52
|
+
frame: z.ZodOptional<z.ZodString>;
|
|
53
|
+
}, z.core.$strip>;
|
|
54
|
+
}, z.core.$strip>;
|
|
55
|
+
export type Finding = z.infer<typeof Finding>;
|
|
56
|
+
/** How much of the ask one document served: rich, thin, off (another subject) or empty (no text). */
|
|
57
|
+
export declare const Quality: z.ZodEnum<{
|
|
58
|
+
rich: "rich";
|
|
59
|
+
thin: "thin";
|
|
60
|
+
off: "off";
|
|
61
|
+
empty: "empty";
|
|
62
|
+
}>;
|
|
63
|
+
export type Quality = z.infer<typeof Quality>;
|
|
64
|
+
/**
|
|
65
|
+
* The extractor's word on one document after its findings: the quality, and the gap, one
|
|
66
|
+
* sentence on what the ask still lacks after this document. A filter's report to the agent,
|
|
67
|
+
* never a query: the agent reads the round's verdicts before it picks the edge.
|
|
68
|
+
*/
|
|
69
|
+
export declare const Verdict: z.ZodObject<{
|
|
70
|
+
quality: z.ZodEnum<{
|
|
71
|
+
rich: "rich";
|
|
72
|
+
thin: "thin";
|
|
73
|
+
off: "off";
|
|
74
|
+
empty: "empty";
|
|
75
|
+
}>;
|
|
76
|
+
gap: z.ZodOptional<z.ZodString>;
|
|
77
|
+
}, z.core.$strip>;
|
|
78
|
+
export type Verdict = z.infer<typeof Verdict>;
|
|
79
|
+
/**
|
|
80
|
+
* What several findings say together, in the agent's words: one sentence, the findings it
|
|
81
|
+
* rests on, and how many distinct sites they come from (counted by code from the findings'
|
|
82
|
+
* proofs, never claimed). A pattern on one site is a claim; the count is what makes it a
|
|
83
|
+
* pattern. A reader judges the sentence; the count tells how far to trust it.
|
|
84
|
+
*/
|
|
85
|
+
export declare const Pattern: z.ZodObject<{
|
|
86
|
+
text: z.ZodString;
|
|
87
|
+
findings: z.ZodArray<z.ZodString>;
|
|
88
|
+
sources: z.ZodNumber;
|
|
89
|
+
}, z.core.$strip>;
|
|
90
|
+
export type Pattern = z.infer<typeof Pattern>;
|
|
91
|
+
/** The host of a proof's URL, lower case, without `www.`; the URL itself when it is not one. */
|
|
92
|
+
export declare const hostOf: (url: string) => string;
|
|
93
|
+
/** A pattern over findings: the source count is the distinct hosts of their proofs. */
|
|
94
|
+
export declare function patternOf(text: string, findings: readonly {
|
|
95
|
+
id: string;
|
|
96
|
+
proof: {
|
|
97
|
+
url: string;
|
|
98
|
+
};
|
|
99
|
+
}[]): Pattern;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the research engine reads and keeps: a document (one URL read whole), a finding (one
|
|
3
|
+
* fact drawn from it) and its proof (where in the document). zod shapes, because a ledger
|
|
4
|
+
* parses them back. Who found it (`from`, `by`) is the ledger's envelope, not the finding's.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
export const Media = z.enum(["html", "pdf", "text", "video", "audio"]);
|
|
8
|
+
/** One URL, read whole: a page's text, a video's markdown. The same free fields a hit shows. */
|
|
9
|
+
export const Document = z.object({
|
|
10
|
+
url: z.string().min(1),
|
|
11
|
+
media: Media,
|
|
12
|
+
title: z.string().optional(),
|
|
13
|
+
/** All of it: never cut here. */
|
|
14
|
+
text: z.string(),
|
|
15
|
+
channel: z.string().optional(),
|
|
16
|
+
/** ISO date, when the document says one. */
|
|
17
|
+
date: z.string().optional(),
|
|
18
|
+
/** Seconds, for video and audio. */
|
|
19
|
+
length: z.number().nonnegative().optional(),
|
|
20
|
+
});
|
|
21
|
+
/** Where a finding rests: the exact words, a second into a recording, or a frame of it. */
|
|
22
|
+
export const Proof = z
|
|
23
|
+
.object({
|
|
24
|
+
url: z.string().min(1),
|
|
25
|
+
/** Copied character for character from the document. */
|
|
26
|
+
quote: z.string().min(1).optional(),
|
|
27
|
+
/** Seconds into a video or audio. */
|
|
28
|
+
timestamp: z.number().nonnegative().optional(),
|
|
29
|
+
/** A frame's stamp (`mm:ss`) of a watched video. */
|
|
30
|
+
frame: z.string().min(1).optional(),
|
|
31
|
+
})
|
|
32
|
+
.refine((p) => p.quote !== undefined || p.timestamp !== undefined || p.frame !== undefined, {
|
|
33
|
+
message: "a proof needs a quote, a timestamp or a frame",
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* One fact with its proof. `kind` names the extractor's sort of finding: `claim` in v1. Three
|
|
37
|
+
* signs this is too thin, to watch for: a finding needs two proofs, a proof needs a span inside
|
|
38
|
+
* a quote, a finding points at another finding.
|
|
39
|
+
*/
|
|
40
|
+
export const Finding = z.object({
|
|
41
|
+
kind: z.string().min(1),
|
|
42
|
+
/** One plain sentence. */
|
|
43
|
+
text: z.string().min(1),
|
|
44
|
+
proof: Proof,
|
|
45
|
+
});
|
|
46
|
+
/** How much of the ask one document served: rich, thin, off (another subject) or empty (no text). */
|
|
47
|
+
export const Quality = z.enum(["rich", "thin", "off", "empty"]);
|
|
48
|
+
/**
|
|
49
|
+
* The extractor's word on one document after its findings: the quality, and the gap, one
|
|
50
|
+
* sentence on what the ask still lacks after this document. A filter's report to the agent,
|
|
51
|
+
* never a query: the agent reads the round's verdicts before it picks the edge.
|
|
52
|
+
*/
|
|
53
|
+
export const Verdict = z.object({
|
|
54
|
+
quality: Quality,
|
|
55
|
+
gap: z.string().min(1).optional(),
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* What several findings say together, in the agent's words: one sentence, the findings it
|
|
59
|
+
* rests on, and how many distinct sites they come from (counted by code from the findings'
|
|
60
|
+
* proofs, never claimed). A pattern on one site is a claim; the count is what makes it a
|
|
61
|
+
* pattern. A reader judges the sentence; the count tells how far to trust it.
|
|
62
|
+
*/
|
|
63
|
+
export const Pattern = z.object({
|
|
64
|
+
/** One plain sentence. */
|
|
65
|
+
text: z.string().min(1),
|
|
66
|
+
/** Ids of the findings it rests on, in the ledger that holds them. */
|
|
67
|
+
findings: z.array(z.string().min(1)).min(1),
|
|
68
|
+
/** Distinct hosts across those findings' proofs. */
|
|
69
|
+
sources: z.number().int().positive(),
|
|
70
|
+
});
|
|
71
|
+
/** The host of a proof's URL, lower case, without `www.`; the URL itself when it is not one. */
|
|
72
|
+
export const hostOf = (url) => {
|
|
73
|
+
try {
|
|
74
|
+
return new URL(url).hostname.toLowerCase().replace(/^www\./, "");
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return url;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
/** A pattern over findings: the source count is the distinct hosts of their proofs. */
|
|
81
|
+
export function patternOf(text, findings) {
|
|
82
|
+
const sentence = text.replace(/\s+/g, " ").trim();
|
|
83
|
+
if (!sentence)
|
|
84
|
+
throw new Error("a pattern needs a sentence");
|
|
85
|
+
if (!findings.length)
|
|
86
|
+
throw new Error("a pattern needs at least one finding");
|
|
87
|
+
return {
|
|
88
|
+
text: sentence,
|
|
89
|
+
findings: findings.map((f) => f.id),
|
|
90
|
+
sources: new Set(findings.map((f) => hostOf(f.proof.url))).size,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/document/types.ts"],"names":[],"mappings":"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"}
|