MHamdan's picture
Deploy Research-Link-AI (Docker Space, offline demo)
a753e74 verified
Raw
History Blame Contribute Delete
4.44 kB
"""Spec paper-module layout: filename mapping and generated artifacts.
Defines the canonical ``papers/<slug>/`` module: the mapping from the pipeline's
internal content keys to the public spec filenames, plus the deterministic
builders for the new spec artifacts (``paper.md``, ``metadata.json``,
``sources.json``). Pure and testable — no I/O here; the Export agent writes.
"""
from __future__ import annotations
from typing import Any
from researchlink.schemas.paper import PaperExtraction, PaperMetadata
from researchlink.schemas.provenance import MetadataProvenance
#: Internal pipeline content key -> on-disk spec filename.
SPEC_RENAME: dict[str, str] = {
"digest.md": "summary.md",
"literature-review.md": "related_work.md",
"implementation-link.md": "implementation.md",
"reproducibility.md": "reproduction.md",
"teaching-path.md": "study_notes.md",
}
#: The public spec module files a complete module should contain.
SPEC_FILES: tuple[str, ...] = (
"paper.md",
"metadata.json",
"sources.json",
"bibtex.bib",
"summary.md",
"claims.md",
"related_work.md",
"implementation.md",
"reproduction.md",
"study_notes.md",
"review.md",
)
def spec_filename(internal_key: str) -> str:
"""Map an internal content key to its on-disk name (identity if unmapped)."""
return SPEC_RENAME.get(internal_key, internal_key)
def paper_markdown(meta: PaperMetadata, extraction: PaperExtraction) -> str:
"""Deterministic structured Markdown of the paper (verbatim extraction)."""
lines = [
f"# {meta.title}",
"",
"> Machine-extracted paper content. Verbatim from the source PDF/text; "
"no interpretation added. Fields may be incomplete — verify against the original.",
"",
f"- **Year:** {meta.year or '[needs-verification]'}",
f"- **Venue:** {meta.venue or '[needs-verification]'}",
f"- **Authors:** {', '.join(meta.authors_provisional[:12]) or '[needs-verification]'}",
f"- **Paper URL:** {meta.paper_url or 'N/A'}",
f"- **Code URL:** {meta.code_url or 'N/A'}",
f"- **Pages extracted:** {extraction.page_count or 'unknown'}",
"",
"## Abstract",
"",
extraction.abstract or "_[needs-verification] no abstract extracted_",
"",
"## Section Headings",
"",
]
if extraction.section_headings:
lines += [f"- {h}" for h in extraction.section_headings]
else:
lines.append("_[needs-verification] no section headings extracted_")
if extraction.full_text:
lines += ["", "## Full Extracted Text", "", "```text", extraction.full_text.strip(), "```"]
return "\n".join(lines) + "\n"
SCHEMA_VERSION = "0.1"
def _confidence_label(value: float) -> str:
return "high" if value >= 0.6 else "medium" if value >= 0.4 else "low"
def build_metadata_json(
provenance: MetadataProvenance,
meta: PaperMetadata,
model_slug: str = "unknown-model",
) -> tuple[dict[str, Any], dict[str, Any]]:
"""Split provenance into (metadata.json payload, sources.json payload).
Both are versioned. metadata.json carries provenance-tracked fields (each with
a human-readable confidence label); sources.json wraps the per-source records
as ``{type, url, accessed_at, status}``.
"""
data = provenance.model_dump(mode="json")
raw_sources = data.pop("sources", [])
# Add a readable confidence label alongside the numeric confidence.
for name in provenance.FIELD_NAMES:
field = data.get(name)
if isinstance(field, dict):
field["confidence_label"] = _confidence_label(field.get("confidence", 0.0))
payload: dict[str, Any] = {
"schema_version": SCHEMA_VERSION,
"slug": meta.slug,
"generated_by_model": model_slug,
"conflicts": provenance.conflicts(),
"fields": data,
}
sources_payload: dict[str, Any] = {
"schema_version": SCHEMA_VERSION,
"sources": [
{
"type": s.get("name"),
"url": s.get("url"),
"accessed_at": s.get("retrieved_at"),
"status": "ok" if s.get("ok", True) else "error",
"error": s.get("error"),
"fields": list((s.get("fields") or {}).keys()),
}
for s in raw_sources
],
}
return payload, sources_payload