Checkpoint frontend UI/UX overhaul and ingestion embed benchmark work
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
"""Server-derived subject scope.
|
||||
|
||||
`routing.py`'s `_scope_gate` abstains outright on `NON_HUMAN` scope. Before
|
||||
this module, that value came straight from the request body — a caller could
|
||||
send `{"subject_scope":"human",...}` regardless of the query text, and the
|
||||
shipped web BFF did exactly that, hard-coded on every request without reading
|
||||
the message at all (Codex's 2026-08-06 review, F-02). A scope decision must
|
||||
not be something the caller gets to assert.
|
||||
|
||||
`resolve_subject_scope` derives it from the query text and combines that with
|
||||
whatever the caller claimed by taking the more conservative of the two: a
|
||||
caller can *narrow* scope (claim `non_human` and have it stick) but can never
|
||||
*widen* it — a claim of `human` cannot override a server-detected veterinary
|
||||
turn. This is a corpus-coverage check, not a restriction on what a doctor or
|
||||
pharmacist is allowed to ask: the book behind this product covers human
|
||||
drug monographs only, so a query about dosing a dog is out of scope
|
||||
regardless of who is asking. It has nothing to do with, and must never be
|
||||
extended into, gatekeeping what kind of *clinical* question a professional
|
||||
user is allowed to ask (see `[[feedback_no_recommendation_gate]]`/progress
|
||||
log 2026-08-06 for the removed `QueryIntent.RECOMMENDATION` keyword
|
||||
detector — this product is for doctors and pharmacists, not lay users, and a
|
||||
"nên dùng thuốc gì" question from a clinician is exactly what a formulary
|
||||
reference is for, not something to abstain on).
|
||||
|
||||
Deliberately not an LLM call: this is the gate every request passes through,
|
||||
so it must be cheap, available during a provider outage, and auditable as a
|
||||
fixed rule instead of a model judgment. It is a keyword heuristic, which
|
||||
means it has real blind spots (an unusual phrasing can still slip past) — the
|
||||
same trade-off `rag/agent.py`'s `_looks_non_human` backstop already accepts.
|
||||
`rag/agent.py` should consolidate onto this module once the new orchestrator
|
||||
is wired (F-03), instead of keeping a second, narrower keyword list.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from .models import SubjectScope
|
||||
from .text import normalize_name
|
||||
|
||||
# Phrases that name a non-human recipient. Matched on normalized text (casefold,
|
||||
# diacritics stripped) so "chó", "CHÓ", "cho chó" all match one entry.
|
||||
_NON_HUMAN_PHRASES = (
|
||||
"cho cho", # "cho chó" — normalize_name collapses "chó" -> "cho"
|
||||
"cho meo",
|
||||
"cho ga",
|
||||
"cho vit",
|
||||
"cho lon",
|
||||
"cho heo",
|
||||
"cho bo",
|
||||
"cho ngua",
|
||||
"cho de",
|
||||
"cho cuu",
|
||||
"thu y",
|
||||
"vat nuoi",
|
||||
"gia suc",
|
||||
"gia cam",
|
||||
"dong vat",
|
||||
)
|
||||
|
||||
|
||||
def looks_non_human(query: str) -> bool:
|
||||
normalized = normalize_name(query)
|
||||
return any(phrase in normalized for phrase in _NON_HUMAN_PHRASES)
|
||||
|
||||
|
||||
def resolve_subject_scope(query: str, claimed: SubjectScope) -> SubjectScope:
|
||||
"""The scope that actually gates retrieval: the more conservative of what
|
||||
the caller claimed and what the query text itself indicates."""
|
||||
if claimed == SubjectScope.NON_HUMAN or looks_non_human(query):
|
||||
return SubjectScope.NON_HUMAN
|
||||
if claimed == SubjectScope.UNKNOWN:
|
||||
return SubjectScope.UNKNOWN
|
||||
return SubjectScope.HUMAN
|
||||
Reference in New Issue
Block a user