recall_guard.dataset.fmp_corpora
recall_guard.dataset.fmp_corpora
FMP-backed calibration corpus builder for honest-model-ranking.
Implements Requirement 11 of the spec: build the IS/OOS calibration corpora
(data/calibration/is_memorized.jsonl label=1 and
data/calibration/oos_control.jsonl label=0) from real, dated articles
fetched via the Financial Modeling Prep (FMP) news endpoints, plus an
update_oos mode that incrementally appends new post-cutoff articles to
the OOS corpus only.
The module exposes three public symbols (re-exported from
recall_guard.dataset.__init__):
ArticleRecord-- frozen dataclass describing one calibration row.build_calibration-- one-shot builder driven by the cutoff registry.update_oos-- incremental refresh of the OOS corpus, never IS.
It also exposes a small fetch_articles helper for direct FMP pagination
which the build/update routines compose internally and which is the only
location that performs HTTP I/O (requests.get). Tests mock that call.
CLI surface (python -m recall_guard.dataset.fmp_corpora --help):
python -m recall_guard.dataset.fmp_corpora build [--cutoffs PATH] [--out DIR]
[--target N] [--include-stock-news]
python -m recall_guard.dataset.fmp_corpora update [--out DIR] [--since YYYY-MM-DD]
Both subcommands read FMP_API_KEY from the environment (with
python-dotenv loading .env when present).
ArticleRecord
dataclass
One calibration article in canonical in-memory form.
prompt is the concatenated title + body excerpt, capped at
_PROMPT_MAX_CHARS characters with surrounding whitespace trimmed.
label is 1 for IS-memorized rows (pre-earliest-cutoff) and 0 for
OOS rows (post-latest-cutoff).
Source code in recall_guard/dataset/fmp_corpora.py
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | |
fetch_articles
fetch_articles(
endpoint, api_key, from_date, to_date, page, limit
)
Fetch one page of articles from an FMP news endpoint.
Builds the canonical FMP stable/ URL with from, to,
page, limit, and apikey query parameters. Raises
RuntimeError (with status code + endpoint) on any non-200 response.
Returns the parsed JSON list (assumed to be a list of article dicts).
Source code in recall_guard/dataset/fmp_corpora.py
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | |
build_calibration
build_calibration(
out_dir,
cutoffs,
target_per_corpus=100,
api_key=None,
endpoints=DEFAULT_ENDPOINTS,
today=None,
is_strata=5,
)
Build both calibration corpora from FMP news endpoints.
Filters strictly by publication date (Req 11.2):
- IS rows: published BEFORE min(cutoffs.values()); sampled across
is_strata equal-width chronological sub-windows of
(_EPOCH, earliest_cutoff) so the corpus does not cluster on the
cutoff edge (task 1.5). Per-bucket target is
target_per_corpus // is_strata; the LAST bucket absorbs any
remainder so the totals always sum to target_per_corpus.
- OOS rows: published AFTER max(cutoffs.values()) and on/before
today. OOS clustering at "now" is acceptable: recent articles are
uniformly unseen by every in-registry model, so the OOS sampler
keeps a single full window (no stratification).
- articles in the gap between earliest and latest cutoff are dropped
Deduplicates by URL exact-match and by sha256(title) (Req 11.3) across ALL sub-windows and ALL endpoints -- a single set per side persists for the whole run, so an article from sub-window 2 cannot reappear under a different bucket in sub-window 3.
Skips articles missing a body or a parseable publishedDate and emits
one WARNING per skip (Req 11.4).
Writes out_dir/is_memorized.jsonl and out_dir/oos_control.jsonl
as JSONL, one row per line, with the schema::
{"prompt": str, "label": int,
"metadata": {"published_at": "YYYY-MM-DD",
"source": str, "url": str}}
Returns (is_path, oos_path).
Source code in recall_guard/dataset/fmp_corpora.py
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | |
update_oos
update_oos(
out_dir,
api_key=None,
endpoints=DEFAULT_ENDPOINTS,
today=None,
since=None,
)
Append new post-cutoff articles to the OOS corpus only (Req 11.5).
Reads the existing out_dir/oos_control.jsonl, derives the latest
published_at (or uses since when supplied), fetches articles
on or after that date from each endpoint (so late-arriving articles
dated on the already-seen max day are still ingestible), dedups
against the existing rows (URL + title hash), and appends the new
rows in place. Never modifies is_memorized.jsonl.
Raises FileNotFoundError if the OOS file does not exist.
Source code in recall_guard/dataset/fmp_corpora.py
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 | |