infobroker 2026.8.24 → 2026.9.2
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 +83 -36
- package/dist/batch.d.ts +34 -0
- package/dist/batch.js +74 -0
- package/dist/batch.js.map +1 -0
- package/dist/bibtex.d.ts +11 -0
- package/dist/bibtex.js +44 -0
- package/dist/bibtex.js.map +1 -0
- package/dist/cite.d.ts +9 -0
- package/dist/cite.js +85 -0
- package/dist/cite.js.map +1 -0
- package/dist/config.js +51 -0
- package/dist/config.js.map +1 -1
- package/dist/corroborate.d.ts +1 -0
- package/dist/corroborate.js +111 -22
- package/dist/corroborate.js.map +1 -1
- package/dist/datetime.d.ts +7 -0
- package/dist/datetime.js +94 -0
- package/dist/datetime.js.map +1 -0
- package/dist/deep-search.d.ts +37 -0
- package/dist/deep-search.js +85 -0
- package/dist/deep-search.js.map +1 -0
- package/dist/expand.d.ts +1 -0
- package/dist/expand.js +31 -0
- package/dist/expand.js.map +1 -0
- package/dist/hedge.d.ts +18 -0
- package/dist/hedge.js +110 -0
- package/dist/hedge.js.map +1 -0
- package/dist/index.js +404 -96
- package/dist/index.js.map +1 -1
- package/dist/kb.d.ts +3 -1
- package/dist/kb.js +14 -3
- package/dist/kb.js.map +1 -1
- package/dist/rerank.d.ts +10 -0
- package/dist/rerank.js +107 -0
- package/dist/rerank.js.map +1 -0
- package/dist/truncate.d.ts +7 -0
- package/dist/truncate.js +27 -0
- package/dist/truncate.js.map +1 -0
- package/dist/types.d.ts +23 -0
- package/package.json +5 -7
package/README.md
CHANGED
|
@@ -25,7 +25,8 @@ README DESIGN:
|
|
|
25
25
|
Structure: Hero → North Star → Quick Start → MCP Server (§3 features) →
|
|
26
26
|
Skills → Providers → Configuration → How It Compares → Contribute →
|
|
27
27
|
License → Spec. No other ordering. Canonical h2 headings are enforced
|
|
28
|
-
by validate-readme.
|
|
28
|
+
by validate-readme. A TOC listing the canonical h2 sections follows the
|
|
29
|
+
Hero; validate-readme enforces TOC↔heading sync.
|
|
29
30
|
|
|
30
31
|
Audience split: §2 is for developers who want to start the server.
|
|
31
32
|
§3 describes what users can do with it. The Skills section (§3.5)
|
|
@@ -85,7 +86,7 @@ README DESIGN:
|
|
|
85
86
|
document, or a changelog. The complete tool inventory lives in the
|
|
86
87
|
feature taxonomy (§D of infobroker.md), which the README links to.
|
|
87
88
|
Tool names appear in prose only as shorthand in backticks where a
|
|
88
|
-
feature is introduced (e.g. `
|
|
89
|
+
feature is introduced (e.g. `manage_kb`), never as a bare list.
|
|
89
90
|
|
|
90
91
|
Validator. Rules marked "(validate-readme)" SHALL be checked by
|
|
91
92
|
scripts/validate-readme.ts. Other rules are enforced by author/AI
|
|
@@ -122,6 +123,17 @@ sources to separate established facts from contested claims. Bundled
|
|
|
122
123
|
client skills transform raw research into polished writing. Free first.
|
|
123
124
|
Privacy always.
|
|
124
125
|
|
|
126
|
+
- [North Star](#north-star)
|
|
127
|
+
- [Quick Start](#quick-start)
|
|
128
|
+
- [MCP Server](#mcp-server)
|
|
129
|
+
- [Skills](#skills)
|
|
130
|
+
- [Providers](#providers)
|
|
131
|
+
- [Configuration](#configuration)
|
|
132
|
+
- [How It Compares](#how-it-compares)
|
|
133
|
+
- [Contribute](#contribute)
|
|
134
|
+
- [License](#license)
|
|
135
|
+
- [Spec](#spec)
|
|
136
|
+
|
|
125
137
|
## North Star
|
|
126
138
|
|
|
127
139
|
Infobroker is the [Bothan Spynet](https://starwars.fandom.com/wiki/Bothan_Spynet/Legends) as a tool — a decentralized intelligence
|
|
@@ -179,7 +191,7 @@ Requirements: Node.js 20+.
|
|
|
179
191
|
|
|
180
192
|
## MCP Server
|
|
181
193
|
|
|
182
|
-
Your research backend.
|
|
194
|
+
Your research backend. Seven tools, twenty providers, one
|
|
183
195
|
corroboration engine. The complete feature inventory is documented in the
|
|
184
196
|
[feature taxonomy](infobroker.md#d-appendix-feature-taxonomy) in the
|
|
185
197
|
spec.
|
|
@@ -188,26 +200,43 @@ spec.
|
|
|
188
200
|
|
|
189
201
|
> "Search for the location of the second Death Star."
|
|
190
202
|
> "Find scholarly papers on hyperspace travel theories."
|
|
203
|
+
> "Search the latest astromech specs and show me the passages that answer: does the R2 unit pre-date the Clone Wars?"
|
|
191
204
|
|
|
192
205
|
`web_search` sends one query to every provider that can answer it. Search
|
|
193
206
|
across DuckDuckGo, Wikipedia, academic databases, news, code repositories —
|
|
194
|
-
or describe your task and the server picks the best source.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
207
|
+
or describe your task and the server picks the best source. Pass an array of
|
|
208
|
+
queries to batch several searches in one call. Ask for a deep read and it
|
|
209
|
+
fetches the top results and ranks each page's passages against your query,
|
|
210
|
+
so you get the specific text that answers the question instead of links.
|
|
211
|
+
Failed providers fall back silently through a configurable chain so you get
|
|
212
|
+
results, not error messages. Other search tools lock you to one engine;
|
|
213
|
+
Infobroker routes every query to the right provider and keeps going when one
|
|
198
214
|
fails.
|
|
199
215
|
|
|
200
216
|
### Content Extraction
|
|
201
217
|
|
|
202
218
|
> "Fetch the article on the Battle of Yavin and summarize it."
|
|
203
219
|
> "Get the text of that page about the Death Star plans."
|
|
220
|
+
> "Where, in that report, does it mention the reactor core?"
|
|
204
221
|
|
|
205
222
|
`fetch_page` hands any URL to Jina Reader, which renders it as clean
|
|
206
223
|
Markdown optimized for LLM consumption. Falls back to native HTTP when
|
|
207
224
|
Jina is throttled. Wikipedia and Internet Archive have dedicated
|
|
208
|
-
renderers for source-specific extraction.
|
|
209
|
-
|
|
210
|
-
|
|
225
|
+
renderers for source-specific extraction. Ask a page a question — pass
|
|
226
|
+
`question` to `fetch_page` and it returns the passages that answer it, each
|
|
227
|
+
scored and ranked, instead of the whole document. Built-in web fetchers
|
|
228
|
+
return raw HTML; Infobroker gives you clean, readable content from any
|
|
229
|
+
source — ready for summarization or analysis. Fetch also reports the page's
|
|
230
|
+
last-updated date when it can determine one, so you know how current your
|
|
231
|
+
source is.
|
|
232
|
+
|
|
233
|
+
### Citations
|
|
234
|
+
|
|
235
|
+
> "Give me BibTeX references for papers on hyperdrive field dynamics."
|
|
236
|
+
|
|
237
|
+
`get_citations` searches scholarly sources and returns each reference as a formatted
|
|
238
|
+
BibTeX entry with its fields — title, authors, year, venue, and URL — ready
|
|
239
|
+
to paste into a reference list.
|
|
211
240
|
|
|
212
241
|
### Provider Intelligence
|
|
213
242
|
|
|
@@ -217,7 +246,7 @@ ready for summarization or analysis.
|
|
|
217
246
|
The server knows its own capabilities. `web_search` auto-selects the
|
|
218
247
|
best backend for your task, weighing capability, quota, and latency —
|
|
219
248
|
or routes by your intent when you ask for privacy, speed, or free-only
|
|
220
|
-
sources. `
|
|
249
|
+
sources. `inspect_providers` surfaces every configured source and drills into a
|
|
221
250
|
single provider's uptime and error history. No other search MCP server
|
|
222
251
|
gives you operational visibility into every backend.
|
|
223
252
|
|
|
@@ -226,16 +255,19 @@ gives you operational visibility into every backend.
|
|
|
226
255
|
> "Verify whether the Empire really destroyed Alderaan."
|
|
227
256
|
> "Find the consensus on who fired first — Han or Greedo."
|
|
228
257
|
|
|
229
|
-
`
|
|
230
|
-
search across active providers
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
258
|
+
`verify_claims` runs a multi-pass truth-finding loop: broad
|
|
259
|
+
search across all of your active providers — search engines,
|
|
260
|
+
encyclopedias, and scholarly indexes alike — then claim extraction,
|
|
261
|
+
cross-source reconciliation, and targeted follow-up for gaps, dispatched
|
|
262
|
+
in parallel. Claims corroborated across independent sources score high
|
|
263
|
+
confidence, weighted by each source's authority; every source is bound
|
|
264
|
+
to the claim it supports. Contradictions are surfaced with all
|
|
265
|
+
perspectives. Gaps trigger refined queries. It also remembers: prior
|
|
266
|
+
findings in your knowledge base participate as corroborating sources
|
|
267
|
+
before it queries the network. You get a structured report — confirmed,
|
|
268
|
+
contested, and unverified findings — with source provenance, per-source
|
|
269
|
+
claims, and confidence scores. Every other search tool returns a list of
|
|
270
|
+
links; Infobroker finds the truth and tells you how sure it is.
|
|
239
271
|
|
|
240
272
|
### Knowledge Base
|
|
241
273
|
|
|
@@ -243,14 +275,16 @@ Infobroker finds the truth and tells you how sure it is.
|
|
|
243
275
|
> "Ingest this article so it's cached for next time."
|
|
244
276
|
|
|
245
277
|
Every search, fetch, and corroboration run is cached in a local knowledge
|
|
246
|
-
base. `
|
|
278
|
+
base. `manage_kb` checks the cache before hitting external providers — only
|
|
247
279
|
falling back to the network when the cached results aren't fresh enough
|
|
248
280
|
or relevant enough. Its actions ingest new text or a URL by hand, report
|
|
249
281
|
what's cached, and remove content. Content is age-scored, expired on a
|
|
250
|
-
freshness schedule, and deduplicated by source. Beyond the cache, `
|
|
282
|
+
freshness schedule, and deduplicated by source. Beyond the cache, `manage_kb`
|
|
251
283
|
archives the reports you generate: ingest with `source_type: "report"`
|
|
252
|
-
(and default to the knowledge base) and revisit them with `
|
|
253
|
-
`
|
|
284
|
+
(and default to the knowledge base) and revisit them with `manage_kb` list and
|
|
285
|
+
`manage_kb` get, or write them to a local directory instead. Each archived report
|
|
286
|
+
records its source's last-updated date, so you can compare it against the
|
|
287
|
+
live source and refresh only what has actually changed. Other search MCP
|
|
254
288
|
servers re-fetch the same facts every session; Infobroker remembers and
|
|
255
289
|
reuses what it already found.
|
|
256
290
|
|
|
@@ -277,7 +311,7 @@ Quota counters persist to disk and survive restarts. Rate limits are
|
|
|
277
311
|
enforced per-provider, not globally. Configuration is hot-reloadable
|
|
278
312
|
via `reload_config` — change providers, adjust chains, or tweak
|
|
279
313
|
thresholds without dropping connections. `web_search` doubles as
|
|
280
|
-
DuckDuckGo query autocomplete. `
|
|
314
|
+
DuckDuckGo query autocomplete. `inspect_providers` reports the server's build
|
|
281
315
|
health and request stats. You always know what your search server is
|
|
282
316
|
doing and how much capacity remains.
|
|
283
317
|
|
|
@@ -301,10 +335,10 @@ polishes language, and `translation` produces multilingual output.
|
|
|
301
335
|
Gated analysis is the escalation shape. When a question is high-stakes or
|
|
302
336
|
decision-driving, the classify gate routes to the `analysis-loop` skill —
|
|
303
337
|
a disciplined path with confidence-scored findings, source-reliability
|
|
304
|
-
grading, and structured analytic techniques
|
|
305
|
-
research-and-write route. It shares the
|
|
306
|
-
tools but runs its own gated workflow, so you
|
|
307
|
-
leaving the pipeline.
|
|
338
|
+
grading, and structured analytic techniques chosen by fit and named with a
|
|
339
|
+
rationale — rather than the lighter research-and-write route. It shares the
|
|
340
|
+
same primitives and Infobroker tools but runs its own gated workflow, so you
|
|
341
|
+
get the rigor without leaving the pipeline.
|
|
308
342
|
|
|
309
343
|
A single instruction file, `search-preferences.md`, routes your client
|
|
310
344
|
toward these tools: the knowledge base first, external providers only
|
|
@@ -421,7 +455,7 @@ passphrase means the store is unrecoverable by design). Second, the server
|
|
|
421
455
|
never writes a partial file: every save is atomic, and an unrecognized or
|
|
422
456
|
newer store format is never overwritten.
|
|
423
457
|
|
|
424
|
-
The `
|
|
458
|
+
The `manage_kb` tool's `encryption` action is the day-to-day surface for this
|
|
425
459
|
journey, and it never echoes secret material — `generate_key` and `backup`
|
|
426
460
|
return file paths, and `rekey` reads a key file rather than a raw key.
|
|
427
461
|
|
|
@@ -437,8 +471,8 @@ move to a new key without losing content. After re-keying, point
|
|
|
437
471
|
confirm the new key opens the store.
|
|
438
472
|
|
|
439
473
|
```
|
|
440
|
-
|
|
441
|
-
|
|
474
|
+
infobroker_manage_kb action=encryption operation=generate_key key_file=~/.config/infobroker/kb.key
|
|
475
|
+
infobroker_manage_kb action=encryption operation=backup key_file=~/.backup/kb.key.bak
|
|
442
476
|
infobroker_reload_config
|
|
443
477
|
```
|
|
444
478
|
|
|
@@ -481,7 +515,7 @@ override and call `reload_config` to use it immediately.
|
|
|
481
515
|
Two optional keys tune per-provider behavior in `config.json`:
|
|
482
516
|
|
|
483
517
|
- `degraded_latency_ms` — a provider whose recent average latency exceeds
|
|
484
|
-
this many milliseconds is reported `degraded` by the `
|
|
518
|
+
this many milliseconds is reported `degraded` by the `inspect_providers` health
|
|
485
519
|
action, even while reachable. A global `output.degraded_latency_ms` acts
|
|
486
520
|
as the fallback when a provider omits its own.
|
|
487
521
|
- `resells` — set `true` on aggregator/reseller backends (search engines
|
|
@@ -491,6 +525,19 @@ Two optional keys tune per-provider behavior in `config.json`:
|
|
|
491
525
|
sources (Wikipedia, arXiv) leave it empty because the page is the
|
|
492
526
|
origin.
|
|
493
527
|
|
|
528
|
+
### Hedged fallback
|
|
529
|
+
|
|
530
|
+
`web_search` and `fetch_page` fall back with a hedge instead of waiting
|
|
531
|
+
out a slow provider's full timeout: the primary (first-choice) provider
|
|
532
|
+
runs alone for a latency-derived window, then the remaining providers
|
|
533
|
+
race and the first result wins. The common path uses one provider call;
|
|
534
|
+
the hedge fires only when the primary is slow or failing. `fetch_page`
|
|
535
|
+
additionally prefers the primary renderer in a short grace window so a
|
|
536
|
+
marginally slow `jina` is not displaced by a lower-quality `native_fetch`.
|
|
537
|
+
Tune the window with `output.hedge_enabled`, `hedge_min_delay_ms`,
|
|
538
|
+
`hedge_max_delay_ms`, and `hedge_grace_ms`; set `hedge_enabled` to
|
|
539
|
+
`false` for the sequential chain.
|
|
540
|
+
|
|
494
541
|
## How It Compares
|
|
495
542
|
|
|
496
543
|
| Tool name | What you're used to | How Infobroker differs |
|
|
@@ -509,7 +556,7 @@ contradiction, and gaps. The bundled skills close the loop from raw
|
|
|
509
556
|
research to finished writing. One server. Every source. Research that
|
|
510
557
|
delivers.
|
|
511
558
|
|
|
512
|
-
Last updated: 2026-
|
|
559
|
+
Last updated: 2026-09-02.
|
|
513
560
|
|
|
514
561
|
## Contribute
|
|
515
562
|
|
|
@@ -539,7 +586,7 @@ terms and API keys.
|
|
|
539
586
|
## Spec
|
|
540
587
|
|
|
541
588
|
The server is built from a single source specification, `infobroker.md`
|
|
542
|
-
(v2026.
|
|
589
|
+
(v2026.09.02), which defines every requirement and the gates that verify it.
|
|
543
590
|
Each requirement traces to an implementation file, and `npm run check`
|
|
544
591
|
reconciles the code, the spec, and this README so what is documented is what
|
|
545
592
|
the server actually delivers.
|
package/dist/batch.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare const MAX_BATCH_INPUTS = 5;
|
|
2
|
+
export declare function capInputs(items: string[]): string[];
|
|
3
|
+
interface EnvelopeOk {
|
|
4
|
+
status: "ok";
|
|
5
|
+
provider: string;
|
|
6
|
+
results: Array<{
|
|
7
|
+
url?: string;
|
|
8
|
+
[k: string]: unknown;
|
|
9
|
+
}>;
|
|
10
|
+
meta?: Record<string, unknown>;
|
|
11
|
+
truncated?: boolean;
|
|
12
|
+
output_path?: string;
|
|
13
|
+
}
|
|
14
|
+
interface EnvelopeError {
|
|
15
|
+
status: "error";
|
|
16
|
+
provider: string;
|
|
17
|
+
error?: unknown;
|
|
18
|
+
}
|
|
19
|
+
type Envelope = EnvelopeOk | EnvelopeError;
|
|
20
|
+
export declare function parseEnvelope(text: string): Envelope;
|
|
21
|
+
export interface PerInputMeta {
|
|
22
|
+
query: string;
|
|
23
|
+
provider: string;
|
|
24
|
+
status: "ok" | "error";
|
|
25
|
+
result_count: number;
|
|
26
|
+
truncated?: boolean;
|
|
27
|
+
output_path?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface BatchItem {
|
|
30
|
+
query: string;
|
|
31
|
+
envelope: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function mergeItems(items: BatchItem[]): Envelope;
|
|
34
|
+
export {};
|
package/dist/batch.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// @implements REQ-020 REQ-021
|
|
2
|
+
export const MAX_BATCH_INPUTS = 5;
|
|
3
|
+
export function capInputs(items) {
|
|
4
|
+
return items.slice(0, MAX_BATCH_INPUTS);
|
|
5
|
+
}
|
|
6
|
+
export function parseEnvelope(text) {
|
|
7
|
+
const t = text.trim();
|
|
8
|
+
if (t.startsWith("[OK]")) {
|
|
9
|
+
return JSON.parse(t.slice(4));
|
|
10
|
+
}
|
|
11
|
+
if (t.startsWith("[ERROR]")) {
|
|
12
|
+
return JSON.parse(t.slice(7));
|
|
13
|
+
}
|
|
14
|
+
throw new Error("unrecognized envelope prefix");
|
|
15
|
+
}
|
|
16
|
+
function dedupeByUrl(results) {
|
|
17
|
+
const seen = new Set();
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const r of results) {
|
|
20
|
+
const key = r.url || "";
|
|
21
|
+
if (key && seen.has(key))
|
|
22
|
+
continue;
|
|
23
|
+
if (key)
|
|
24
|
+
seen.add(key);
|
|
25
|
+
out.push(r);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
// Merge per-input envelopes into a single envelope. Results are flattened in
|
|
30
|
+
// input order and URL-deduplicated; per-input provenance is recorded in meta.
|
|
31
|
+
export function mergeItems(items) {
|
|
32
|
+
const perQuery = [];
|
|
33
|
+
const okItems = [];
|
|
34
|
+
let firstError;
|
|
35
|
+
for (const item of items) {
|
|
36
|
+
let env;
|
|
37
|
+
try {
|
|
38
|
+
env = parseEnvelope(item.envelope);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
env = { status: "error", provider: "system", error: { code: "internal_error", message: "batch item failed to parse" } };
|
|
42
|
+
}
|
|
43
|
+
if (env.status === "ok") {
|
|
44
|
+
okItems.push({ query: item.query, env });
|
|
45
|
+
perQuery.push({
|
|
46
|
+
query: item.query,
|
|
47
|
+
provider: env.provider,
|
|
48
|
+
status: "ok",
|
|
49
|
+
result_count: env.results.length,
|
|
50
|
+
...(env.truncated ? { truncated: true, output_path: env.output_path } : {}),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
if (!firstError)
|
|
55
|
+
firstError = env;
|
|
56
|
+
perQuery.push({ query: item.query, provider: env.provider, status: "error", result_count: 0 });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (okItems.length === 0) {
|
|
60
|
+
return firstError ?? { status: "error", provider: "none", error: { code: "internal_error", message: "no results" } };
|
|
61
|
+
}
|
|
62
|
+
const firstOk = okItems[0].env;
|
|
63
|
+
const results = dedupeByUrl(okItems.flatMap((o) => o.env.results));
|
|
64
|
+
return {
|
|
65
|
+
status: "ok",
|
|
66
|
+
provider: firstOk.provider,
|
|
67
|
+
results,
|
|
68
|
+
meta: {
|
|
69
|
+
...(firstOk.meta ?? {}),
|
|
70
|
+
per_query: perQuery,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=batch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch.js","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"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"}
|
package/dist/bibtex.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface CitationFields {
|
|
2
|
+
type: "article" | "misc";
|
|
3
|
+
key: string;
|
|
4
|
+
title: string;
|
|
5
|
+
authors: string[];
|
|
6
|
+
year?: string;
|
|
7
|
+
venue?: string;
|
|
8
|
+
url?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function formatBibtex(fields: CitationFields): string;
|
|
11
|
+
export declare function citationFor(title: string, authors: string[], year?: string, venue?: string, url?: string): string;
|
package/dist/bibtex.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
function escapeLatex(text) {
|
|
2
|
+
return text
|
|
3
|
+
.replace(/\\/g, "\\textbackslash{}")
|
|
4
|
+
.replace(/[{}]/g, (c) => (c === "{" ? "\\{" : "\\}"))
|
|
5
|
+
.replace(/([&%$#_])/g, "\\$1")
|
|
6
|
+
.replace(/~/g, "\\textasciitilde{}")
|
|
7
|
+
.replace(/\^/g, "\\textasciicircum{}");
|
|
8
|
+
}
|
|
9
|
+
function citationKey(author, year, title) {
|
|
10
|
+
const last = (author || "").split(/[,\s]+/)[0] || "anon";
|
|
11
|
+
const slug = last.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
12
|
+
const y = year || "nd";
|
|
13
|
+
return `${slug}${y}`;
|
|
14
|
+
}
|
|
15
|
+
export function formatBibtex(fields) {
|
|
16
|
+
const authorField = fields.authors.length > 0
|
|
17
|
+
? fields.authors.map((a) => escapeLatex(a)).join(" and ")
|
|
18
|
+
: undefined;
|
|
19
|
+
const lines = [`@${fields.type}{${escapeLatex(fields.key)},`];
|
|
20
|
+
lines.push(` title = {${escapeLatex(fields.title)}}`);
|
|
21
|
+
if (authorField)
|
|
22
|
+
lines.push(` author = {${authorField}}`);
|
|
23
|
+
if (fields.year)
|
|
24
|
+
lines.push(` year = {${fields.year}}`);
|
|
25
|
+
if (fields.venue)
|
|
26
|
+
lines.push(fields.type === "article" ? ` journal = {${escapeLatex(fields.venue)}}` : ` howpublished = {${escapeLatex(fields.venue)}}`);
|
|
27
|
+
if (fields.url)
|
|
28
|
+
lines.push(` url = {${fields.url}}`);
|
|
29
|
+
lines.push("}");
|
|
30
|
+
return lines.join("\n");
|
|
31
|
+
}
|
|
32
|
+
export function citationFor(title, authors, year, venue, url) {
|
|
33
|
+
const key = citationKey(authors[0], year || undefined, title);
|
|
34
|
+
return formatBibtex({
|
|
35
|
+
type: authors.length > 0 ? "article" : "misc",
|
|
36
|
+
key,
|
|
37
|
+
title,
|
|
38
|
+
authors,
|
|
39
|
+
year,
|
|
40
|
+
venue,
|
|
41
|
+
url,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=bibtex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bibtex.js","sourceRoot":"","sources":["../src/bibtex.ts"],"names":[],"mappings":"AAWA,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI;SACR,OAAO,CAAC,KAAK,EAAE,mBAAmB,CAAC;SACnC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;SACpD,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC;SAC7B,OAAO,CAAC,IAAI,EAAE,oBAAoB,CAAC;SACnC,OAAO,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,WAAW,CAAC,MAAe,EAAE,IAAa,EAAE,KAAc;IACjE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IACzD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC;IACvB,OAAO,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAsB;IACjD,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;QAC3C,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;QACzD,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,KAAK,GAAa,CAAC,IAAI,MAAM,CAAC,IAAI,IAAI,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxE,KAAK,CAAC,IAAI,CAAC,cAAc,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvD,IAAI,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,eAAe,WAAW,GAAG,CAAC,CAAC;IAC3D,IAAI,MAAM,CAAC,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;IACzD,IAAI,MAAM,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,gBAAgB,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,qBAAqB,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3J,IAAI,MAAM,CAAC,GAAG;QAAE,KAAK,CAAC,IAAI,CAAC,YAAY,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;IACtD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,OAAiB,EAAE,IAAa,EAAE,KAAc,EAAE,GAAY;IACvG,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,SAAS,EAAE,KAAK,CAAC,CAAC;IAC9D,OAAO,YAAY,CAAC;QAClB,IAAI,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM;QAC7C,GAAG;QACH,KAAK;QACL,OAAO;QACP,IAAI;QACJ,KAAK;QACL,GAAG;KACJ,CAAC,CAAC;AACL,CAAC"}
|
package/dist/cite.d.ts
ADDED
package/dist/cite.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// @implements REQ-027
|
|
2
|
+
import { infobrokerFetch } from "./http.js";
|
|
3
|
+
import { getEnvVar } from "./config.js";
|
|
4
|
+
import { citationFor } from "./bibtex.js";
|
|
5
|
+
const ARXIV_API = "https://export.arxiv.org/api/query";
|
|
6
|
+
const SS_API = "https://api.semanticscholar.org/graph/v1/paper/search";
|
|
7
|
+
function tagContent(xml, tag) {
|
|
8
|
+
const re = new RegExp(`<${tag}[^>]*>([\\s\\S]*?)</${tag}>`, "i");
|
|
9
|
+
const m = xml.match(re);
|
|
10
|
+
return m ? (m[1] || "").replace(/<[^>]*>/g, "").trim() : "";
|
|
11
|
+
}
|
|
12
|
+
async function searchArxiv(query, limit) {
|
|
13
|
+
const params = new URLSearchParams({ search_query: query, max_results: String(limit) });
|
|
14
|
+
const resp = await infobrokerFetch(`${ARXIV_API}?${params.toString()}`, { providerSlug: "arxiv" });
|
|
15
|
+
if (!resp.ok)
|
|
16
|
+
return [];
|
|
17
|
+
const xml = await resp.text();
|
|
18
|
+
const out = [];
|
|
19
|
+
const entryRe = /<entry>([\s\S]*?)<\/entry>/g;
|
|
20
|
+
let m;
|
|
21
|
+
while ((m = entryRe.exec(xml)) !== null) {
|
|
22
|
+
const entry = m[1];
|
|
23
|
+
const title = tagContent(entry, "title");
|
|
24
|
+
const id = tagContent(entry, "id");
|
|
25
|
+
const published = tagContent(entry, "published");
|
|
26
|
+
const year = published.slice(0, 4);
|
|
27
|
+
const authors = [...entry.matchAll(/<name>([\s\S]*?)<\/name>/g)].map((a) => a[1].trim()).filter(Boolean);
|
|
28
|
+
if (!title || !id)
|
|
29
|
+
continue;
|
|
30
|
+
out.push({
|
|
31
|
+
title,
|
|
32
|
+
url: id,
|
|
33
|
+
year,
|
|
34
|
+
authors,
|
|
35
|
+
venue: "arXiv",
|
|
36
|
+
bibtex: citationFor(title, authors, year, "arXiv", id),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
async function searchSemanticScholar(query, limit) {
|
|
42
|
+
const fields = ["title", "authors", "year", "venue", "externalIds", "url"].join(",");
|
|
43
|
+
const params = new URLSearchParams({ query, limit: String(limit), fields });
|
|
44
|
+
const headers = {};
|
|
45
|
+
const key = getEnvVar("semantic_scholar", "_API_KEY");
|
|
46
|
+
if (key)
|
|
47
|
+
headers["x-api-key"] = key;
|
|
48
|
+
const resp = await infobrokerFetch(`${SS_API}?${params.toString()}`, { providerSlug: "semantic_scholar", headers });
|
|
49
|
+
if (!resp.ok)
|
|
50
|
+
return [];
|
|
51
|
+
const data = (await resp.json());
|
|
52
|
+
return (data.data || []).map((item) => {
|
|
53
|
+
const year = item.year ? String(item.year) : undefined;
|
|
54
|
+
const authors = (item.authors || []).map((a) => a.name);
|
|
55
|
+
return {
|
|
56
|
+
title: item.title,
|
|
57
|
+
url: item.url || `https://api.semanticscholar.org/paper/${item.title}`,
|
|
58
|
+
year,
|
|
59
|
+
authors,
|
|
60
|
+
venue: item.venue,
|
|
61
|
+
bibtex: citationFor(item.title, authors, year, item.venue, undefined),
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export async function searchCitations(query, limit = 8, source) {
|
|
66
|
+
const results = [];
|
|
67
|
+
const wanted = source ? [source] : ["arxiv", "semantic_scholar"];
|
|
68
|
+
for (const s of wanted) {
|
|
69
|
+
if (results.length >= limit)
|
|
70
|
+
break;
|
|
71
|
+
try {
|
|
72
|
+
const batch = s === "arxiv" ? await searchArxiv(query, limit) : await searchSemanticScholar(query, limit);
|
|
73
|
+
for (const r of batch) {
|
|
74
|
+
if (results.length >= limit)
|
|
75
|
+
break;
|
|
76
|
+
results.push(r);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// source unavailable — continue to the next
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return results;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=cite.js.map
|
package/dist/cite.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cite.js","sourceRoot":"","sources":["../src/cite.ts"],"names":[],"mappings":"AAAA,sBAAsB;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAW1C,MAAM,SAAS,GAAG,oCAAoC,CAAC;AACvD,MAAM,MAAM,GAAG,uDAAuD,CAAC;AAEvE,SAAS,UAAU,CAAC,GAAW,EAAE,GAAW;IAC1C,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,uBAAuB,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC;IACjE,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxB,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,KAAa,EAAE,KAAa;IACrD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxF,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,GAAG,SAAS,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC;IACnG,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAE9B,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,6BAA6B,CAAC;IAC9C,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACzC,MAAM,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACnC,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACnC,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzG,IAAI,CAAC,KAAK,IAAI,CAAC,EAAE;YAAE,SAAS;QAC5B,GAAG,CAAC,IAAI,CAAC;YACP,KAAK;YACL,GAAG,EAAE,EAAE;YACP,IAAI;YACJ,OAAO;YACP,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;SACvD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,KAAa,EAAE,KAAa;IAC/D,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrF,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5E,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,MAAM,GAAG,GAAG,SAAS,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;IACtD,IAAI,GAAG;QAAE,OAAO,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,GAAG,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC,CAAC;IACpH,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAE9B,CAAC;IAEF,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACvD,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACxD,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,yCAAyC,IAAI,CAAC,KAAK,EAAE;YACtE,IAAI;YACJ,OAAO;YACP,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC;SACtE,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAa,EAAE,KAAK,GAAG,CAAC,EAAE,MAAe;IAC7E,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IACjE,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK;YAAE,MAAM;QACnC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,qBAAqB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC1G,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK;oBAAE,MAAM;gBACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -152,6 +152,9 @@ function validateConfig(config) {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
|
+
if (config.corroboration?.kb_recall !== undefined && typeof config.corroboration.kb_recall !== "boolean") {
|
|
156
|
+
errors.push("corroboration.kb_recall must be a boolean");
|
|
157
|
+
}
|
|
155
158
|
if (typeof config.output.fallback_depth !== "number" || config.output.fallback_depth < 1) {
|
|
156
159
|
errors.push("output.fallback_depth must be a positive number");
|
|
157
160
|
}
|
|
@@ -161,6 +164,54 @@ function validateConfig(config) {
|
|
|
161
164
|
if (config.output.degraded_latency_ms !== undefined && (typeof config.output.degraded_latency_ms !== "number" || config.output.degraded_latency_ms < 0)) {
|
|
162
165
|
errors.push("output.degraded_latency_ms must be a non-negative number");
|
|
163
166
|
}
|
|
167
|
+
if (config.output.hedge_enabled !== undefined && typeof config.output.hedge_enabled !== "boolean") {
|
|
168
|
+
errors.push("output.hedge_enabled must be a boolean");
|
|
169
|
+
}
|
|
170
|
+
if (config.output.hedge_min_delay_ms !== undefined && (typeof config.output.hedge_min_delay_ms !== "number" || config.output.hedge_min_delay_ms < 0)) {
|
|
171
|
+
errors.push("output.hedge_min_delay_ms must be a non-negative number");
|
|
172
|
+
}
|
|
173
|
+
if (config.output.hedge_max_delay_ms !== undefined && (typeof config.output.hedge_max_delay_ms !== "number" || config.output.hedge_max_delay_ms < config.output.hedge_min_delay_ms)) {
|
|
174
|
+
errors.push("output.hedge_max_delay_ms must be a number no less than hedge_min_delay_ms");
|
|
175
|
+
}
|
|
176
|
+
if (config.output.hedge_grace_ms !== undefined && (typeof config.output.hedge_grace_ms !== "number" || config.output.hedge_grace_ms < 0)) {
|
|
177
|
+
errors.push("output.hedge_grace_ms must be a non-negative number");
|
|
178
|
+
}
|
|
179
|
+
if (config.fetch) {
|
|
180
|
+
if (config.fetch.passage_size !== undefined && (typeof config.fetch.passage_size !== "number" || config.fetch.passage_size < 1)) {
|
|
181
|
+
errors.push("fetch.passage_size must be a positive number");
|
|
182
|
+
}
|
|
183
|
+
if (config.fetch.max_passages !== undefined && (typeof config.fetch.max_passages !== "number" || config.fetch.max_passages < 1)) {
|
|
184
|
+
errors.push("fetch.max_passages must be a positive number");
|
|
185
|
+
}
|
|
186
|
+
if (config.fetch.detect_date !== undefined && typeof config.fetch.detect_date !== "boolean") {
|
|
187
|
+
errors.push("fetch.detect_date must be a boolean");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (config.expand) {
|
|
191
|
+
if (config.expand.max_expansions !== undefined && (typeof config.expand.max_expansions !== "number" || config.expand.max_expansions < 1)) {
|
|
192
|
+
errors.push("expand.max_expansions must be a positive number");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (config.deep) {
|
|
196
|
+
if (config.deep.max_pages !== undefined && (typeof config.deep.max_pages !== "number" || config.deep.max_pages < 1)) {
|
|
197
|
+
errors.push("deep.max_pages must be a positive number");
|
|
198
|
+
}
|
|
199
|
+
if (config.deep.max_total_pages !== undefined && (typeof config.deep.max_total_pages !== "number" || config.deep.max_total_pages < 1)) {
|
|
200
|
+
errors.push("deep.max_total_pages must be a positive number");
|
|
201
|
+
}
|
|
202
|
+
if (config.deep.concurrency !== undefined && (typeof config.deep.concurrency !== "number" || config.deep.concurrency < 1)) {
|
|
203
|
+
errors.push("deep.concurrency must be a positive number");
|
|
204
|
+
}
|
|
205
|
+
if (config.deep.early_exit_score !== undefined && (typeof config.deep.early_exit_score !== "number" || config.deep.early_exit_score < 0 || config.deep.early_exit_score > 1)) {
|
|
206
|
+
errors.push("deep.early_exit_score must be a number between 0 and 1");
|
|
207
|
+
}
|
|
208
|
+
if (config.deep.max_ms !== undefined && (typeof config.deep.max_ms !== "number" || config.deep.max_ms < 1)) {
|
|
209
|
+
errors.push("deep.max_ms must be a positive number");
|
|
210
|
+
}
|
|
211
|
+
if (config.deep.detect_date !== undefined && typeof config.deep.detect_date !== "boolean") {
|
|
212
|
+
errors.push("deep.detect_date must be a boolean");
|
|
213
|
+
}
|
|
214
|
+
}
|
|
164
215
|
if (config.kb) {
|
|
165
216
|
const kb = config.kb;
|
|
166
217
|
if (typeof kb.chunk_size !== "number" || kb.chunk_size < 1) {
|