Add read-only production runtime audit
This commit is contained in:
@@ -5,7 +5,7 @@ from fastapi.testclient import TestClient
|
||||
from adapters.prometheus import PrometheusMetrics
|
||||
from adapters.postgres import FeedbackTraceNotFound, RetrievalTrace
|
||||
from config import Settings
|
||||
from main import create_app
|
||||
from main import _route_label, create_app
|
||||
from rag.agent import AgentReply
|
||||
from rag.answer import DISCLAIMER, Citation, GroundedAnswerService
|
||||
from rag.metrics import TRACE_WRITE_FAILED, InMemoryMetrics
|
||||
@@ -150,6 +150,18 @@ def test_history_for_unknown_conversation_is_empty_not_an_error():
|
||||
assert response.json() == {"items": []}
|
||||
|
||||
|
||||
def test_history_rejects_an_oversized_conversation_id_before_querying_storage():
|
||||
traces = FakeHistoryTraceWriter({})
|
||||
app = create_app(settings=Settings(), trace_writer=traces)
|
||||
|
||||
response = TestClient(app).get(
|
||||
"/v1/rag/history", params={"conversation_id": "x" * 129}
|
||||
)
|
||||
|
||||
assert response.status_code == 422
|
||||
assert traces.calls == []
|
||||
|
||||
|
||||
def test_health_and_fail_closed_rag_response_are_traced():
|
||||
traces = MemoryTraceWriter()
|
||||
app = create_app(
|
||||
@@ -204,6 +216,19 @@ def _metrics_app(**settings_kwargs):
|
||||
)
|
||||
|
||||
|
||||
def test_all_public_rag_endpoints_have_bounded_request_metric_labels():
|
||||
paths = (
|
||||
"/v1/rag/query",
|
||||
"/v1/rag/suggest",
|
||||
"/v1/rag/feedback",
|
||||
"/v1/rag/history",
|
||||
"/v1/rag/sections",
|
||||
"/v1/rag/section-text",
|
||||
)
|
||||
|
||||
assert {_route_label(path) for path in paths} == set(paths)
|
||||
|
||||
|
||||
def test_metrics_stays_open_when_no_token_is_configured():
|
||||
"""The default must not break the existing Compose scrape or local runs —
|
||||
the endpoint is not internet-reachable in that topology."""
|
||||
@@ -252,10 +277,15 @@ class FakeAgent:
|
||||
|
||||
def __init__(self, reply: AgentReply) -> None:
|
||||
self._reply = reply
|
||||
self.calls: list[tuple[str, str | None]] = []
|
||||
self.calls: list[tuple[str, str | None, str]] = []
|
||||
|
||||
def handle(self, turn: str, conversation_id: str | None = None) -> AgentReply:
|
||||
self.calls.append((turn, conversation_id))
|
||||
def handle(
|
||||
self,
|
||||
turn: str,
|
||||
conversation_id: str | None = None,
|
||||
response_mode: str = "ai",
|
||||
) -> AgentReply:
|
||||
self.calls.append((turn, conversation_id, response_mode))
|
||||
return self._reply
|
||||
|
||||
def complete(self, prefix: str, k: int = 8) -> list[str]:
|
||||
@@ -291,7 +321,34 @@ def test_query_routes_through_the_agent_when_one_is_configured():
|
||||
assert body["answer"] == "Liều 500 mg [1]."
|
||||
assert body["resolved_drug_id"] == "metformin"
|
||||
assert len(body["citations"]) == 1
|
||||
assert agent.calls == [("Liều metformin?", "c1")]
|
||||
assert agent.calls == [("Liều metformin?", "c1", "ai")]
|
||||
|
||||
|
||||
def test_query_forwards_monograph_response_mode_to_agent():
|
||||
agent = FakeAgent(AgentReply(
|
||||
decision="clarify",
|
||||
reason="select_drug_sections",
|
||||
clarification="Chọn mục cần xem.",
|
||||
drugs=("metformin",),
|
||||
turn_type="drug_overview",
|
||||
))
|
||||
app = create_app(
|
||||
settings=Settings(),
|
||||
answer_service=GroundedAnswerService(FixedRouting()),
|
||||
conversational=agent,
|
||||
trace_writer=MemoryTraceWriter(),
|
||||
)
|
||||
|
||||
response = TestClient(app).post("/v1/rag/query", json={
|
||||
"query": "Metformin",
|
||||
"subject_scope": "human",
|
||||
"intent": "fact_lookup",
|
||||
"response_mode": "monograph",
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["reason"] == "select_drug_sections"
|
||||
assert agent.calls == [("Metformin", None, "monograph")]
|
||||
|
||||
|
||||
def test_query_agent_clarification_is_surfaced_as_the_answer():
|
||||
|
||||
Reference in New Issue
Block a user