medbillcodes-api / app /fp_retrieve.py
medbillcodes-deploy
Deploy cloud pilot API
1ddeb51
Raw
History Blame Contribute Delete
11.7 kB
"""Family Practice (00) hybrid retrieval + Schedule relationship graph.
Lexical (+ optional semantic) search is constrained to the Family Practice &
Practice in General (00) specialty set so retrieval does not drift into other
specialty sections (e.g. Paediatrics 26).
"""
from __future__ import annotations
import logging
from collections import defaultdict
from .config import settings
from .family_scope import (
FAMILY_PRACTICE_SECTION,
_normalize_doc,
family_practice_00_filter,
fetch_all_family_practice_00_primaries,
filter_docs_family_practice_00_premium,
is_family_practice_00_primary,
is_family_practice_00_premium,
)
from .opensearch_client import get_client
from .paeds_retrieve import rrf_fuse
from .search import PRIMARY_ONLY, get_code
from .specialty_scope import filter_catalog_by_prefixes
from .weave_trace import redact_inputs, summarize_docs, traced
logger = logging.getLogger(__name__)
_RRF_K = 60
# Setting mirrors for common FP assessments / consults.
SETTING_MIRROR_GROUPS: dict[str, tuple[str, ...]] = {
"general_assessment": ("A003", "C003"),
"general_reassessment": ("A004", "C004", "W004"),
# Office A006 ↔ hospital C005/C006 ↔ LTC W105/W106 when setting matches.
"consultation": ("A006", "C005", "C006", "W105", "W106"),
"hospital_subsequent": ("C002", "C007", "C009"),
"ltc_subsequent": ("W001", "W002", "W003", "W008"),
}
# Content ladder (office) — not a strict consult ladder like Paediatrics.
ASSESSMENT_LADDER: tuple[str, ...] = (
"mini_assessment",
"minor_assessment",
"intermediate_assessment",
"general_assessment",
"general_reassessment",
)
_FAMILY_PREMIUMS: dict[str, tuple[str, ...]] = {
"office_assessment": (
"E078",
"E080",
"G372",
"G373",
"G590",
"E402",
"E403",
"E409",
"E410",
"A110",
"A112",
),
"well_baby": ("G372", "G373", "G590", "E402", "E403"),
"consultation": ("E078", "E402", "E403", "E409", "E410"),
"hospital_inpatient": (
"C102",
"C103",
"C104",
"C105",
"C106",
"C107",
"C108",
"C109",
"C110",
"E078",
"E409",
"E410",
),
"ltc": ("E078", "E402", "E403"),
"periodic_health": ("E078", "G372", "G373"),
}
_CODE_TO_FAMILY: dict[str, str] = {}
for code in ("A001", "A007", "A008", "A003", "A004"):
_CODE_TO_FAMILY[code] = "office_assessment"
_CODE_TO_FAMILY["A002"] = "well_baby"
for code in ("A006", "C005", "C006", "W105", "W106"):
_CODE_TO_FAMILY[code] = "consultation"
for code in ("C002", "C003", "C004", "C007", "C008", "C009", "C010", "C882"):
_CODE_TO_FAMILY[code] = "hospital_inpatient"
for code in ("W001", "W002", "W003", "W004", "W008", "W010", "W109"):
_CODE_TO_FAMILY[code] = "ltc"
_CODE_TO_FAMILY["K017"] = "periodic_health"
_CODE_TO_MIRRORS: dict[str, tuple[str, ...]] = {}
for _group, members in SETTING_MIRROR_GROUPS.items():
for m in members:
_CODE_TO_MIRRORS[m] = tuple(x for x in members if x != m)
_CODE_TO_GROUP: dict[str, str] = {
m: g for g, members in SETTING_MIRROR_GROUPS.items() for m in members
}
def relationship_meta(code: str) -> dict:
code = (code or "").upper()
family = _CODE_TO_FAMILY.get(code)
mirrors = list(_CODE_TO_MIRRORS.get(code, ()))
group = _CODE_TO_GROUP.get(code)
related_premiums = list(_FAMILY_PREMIUMS.get(family, ())) if family else []
ladder_note = None
if family == "office_assessment":
ladder_note = (
"Office assessment ladder: mini A008 → minor A001 → intermediate "
"A007 → general A003 → re-assessment A004 / periodic health K017"
)
return {
"service_family": family,
"setting_mirrors": mirrors,
"mirror_group": group,
"related_premiums": related_premiums,
"consult_ladder": ladder_note,
"section_alternatives": [],
}
def enrich_with_relationships(doc: dict) -> dict:
out = dict(doc)
meta = relationship_meta(out.get("billing_code") or "")
out["relationship_family"] = meta["service_family"]
out["relationship_mirrors"] = meta["setting_mirrors"]
out["relationship_premiums"] = meta["related_premiums"]
out["relationship_ladder"] = meta["consult_ladder"]
out["relationship_section_alternatives"] = meta["section_alternatives"]
return out
def related_premium_codes_for_primaries(primary_codes: list[str]) -> list[str]:
"""Premiums tied to each primary's service family only (no global dump).
Hospital special-visit matrix codes (C102–C110) stay with hospital_inpatient
primaries; office visits do not inherit them via allowlist bleed.
"""
out: list[str] = []
seen: set[str] = set()
for code in primary_codes:
family = _CODE_TO_FAMILY.get(code.upper())
for prem in _FAMILY_PREMIUMS.get(family, ()):
if prem not in seen:
seen.add(prem)
out.append(prem)
return out
def _hits_to_docs(hits: list[dict]) -> list[dict]:
docs = []
for h in hits:
src = h["_source"]
doc = _normalize_doc(src, score=h.get("_score"))
if is_family_practice_00_primary(doc):
docs.append(enrich_with_relationships(doc))
return docs
def _bm25_fp(query_text: str, filters: list[dict], top_k: int) -> list[dict]:
client = get_client()
body = {
"size": top_k,
"_source": {"excludes": ["code_vector"]},
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": query_text,
"fields": [
"billing_code^3",
"description_text^2",
"rules_and_constraints",
"parent_section",
],
}
}
],
"filter": filters,
}
},
}
response = client.search(index=settings.opensearch_index, body=body)
return _hits_to_docs(response.get("hits", {}).get("hits", []))
def _knn_fp(
query_vector: list[float] | None, filters: list[dict], top_k: int
) -> list[dict]:
if not query_vector:
return []
client = get_client()
body = {
"size": top_k,
"_source": {"excludes": ["code_vector"]},
"query": {
"knn": {
"code_vector": {
"vector": query_vector,
"k": top_k,
"filter": {"bool": {"filter": filters}},
}
}
},
}
try:
response = client.search(index=settings.opensearch_index, body=body)
except Exception: # noqa: BLE001
logger.exception("Family Practice k-NN search failed; continuing BM25 only")
return []
return _hits_to_docs(response.get("hits", {}).get("hits", []))
@traced(
"hybrid_search_family_practice",
kind="search",
postprocess_inputs=redact_inputs,
postprocess_output=summarize_docs,
)
def hybrid_search_family_practice(
query_text: str,
query_vector: list[float] | None,
*,
prefixes: list[str] | None = None,
top_k: int | None = None,
) -> list[dict]:
k = top_k or max(settings.hybrid_top_k, 40)
filters = PRIMARY_ONLY + family_practice_00_filter()
if prefixes:
from .encounter_align import prefix_filter # noqa: PLC0415
pref = prefix_filter(prefixes)
lexical = _bm25_fp(query_text, filters + pref, k)
semantic = _knn_fp(query_vector, filters + pref, k)
if not lexical and not semantic:
lexical = _bm25_fp(query_text, filters, k)
semantic = _knn_fp(query_vector, filters, k)
else:
lexical = _bm25_fp(query_text, filters, k)
semantic = _knn_fp(query_vector, filters, k)
fused = rrf_fuse([lexical, semantic]) if semantic else lexical
logger.info(
"Family Practice hybrid: lexical=%d semantic=%d fused=%d section=%s",
len(lexical),
len(semantic),
len(fused),
FAMILY_PRACTICE_SECTION,
)
return fused
def rank_full_fp_catalog(
query_text: str,
query_vector: list[float] | None,
*,
prefixes: list[str] | None = None,
) -> list[dict]:
"""Hybrid-rank the Family Practice (00) catalog (prefix-scoped)."""
catalog = fetch_all_family_practice_00_primaries()
catalog = filter_catalog_by_prefixes(catalog, prefixes or [])
if not catalog and prefixes:
catalog = fetch_all_family_practice_00_primaries()
top_k = max(len(catalog), settings.hybrid_top_k, 50)
hybrid_hits = hybrid_search_family_practice(
query_text,
query_vector,
prefixes=prefixes,
top_k=top_k,
)
by_code = {d["billing_code"]: d for d in hybrid_hits}
relationship_boost: list[dict] = []
for hit in hybrid_hits[:12]:
for mirror in hit.get("relationship_mirrors") or []:
if prefixes and mirror[:1] not in {p.upper() for p in prefixes}:
continue
if mirror in by_code:
continue
doc = get_code(mirror)
if doc and is_family_practice_00_primary(doc):
relationship_boost.append(enrich_with_relationships(doc))
fused = rrf_fuse(
[hybrid_hits, relationship_boost] if relationship_boost else [hybrid_hits]
)
fused_codes = {d["billing_code"] for d in fused}
for doc in catalog:
code = doc["billing_code"]
if code not in fused_codes:
fused.append(enrich_with_relationships(doc))
fused_codes.add(code)
return fused
def hybrid_premium_search_family_practice(
query_text: str,
query_vector: list[float] | None,
*,
primary_codes: list[str],
top_k: int = 24,
) -> list[dict]:
from .search import premium_search # noqa: PLC0415
from .sob_grounding import filter_current_sob_only, is_current_sob # noqa: PLC0415
families = sorted(
{_CODE_TO_FAMILY[c] for c in primary_codes if c in _CODE_TO_FAMILY}
)
family_kw = " ".join(f.replace("_", " ") for f in families)
premium_query = (
f"Family Practice specialty 00 {family_kw}. {query_text} "
"premium add-on immunization injection special visit chronic disease "
"OTHER PREMIUMS after hours procedure E409 E410"
)
hits = premium_search(premium_query, query_vector=query_vector, top_k=top_k)
hits = filter_current_sob_only(hits)
related = related_premium_codes_for_primaries(primary_codes)
by_code = {h["billing_code"]: enrich_with_relationships(h) for h in hits}
for code in related:
if code in by_code:
continue
doc = get_code(code)
if not is_current_sob(doc):
continue
if not is_family_practice_00_premium(doc):
continue
by_code[code] = enrich_with_relationships(doc)
ordered: list[dict] = []
seen: set[str] = set()
for code in related:
if code in by_code and code not in seen:
ordered.append(by_code[code])
seen.add(code)
for code, doc in by_code.items():
if code not in seen:
ordered.append(doc)
seen.add(code)
return filter_docs_family_practice_00_premium(ordered)
# Aliases matching the Paediatrics retrieve surface used by analyze dispatch.
rank_full_specialty_catalog = rank_full_fp_catalog
hybrid_premium_search_specialty = hybrid_premium_search_family_practice