Files
codex-py/codex/models.py
Tarik Moussa 1df9be6563 feat(F-09): rich parsing — formula + figure extraction
- codex/parsing/mathpix.py: pix2tex (local, CPU) primary + MathPix API
  optional; bbox heuristic h>15px, math-char-count>5; singleton model cache
- codex/parsing/figures.py: pymupdf embedded-image extraction → PNG;
  caption detection via proximity + "Figure/Fig./Abbildung" prefix
- codex/models.py: FormulaChunk + FigureChunk dataclasses (R-10/R-11)
- codex/ingest.py: --rich flag wires formula+figure extraction post-ingest
- codex/cli.py: search_app sub-typer (paper + formula subcommands),
  --rich flag on ingest; wiki_app from F-12 preserved intact
- codex/config.py: mathpix_app_id/key, pix2tex_fallback, figures_dir
- infra/schema.sql: formulas + figures tables with HNSW pgvector indexes
- pyproject.toml: pymupdf>=1.24, pix2tex>=0.1.4
- tests/parsing/test_mathpix.py + test_figures.py: 31 tests (mock pix2tex
  + MathPix HTTP, real pymupdf on synthetic PDF)

Gate: 158 passed, ruff clean, mypy clean (20 files)
Requirements: R-10 R-11 R-12 R-13 R-14 → done

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 01:16:24 +02:00

117 lines
3.1 KiB
Python

"""Domain dataclasses.
Each class maps directly to a table in ``infra/schema.sql``.
Field names and types are kept in sync with the DDL.
All fields that are nullable in the schema are typed as ``… | None``
with a default of ``None`` so instances can be constructed incrementally.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class Paper:
"""Maps to the ``papers`` table (Layer 1 — semantic search).
``id`` is the canonical identifier: an arXiv ID (e.g. ``2301.00001``)
or a DOI (e.g. ``10.1145/3592430``).
"""
id: str
title: str
openalex_id: str | None = None
bibkey: str | None = None
authors: list[str] = field(default_factory=list)
year: int | None = None
abstract: str | None = None
source_path: str | None = None
# abstract_emb is a list of floats representing the pgvector column;
# pgvector's Python driver accepts list[float] for vector columns.
abstract_emb: list[float] | None = None
added_at: datetime | None = None
@dataclass
class Chunk:
"""Maps to the ``chunks`` table (Layer 1 — full-text chunks).
``id`` is set by the database (BIGSERIAL); leave it as ``None``
when constructing a new chunk before insertion.
"""
paper_id: str
ord: int
content: str
id: int | None = None
embedding: list[float] | None = None
@dataclass
class Citation:
"""Maps to the ``citations`` table (Layer 2 — citation graph).
``cited_id`` intentionally has no foreign-key constraint in the schema;
citations to not-yet-ingested papers are preserved as discovery leads.
"""
citing_id: str
cited_id: str
context: str | None = None
@dataclass
class CodeLink:
"""Maps to the ``code_links`` table (Layer 3 — provenance).
``symbol`` is a C++ qualified name or ``file.cpp:line`` reference.
``role`` is a free-text description such as 'implements Thm 3.2'.
``id`` is set by the database (BIGSERIAL).
"""
symbol: str
paper_id: str | None = None
role: str | None = None
note: str | None = None
id: int | None = None
added_at: datetime | None = None
@dataclass
class FormulaChunk:
"""Maps to the ``formulas`` table (F-09 Rich Parsing).
Stores a single extracted mathematical formula (LaTeX) from a PDF page.
``id`` is set by the database (BIGSERIAL).
``embedding`` is reserved for future pgvector similarity search.
"""
paper_id: str
page: int
raw_latex: str
context: str
id: int | None = None
eq_label: str | None = None
embedding: list[float] | None = None
@dataclass
class FigureChunk:
"""Maps to the ``figures`` table (F-09 Rich Parsing).
Stores metadata for a single extracted figure from a PDF page.
``image_path`` points to the saved PNG on disk.
``id`` is set by the database (BIGSERIAL).
``embedding`` is reserved for future pgvector similarity search.
"""
paper_id: str
page: int
image_path: str
caption: str
id: int | None = None
embedding: list[float] | None = None