feat(bib): code:/family: item tags from chunk anchors and FR paragraphs — stack bib code-tags (refs #688)

Rolls up pfs.anchors.anchor_metadata (already stamped on pgvector
chunks by stack llm index/restamp, plus FR paragraph anchors for
rules) to the bib item level: code:<HCPCS> and family:<KEY> tags,
additive and idempotent via Store.add_tag. Items with more than
--max-codes-per-item (200) distinct codes get family: tags only,
plus code: for hand-family codes (ACP/CCM/PCM/TCM/APCM) — an RVU
file or CPT edition naming thousands of codes doesn't need a tag
cloud that size.

Live run over the whole store: 67,198 items tagged, 486,519 tags
added (22,111 code: tags, 786 family: tags), 141 wide items capped.
The nightly zotero-sync.yml needs no change — tags ride along on
items already in its --tag scopes.
This commit is contained in:
kert
2026-09-09 22:19:13 -04:00
parent d016f707ac
commit 7eeee59fd3
5 changed files with 560 additions and 0 deletions

157
src/bib/codetags.py Normal file
View File

@@ -0,0 +1,157 @@
"""``code:``/``family:`` item tags — ``stack bib code-tags``.
Chunk-level anchors (``pfs.anchors.anchor_metadata``, stamped by
``llm.index``/``llm.restamp`` into pgvector's ``cmetadata``) already say
which HCPCS/CPT codes and code families a piece of text names. This
module rolls those per-chunk anchors up to the bib *item* they belong
to and stamps ``code:<CODE>`` / ``family:<KEY>`` tags on the item —
so ``stack bib query``/``stack bib tag`` and any other item-level tool
can filter "everything about G0556" without touching pgvector.
Two sources feed the same shape (``item_key -> (codes, families)``):
* :func:`item_codes_from_chunks` — one ``GROUP BY`` round trip per
pgvector collection (comments, rules, corpus).
* :func:`item_codes_from_anchors` — rules only, straight from
``fr_anchors.text`` via ``pfs.anchors.anchor_metadata``, no pgvector
needed (useful before/without an index run, or with
``--anchors-only``).
:func:`apply_code_tags` stamps the tags. It is additive and idempotent
— ``Store.add_tag`` only writes when the tag is new — and caps the
per-item ``code:`` cloud at ``max_codes_per_item`` distinct codes: an
RVU file or code-list item can name thousands of codes, and a tag cloud
that size helps nobody. Wide items still get every ``family:`` tag,
plus ``code:`` for codes in a hand-registered family
(``pfs.families.HAND_FAMILIES`` — ACP/CCM/PCM/TCM/APCM) since those are
the ones anyone actually filters by.
"""
from __future__ import annotations
from typing import Any, Mapping
from sqlalchemy import text
from bib.tag import Tag
from pfs.anchors import anchor_metadata
from pfs.families import HAND_FAMILIES, Family
CodeMap = dict[str, tuple[set[str], set[str]]]
#: Codes belonging to a hand-registered family — these survive the
#: ``max_codes_per_item`` cap even on a wide item (Ruling: brief Task 4).
HAND_FAMILY_CODES: frozenset[str] = frozenset(
code for fam in HAND_FAMILIES.values() for code in fam.codes
)
SELECT_CHUNK_CODES_SQL = (
"SELECT e.cmetadata->>'item_key', "
"string_agg(e.cmetadata->>'codes', ' '), "
"string_agg(e.cmetadata->>'families', ' ') "
"FROM langchain_pg_embedding e "
"WHERE e.collection_id = (SELECT uuid FROM langchain_pg_collection WHERE name = :collection) "
"AND coalesce(e.cmetadata->>'codes', '') <> '' "
"GROUP BY 1"
)
def item_codes_from_chunks(engine: Any, *, collection: str) -> CodeMap:
"""``item_key -> (codes, families)`` for one pgvector collection, via
a single ``GROUP BY e.cmetadata->>'item_key'`` round trip."""
with engine.begin() as conn:
rows = conn.execute(
text(SELECT_CHUNK_CODES_SQL), {"collection": collection}
).fetchall()
out: CodeMap = {}
for item_key, codes_agg, families_agg in rows:
if not item_key:
continue
codes = {c for c in (codes_agg or "").split() if c}
families = {f for f in (families_agg or "").split() if f}
if not codes and not families:
continue
out[item_key] = (codes, families)
return out
def item_codes_from_anchors(
store: Any,
*,
families: Mapping[str, Family] | None = None,
code_index: Mapping[str, tuple[str, ...]] | None = None,
) -> CodeMap:
"""``item_key -> (codes, families)`` for rules, computed straight from
``fr_anchors.text`` via ``anchor_metadata`` — no pgvector needed."""
con = store._con()
rows = con.execute(
"SELECT item_key, text FROM fr_anchors ORDER BY item_key, p_id"
).fetchall()
out: CodeMap = {}
for row in rows:
item_key = row["item_key"]
md = anchor_metadata(
row["text"] or "", families=families, code_index=code_index
)
codes = {c for c in md["codes"].split() if c}
fams = {f for f in md["families"].split() if f}
if not codes and not fams:
continue
cur_codes, cur_fams = out.setdefault(item_key, (set(), set()))
cur_codes.update(codes)
cur_fams.update(fams)
return out
def apply_code_tags(
store: Any,
mapping: CodeMap,
*,
dry_run: bool = False,
max_codes_per_item: int = 200,
) -> dict[str, int]:
"""Stamp ``code:``/``family:`` tags for every item in *mapping*.
Additive and idempotent: an item's current tags are read once and
only labels not already present count toward ``tags_added`` (and
are written, unless ``dry_run``) — a rerun over an unchanged mapping
adds zero tags. Items with more than ``max_codes_per_item`` distinct
codes get ``family:`` tags plus ``code:`` only for hand-family codes
(:data:`HAND_FAMILY_CODES`); such items count toward
``skipped_wide``. Items absent from *store* are skipped silently —
a caller can build *mapping* from a wider scope (e.g. pgvector) than
what's currently in bib.
Returns ``{"items": n, "tags_added": n, "skipped_wide": n}``.
"""
items = 0
tags_added = 0
skipped_wide = 0
for item_key, (codes, families) in mapping.items():
try:
current = set(store.get(item_key).tags)
except KeyError:
continue
items += 1
wide = len(codes) > max_codes_per_item
if wide:
skipped_wide += 1
tag_codes = codes & HAND_FAMILY_CODES
else:
tag_codes = codes
labels = [Tag.code(c).label for c in tag_codes] + [
Tag.family(f).label for f in families
]
for label in labels:
if label in current:
continue
tags_added += 1
if not dry_run:
store.add_tag(item_key, label)
return {"items": items, "tags_added": tags_added, "skipped_wide": skipped_wide}

View File

@@ -49,6 +49,17 @@ Namespaces
``fn:cclf.stg_beneficiary_xref``, ``fn:cclf.stg_beneficiary_xref``,
``fn:readmissions.int_encounter`` ``fn:readmissions.int_encounter``
``code``
An HCPCS/CPT code the item names (upper-case), rolled up from
chunk-level anchors (``pfs.anchors.anchor_metadata``) or FR
paragraph text — see ``bib.codetags``:
``code:99490``, ``code:G0556``
``family``
A ``pfs.code_family``/``pfs.families`` family key the item touches
— the key exactly as registered, never lower-cased:
``family:CCM``, ``family:APCM``
Usage:: Usage::
from bib.tag import Tag from bib.tag import Tag
@@ -238,6 +249,39 @@ class Tag(BaseModel):
value = f"{item_key}/{locator}" if locator else item_key value = f"{item_key}/{locator}" if locator else item_key
return cls(namespace="pin", value=value) return cls(namespace="pin", value=value)
@classmethod
def code(cls, hcpcs: str) -> Tag:
"""Tag for an HCPCS/CPT code the item names.
Parameters
----------
hcpcs : str
A code, e.g. ``99490``, ``G0556``. Upper-cased.
Examples::
Tag.code("g0556") # code:G0556
"""
return cls(namespace="code", value=hcpcs.upper())
@classmethod
def family(cls, key: str) -> Tag:
"""Tag for a ``pfs.families``/``pfs.code_family`` family key.
Parameters
----------
key : str
The family key exactly as registered (e.g. ``CCM``,
``APCM``, or a derived ``pfs.code_family`` key) — never
lower-cased, since derived keys aren't necessarily
uppercase-safe round trips.
Examples::
Tag.family("CCM") # family:CCM
"""
return cls(namespace="family", value=key)
@classmethod @classmethod
def fn(cls, qualified_name: str) -> Tag: def fn(cls, qualified_name: str) -> Tag:
"""Tag linking a bibliography item to an express function. """Tag linking a bibliography item to an express function.

View File

@@ -912,3 +912,104 @@ def cfr_jump(
typer.echo(cfrlink.md_link(ref)) typer.echo(cfrlink.md_link(ref))
else: else:
typer.echo(cfrlink.url(cfrlink.parse_cite(ref))) typer.echo(cfrlink.url(cfrlink.parse_cite(ref)))
@app.command(name="code-tags")
def code_tags(
collection: str = typer.Option(
"all", "--collection", help="Which collection: all | comments | rules | corpus."
),
anchors_only: bool = typer.Option(
False,
"--anchors-only",
help="Rules only, straight from fr_anchors text — skip pgvector entirely.",
),
dry_run: bool = typer.Option(
False, "--dry-run", help="Report per-collection item/tag counts; write nothing."
),
max_codes_per_item: int = typer.Option(
200,
"--max-codes-per-item",
help="Items with more distinct codes get family: tags only (+ hand-family codes).",
),
) -> None:
"""Stamp code:/family: item tags from chunk anchors and FR paragraphs.
Rolls up ``pfs.anchors.anchor_metadata`` — already stamped on
pgvector chunks by ``stack llm index``/``stack llm restamp`` — to
the bib item level: every code an item's chunks name becomes a
``code:<CODE>`` tag, every family a ``family:<KEY>`` tag. Rules also
get a pgvector-free source (``fr_anchors.text``, the FR paragraph
anchor map from ``stack bib fr-grab``); pass ``--anchors-only`` to
use only that and skip pgvector for rules (and skip comments/corpus
entirely — they have no other source). Additive and idempotent
(``bib.codetags.apply_code_tags``) — safe to rerun after every
``stack llm restamp``.
"""
import os
from bib import connect
from bib.codetags import (
apply_code_tags,
item_codes_from_anchors,
item_codes_from_chunks,
)
from llm import config as llm_config
from llm.index import _engine
from pfs.anchors import code_family_index
from pfs.families import FAMILIES, refresh_from
collections = ("comments", "rules", "corpus")
if collection not in ("all", *collections):
raise typer.BadParameter("collection must be all, comments, rules or corpus")
targets = collections if collection == "all" else (collection,)
store = connect()
cfg = llm_config.load()
if os.path.exists(cfg.duckdb_replica):
import duckdb
con = duckdb.connect(cfg.duckdb_replica, read_only=True)
try:
refresh_from(con)
finally:
con.close()
code_index = code_family_index(FAMILIES)
engine = None
totals = {"items": 0, "tags_added": 0, "skipped_wide": 0}
for target in targets:
if anchors_only and target != "rules":
typer.echo(f"{target}: skipped (--anchors-only)")
continue
if target == "rules":
mapping = item_codes_from_anchors(store, code_index=code_index)
if not anchors_only:
if engine is None:
engine = _engine(cfg)
for key, (codes, fams) in item_codes_from_chunks(
engine, collection=target
).items():
cur_codes, cur_fams = mapping.setdefault(key, (set(), set()))
mapping[key] = (cur_codes | codes, cur_fams | fams)
else:
if engine is None:
engine = _engine(cfg)
mapping = item_codes_from_chunks(engine, collection=target)
stats = apply_code_tags(
store, mapping, dry_run=dry_run, max_codes_per_item=max_codes_per_item
)
for k in totals:
totals[k] += stats[k]
typer.echo(
f"{target}: items={stats['items']} tags_added={stats['tags_added']} "
f"skipped_wide={stats['skipped_wide']}"
)
prefix = "would-" if dry_run else ""
typer.echo(
f"total: {prefix}items={totals['items']} {prefix}tags_added={totals['tags_added']} "
f"skipped_wide={totals['skipped_wide']}"
)

146
tests/bib/test_codetags.py Normal file
View File

@@ -0,0 +1,146 @@
"""Tests for bib.codetags — code:/family: item tags from chunk anchors
and FR paragraphs (Task 4, refs #688)."""
from __future__ import annotations
from unittest.mock import MagicMock
from bib.codetags import (
apply_code_tags,
item_codes_from_anchors,
item_codes_from_chunks,
)
from bib.item import Rule, Source
from bib.store import Store
from bib.tag import Tag
def _store() -> Store:
return Store(":memory:")
class TestTagFactories:
def test_code_label(self) -> None:
assert Tag.code("G0556").label == "code:G0556"
def test_code_upper_cases(self) -> None:
assert Tag.code("g0556").label == "code:G0556"
def test_family_label(self) -> None:
assert Tag.family("CCM").label == "family:CCM"
class TestItemCodesFromChunks:
def test_group_by_item_key_unions_codes_and_families(self) -> None:
engine = MagicMock()
conn = engine.begin.return_value.__enter__.return_value
conn.execute.return_value.fetchall.return_value = [
("ITEM0001", "99490 99491", "CCM"),
("ITEM0002", "99497", ""),
]
mapping = item_codes_from_chunks(engine, collection="comments")
assert mapping["ITEM0001"] == ({"99490", "99491"}, {"CCM"})
assert mapping["ITEM0002"] == ({"99497"}, set())
def test_skips_rows_with_no_item_key(self) -> None:
engine = MagicMock()
conn = engine.begin.return_value.__enter__.return_value
conn.execute.return_value.fetchall.return_value = [
(None, "99490", ""),
]
mapping = item_codes_from_chunks(engine, collection="rules")
assert mapping == {}
class TestItemCodesFromAnchors:
def test_reads_fr_anchors_per_item(self) -> None:
store = _store()
key = store.create(Rule(title="CY 2026 PFS Final Rule"))
store._con().executemany(
"INSERT INTO fr_anchors (item_key, p_id, page, ordinal, text) VALUES (?,?,?,?,?)",
[
(key, 1, 100, 1, "CPT code 99490 is for chronic care management."),
(key, 2, 100, 2, "See also 99491 for extended CCM."),
],
)
store._con().commit()
mapping = item_codes_from_anchors(store)
assert key in mapping
codes, families = mapping[key]
assert {"99490", "99491"} <= codes
assert "CCM" in families
def test_item_with_no_codes_is_absent(self) -> None:
store = _store()
key = store.create(Rule(title="No codes rule"))
store._con().execute(
"INSERT INTO fr_anchors (item_key, p_id, page, ordinal, text) VALUES (?,?,?,?,?)",
(key, 1, 1, 1, "This paragraph names no codes at all."),
)
store._con().commit()
mapping = item_codes_from_anchors(store)
assert key not in mapping
class TestApplyCodeTags:
def test_tags_items_with_code_and_family(self) -> None:
store = _store()
key = store.create(Source(title="Narrow item"))
mapping = {key: ({"99490", "99491"}, {"CCM"})}
stats = apply_code_tags(store, mapping)
assert stats == {"items": 1, "tags_added": 3, "skipped_wide": 0}
tags = set(store.get(key).tags)
assert {"code:99490", "code:99491", "family:CCM"} <= tags
def test_wide_item_gets_only_family_and_hand_family_codes(self) -> None:
store = _store()
key = store.create(Source(title="RVU file"))
wide_codes = {f"{n:05d}" for n in range(300)}
# G0556 is a hand-family (APCM) code and must survive the cap.
wide_codes.add("G0556")
mapping = {key: (wide_codes, {"APCM", "CCM"})}
stats = apply_code_tags(store, mapping, max_codes_per_item=200)
assert stats["items"] == 1
assert stats["skipped_wide"] == 1
tags = set(store.get(key).tags)
assert "code:G0556" in tags
assert "family:APCM" in tags
assert "family:CCM" in tags
# None of the plain wide codes made it through as code: tags.
assert not any(t.startswith("code:") and t != "code:G0556" for t in tags)
def test_rerun_adds_zero_new_tags(self) -> None:
store = _store()
key = store.create(Source(title="Idempotent item"))
mapping = {key: ({"99490"}, {"CCM"})}
first = apply_code_tags(store, mapping)
second = apply_code_tags(store, mapping)
assert first["tags_added"] == 2
assert second["tags_added"] == 0
assert second["items"] == 1
def test_dry_run_writes_nothing(self) -> None:
store = _store()
key = store.create(Source(title="Dry run item"))
mapping = {key: ({"99490"}, {"CCM"})}
stats = apply_code_tags(store, mapping, dry_run=True)
assert stats["tags_added"] == 2
assert store.get(key).tags == []
def test_unknown_item_key_is_skipped(self) -> None:
store = _store()
mapping = {"MISSING1": ({"99490"}, set())}
stats = apply_code_tags(store, mapping)
assert stats == {"items": 0, "tags_added": 0, "skipped_wide": 0}

View File

@@ -263,3 +263,115 @@ class TestAllHelp:
def test_refresh_oig(self): def test_refresh_oig(self):
assert runner.invoke(app, ["refresh-oig", "--help"]).exit_code == 0 assert runner.invoke(app, ["refresh-oig", "--help"]).exit_code == 0
class TestCodeTags:
@patch("bib.codetags.apply_code_tags")
@patch("bib.codetags.item_codes_from_chunks")
@patch("bib.codetags.item_codes_from_anchors")
@patch("pfs.families.refresh_from")
@patch("pfs.anchors.code_family_index", return_value={})
@patch("os.path.exists", return_value=False)
@patch("llm.index._engine")
@patch("llm.config.load")
@patch("bib.connect")
def test_all_collections_run_rules_via_anchors_and_chunks(
self,
mc_connect,
mc_cfg,
mc_engine,
mc_exists,
mc_code_index,
mc_refresh,
mc_anchors,
mc_chunks,
mc_apply,
):
mc_anchors.return_value = {"R1": ({"99490"}, {"CCM"})}
mc_chunks.return_value = {"C1": ({"99497"}, set())}
mc_apply.return_value = {"items": 1, "tags_added": 1, "skipped_wide": 0}
result = runner.invoke(app, ["code-tags", "--collection", "all"])
assert result.exit_code == 0, result.output
mc_anchors.assert_called_once()
# rules + comments + corpus == 3 apply_code_tags calls
assert mc_apply.call_count == 3
for _, kwargs in mc_apply.call_args_list:
assert kwargs["dry_run"] is False
@patch("bib.codetags.apply_code_tags")
@patch("bib.codetags.item_codes_from_chunks")
@patch("bib.codetags.item_codes_from_anchors", return_value={})
@patch("pfs.families.refresh_from")
@patch("pfs.anchors.code_family_index", return_value={})
@patch("os.path.exists", return_value=False)
@patch("llm.index._engine")
@patch("llm.config.load")
@patch("bib.connect")
def test_dry_run_passes_through_and_writes_nothing(
self,
mc_connect,
mc_cfg,
mc_engine,
mc_exists,
mc_code_index,
mc_refresh,
mc_anchors,
mc_chunks,
mc_apply,
):
mc_chunks.return_value = {}
mc_apply.return_value = {"items": 0, "tags_added": 0, "skipped_wide": 0}
store = MagicMock()
mc_connect.return_value = store
result = runner.invoke(
app, ["code-tags", "--collection", "comments", "--dry-run"]
)
assert result.exit_code == 0, result.output
assert mc_apply.call_args.kwargs["dry_run"] is True
store.add_tag.assert_not_called()
@patch("bib.codetags.apply_code_tags")
@patch("bib.codetags.item_codes_from_chunks")
@patch("bib.codetags.item_codes_from_anchors", return_value={})
@patch("pfs.families.refresh_from")
@patch("pfs.anchors.code_family_index", return_value={})
@patch("os.path.exists", return_value=False)
@patch("llm.index._engine")
@patch("llm.config.load")
@patch("bib.connect")
def test_anchors_only_skips_pgvector_entirely(
self,
mc_connect,
mc_cfg,
mc_engine,
mc_exists,
mc_code_index,
mc_refresh,
mc_anchors,
mc_chunks,
mc_apply,
):
mc_apply.return_value = {"items": 0, "tags_added": 0, "skipped_wide": 0}
result = runner.invoke(
app, ["code-tags", "--collection", "all", "--anchors-only"]
)
assert result.exit_code == 0, result.output
mc_anchors.assert_called_once()
mc_chunks.assert_not_called()
mc_engine.assert_not_called()
# only the rules mapping gets applied — comments/corpus skipped
assert mc_apply.call_count == 1
def test_help(self):
assert runner.invoke(app, ["code-tags", "--help"]).exit_code == 0
@patch("bib.connect")
def test_bad_collection_raises_bad_parameter(self, mc_connect):
result = runner.invoke(app, ["code-tags", "--collection", "nope"])
assert result.exit_code != 0