code wiki / _hdl_build / nx_bulk_ingest.nx
nx_bulk_ingest.nx source
↩ module page · 88 lines · 5487 B
1// nx_bulk_ingest.nx -- the POLITE-FETCH -> BULK-CATALOG HAND-OFF (operator 2026-06-05): "a hand off function
2// when its being polite that if we can download the whole catalog of research papers like arxiv that triggers
3// like we were doing with the nishi search engine and we grow and grow our library so we can self serve more."
4//
5// When the team's polite single-fetch (nx_pipeline_fetch) lands on a page that is actually a HARVESTABLE
6// REPOSITORY (a catalog/listing/OAI-PMH/sitemap), it HANDS OFF to bulk-catalog ingest: walk the catalog,
7// mirror each doc into the Library (content-addressed dedup), so over time the team SELF-SERVES from its own
8// Library and depends on the live web less. Two hard rules from the operator's prior laws:
9// - STAY POLITE: the bulk walk runs at the SAME one-IP polite floor (pf_polite_floor) -- never a blast.
10// - SERVE THE RAISED HANDS: prioritize repositories that fill the gaps the team raised its hand about.
11// Convergent (K dry rounds = catalog exhausted), resumable, never infinite. license_tier: ORIGINAL
12import "nx_pipeline_fetch.nx"
13import "nx_syscalls.nx"
14
15// hand-off decisions
16const BI_SINGLE: i64 = 0 // ordinary page -> normal single fetch
17const BI_BULK: i64 = 1 // harvestable repository + polite capacity -> bulk-catalog ingest
18const BI_DEFER: i64 = 2 // a repository, but no polite spare capacity right now -> come back later
19
20// thresholds (data-driven, not magic-in-logic): a listing page has many links that mostly share one URL shape.
21const BI_REPO_MIN_LINKS: i64 = 20 // fewer than this is not a catalog
22const BI_REPO_PATTERN_PMIL: i64 = 600 // >=60% of links share the catalog's URL pattern -> it's a listing
23const BI_MIN_CAPACITY_PMIL: i64 = 200 // need >=20% spare polite capacity before starting a bulk walk
24
25// REPOSITORY DETECTOR: is this fetched page actually a catalog we can harvest? A known bulk endpoint
26// (OAI-PMH / sitemap.xml / a list API) is definitive; otherwise it's the link-count x pattern-density signal.
27func bi_is_repository(link_count: i64, repeated_pattern_pmil: i64, known_bulk_endpoint: i64) -> i64 {
28 if known_bulk_endpoint == 1 { return 1 }
29 if link_count >= BI_REPO_MIN_LINKS { if repeated_pattern_pmil >= BI_REPO_PATTERN_PMIL { return 1 } }
30 return 0
31}
32
33// THE HAND-OFF TRIGGER: single page -> SINGLE; repository but no polite spare capacity -> DEFER (don't
34// overwhelm the host or our one IP); repository + capacity -> BULK. This is the function the polite fetcher
35// calls on every fetched page.
36func bi_handoff(is_repo: i64, polite_capacity_pmil: i64) -> i64 {
37 if is_repo == 0 { return BI_SINGLE }
38 if polite_capacity_pmil < BI_MIN_CAPACITY_PMIL { return BI_DEFER }
39 return BI_BULK
40}
41
42// PRIORITY: among detected repositories, harvest the ones that fill a RAISED-HAND assignment first (the team
43// flagged "I need research on X"; a repository matching X is worth more). assignment_match in {0,1};
44// gap_severity 0..1000. Score = base + match-bonus + severity. (Feeds the work dispatcher's ordering.)
45func bi_priority(assignment_match: i64, gap_severity_pmil: i64) -> i64 {
46 var s: i64 = 100
47 if assignment_match == 1 { s = s + 1000 } // a raised-hand match dominates
48 s = s + gap_severity_pmil
49 return s
50}
51
52// CONTENT-ADDRESSED DEDUP: skip a doc already mirrored (same content hash present in the Library). Returns 1
53// = skip (already have it), 0 = mirror it. (Composes with the existing content-addressed blob store.)
54func bi_dedup_skip(content_hash: i64, seen: *i64, n_seen: i64) -> i64 {
55 var i: i64 = 0
56 while i < n_seen { if seen[i] == content_hash { return 1 } i = i + 1 }
57 return 0
58}
59
60// POLITE BULK-HARVEST wall-clock: the catalog is walked as a PIPELINE (fetch->extract->mirror) at the SAME
61// one-IP polite floor as ordinary fetch -- bulk does NOT mean fast/rude, it means CONTINUOUS. Reuses the
62// proven pf_team_pipeline so the harvest is polite-optimal by construction.
63func bi_harvest_wall_ms(n_docs: i64, pace: i64, extract_ms: i64, mirror_ms: i64) -> i64 {
64 return pf_team_pipeline(n_docs, pace, extract_ms, mirror_ms)
65}
66// the harvest is POLITE iff its wall never undercuts the irreducible polite floor (n paced fetches).
67func bi_harvest_is_polite(n_docs: i64, pace: i64, extract_ms: i64, mirror_ms: i64) -> i64 {
68 if bi_harvest_wall_ms(n_docs, pace, extract_ms, mirror_ms) >= pf_polite_floor(n_docs, pace) { return 1 }
69 return 0
70}
71
72// CONVERGENCE: the walk stops when K consecutive rounds add nothing new (catalog exhausted) OR a hard backstop
73// is hit -- never infinite. Returns 1 = done.
74func bi_converged(new_this_round: i64, dry_rounds: i64, max_dry: i64, rounds_done: i64, hard_cap: i64) -> i64 {
75 if rounds_done >= hard_cap { return 1 } // backstop: never run forever
76 if new_this_round > 0 { return 0 } // still finding new docs -> keep going
77 if dry_rounds >= max_dry { return 1 } // K dry rounds -> catalog exhausted
78 return 0
79}
80
81// LIBRARY SELF-SERVE GROWTH: after a harvest, how much of the team's information need can now be served from
82// the OWN Library (zero live fetch)? coverage = mirrored / needed, in permil. This is the whole point: it
83// goes UP with every harvest, so the team depends on the live web less and less.
84func bi_self_serve_pmil(mirrored_docs: i64, needed_docs: i64) -> i64 {
85 if needed_docs <= 0 { return 1000 }
86 if mirrored_docs >= needed_docs { return 1000 }
87 return (mirrored_docs * 1000) / needed_docs
88}