API reference
The CLI is the primary user interface, but the underlying Python package is also importable for programmatic use. This page is a module-by-module reference for what’s in each file and what’s part of the public API.
For the source: home/collabs/elife/claim-trees/extract/elife_extract/. ~1200 lines of Python across 9 modules.
elife_extract.cli
Section titled “elife_extract.cli”The argparse-based command-line interface. ~400 lines.
Entry points:
main(argv: list[str] | None = None) -> int— theelife-extractconsole scriptbuild_parser() -> argparse.ArgumentParser— build the full parser; useful for embedding the CLI in another tool
Subcommand handlers:
cmd_extract(args)— Steps 1-4 (prepare → reconcile)cmd_write(args)— Steps 5-7 (review → write)cmd_verify_refs(args)— CrossRef DOI resolutioncmd_run(args)— composed shorthandcmd_evaluate(args)— round-trip scoring
Each handler resolves a Config from the args, runs its phase, and returns an exit code.
elife_extract.config
Section titled “elife_extract.config”Configuration resolution. ~120 lines.
Class:
Config— dataclass holding resolved configuration. Constructed viaConfig.from_args(args)which falls through CLI args → env vars → defaults.
Module constants:
DEFAULT_MODEL_RESULTS = "claude-sonnet-4-6"DEFAULT_MODEL_CAPTION = "claude-sonnet-4-6"DEFAULT_MODEL_STRUCTURE = "claude-sonnet-4-6"DEFAULT_MODEL_RECONCILE = "claude-opus-4-6"DEFAULT_VERTEX_PROJECT = "cr-mainen"DEFAULT_VERTEX_REGION = "europe-west1"DEFAULT_PROMPT_VARIANT = "default"
Methods:
Config.from_args(args) -> Config— build from argparse NamespaceConfig.prompt_path(agent: str) -> Path— resolve the prompt file for a given agent in the active variantConfig.validate() -> list[str]— return error messages if config is incomplete
elife_extract.schema
Section titled “elife_extract.schema”Pydantic models for the wire format between pipeline steps. ~80 lines.
Type aliases:
ClaimType = Literal[...]— the 5 (or 9, in extended schema) claim-type valuesRole = Literal[...]— the 9 role valuesAgentName = Literal["results", "caption", "structure", "reviewer"]— agent identifiersAgentConfidence = Literal["high", "tentative"]ReconciledConfidence = Literal["high", "contested", "single-source"]
Classes:
CandidateClaim— one claim as emitted by a single extraction agentAgentExtraction— the output of one extraction agent on one paper (paper_slug + claims list)ReconciledClaim— a claim after the reconciliation step (with confidence, sources, evidence_by_agent)DraftClaimTable— the output of Step 4, the input to Step 5
All models inherit from pydantic.BaseModel; instantiate from dicts (validated) and serialize via .model_dump_json().
elife_extract.prepare
Section titled “elife_extract.prepare”Step 1: paper fetch, slice, figure mapping. ~200 lines.
Class:
PreparedPaper— dataclass withdoi,paper_slug,title,authors,abstract,results_text,captions_text,methods_text,extraction_path,figure_captions
Functions:
prepare(doi: str, ...) -> PreparedPaper— top-level: fetch, slice, return prepared paperfetch_pdf(article_id: str, cache_dir: Path | None = None) -> Path— fetch from eLife CDN with disk cacheextract_text(pdf_path: Path) -> str— pdfplumber wrapperslice_sections(text: str) -> dict[str, str]— regex-based section detectionextract_figure_captions(text: str) -> list[FigureCaption]— caption parserderive_slug(authors, year, title) -> str— slug derivation from metadataarticle_id_from_doi(doi: str) -> str— extract article ID from eLife DOI
elife_extract.agents
Section titled “elife_extract.agents”Steps 2-3: extraction agents. ~150 lines.
Functions:
run_agent(agent: AgentName, paper: PreparedPaper, cfg: Config) -> AgentExtraction— run one agentrun_all_agents(paper, cfg) -> tuple[AgentExtraction, AgentExtraction, AgentExtraction]— run all three sequentiallyslice_for_agent(agent: AgentName, paper: PreparedPaper) -> str— what each agent readsload_prompt(agent: AgentName, cfg: Config) -> str— load the role-specific promptparse_json_response(raw: str) -> list[dict] | dict— JSON parsing with code-fence toleranceget_vertex_client(cfg: Config) -> AnthropicVertex— cached Vertex client
elife_extract.reconcile
Section titled “elife_extract.reconcile”Step 4: reconciliation. ~120 lines.
Function:
reconcile(results, caption, structure, cfg, paper_doi, paper_title) -> DraftClaimTable— the Opus reconciliation call
elife_extract.external_review
Section titled “elife_extract.external_review”Step 4.5: external Opus reviewer pass. ~110 lines.
Function:
external_review(paper: PreparedPaper, draft: DraftClaimTable, cfg: Config) -> DraftClaimTable— runs the Opus reviewer pass on a reconciled draft, returns a revised DraftClaimTable
elife_extract.review
Section titled “elife_extract.review”Step 5: human review gate. ~110 lines.
Function:
review(draft: DraftClaimTable, cfg: Config) -> DraftClaimTable | None— invoke the configured review mode (interactive / auto-approve / dry-run); external mode is invoked fromcmd_writedirectly
elife_extract.write
Section titled “elife_extract.write”Steps 6-7: dependency mapping (scaffolded) + claim file emission. ~200 lines.
Functions:
write_claim_files(draft: DraftClaimTable, cfg: Config) -> list[Path]— emit claim files; returns paths writtenderive_claim_slug(claim_text: str, panel: str | None) -> str— slug derivation from claim textmap_dependencies(draft, cfg) -> DraftClaimTable— Step 6 (currently a no-op; raises NotImplementedError)
elife_extract.verify_refs
Section titled “elife_extract.verify_refs”CrossRef DOI resolution for literature-context claims. ~250 lines.
Class:
VerifyResult— per-claim resolution outcome
Functions:
verify_refs(paper_slug, cfg, dry_run) -> list[VerifyResult]— top-level walkverify_claim(fm, body, paper_slug, claim_path, dry_run) -> VerifyResult— verify one claimcrossref_lookup(query, rows=3) -> list[CrossrefMatch]— CrossRef /works querycrossref_resolve_doi(doi) -> CrossrefMatch | None— confirm a DOI resolvesextract_reference_hints(slug, claim_text, body_text) -> list[str]— hint extraction for un-DOI’d claims
elife_extract.evaluate
Section titled “elife_extract.evaluate”Round-trip scoring. ~300 lines.
Class:
PaperScorecard— per-paper scoring resultClaim— per-claim representation for the matcher
Functions:
evaluate_paper(ref_paper_dir, work_dir, cfg, review_mode) -> PaperScorecard— full pipeline + scoring on one paperscore_against_reference(ref_dir, cli_dir, paper_slug, paper_doi, review_mode, cfg) -> PaperScorecard— score one CLI output against a referenceaggregate_report(cards, out_path, reference_dir, work_root, review_mode)— render multi-paper scorecardrun_matcher(ref_claims, cli_claims, cfg) -> dict— Opus matcher callload_claims(claim_dir, source) -> list[Claim]— read claim files from a paper directory
Programmatic use
Section titled “Programmatic use”from elife_extract.config import Configfrom elife_extract.prepare import preparefrom elife_extract.agents import run_all_agentsfrom elife_extract.reconcile import reconcilefrom elife_extract.external_review import external_reviewfrom elife_extract.write import write_claim_files
# Build a Config (without going through argparse)import argparsens = argparse.Namespace(corpus_dir="/tmp/corpus", review_mode="external")cfg = Config.from_args(ns)
# Run the pipeline programmaticallypaper = prepare("10.7554/eLife.95562")results, caption, structure = run_all_agents(paper, cfg)draft = reconcile(results, caption, structure, cfg, paper_doi=paper.doi, paper_title=paper.title)revised = external_review(paper, draft, cfg)written = write_claim_files(revised, cfg)print(f"wrote {len(written)} claim files")This bypasses the CLI’s user-facing logging but uses the same modules. Useful for embedding the pipeline in larger systems or for testing.
Implementation references
Section titled “Implementation references”home/collabs/elife/claim-trees/extract/elife_extract/— the sourcepyproject.toml— package metadata and entry point declaration
Next steps
Section titled “Next steps”- Configuration reference — every flag and env var
- Code structure — where things live and why
- Design decisions — ADR-style record of architectural choices