import copy import json from tools.rag.build_chunks import TOKEN_COUNTER from tools.rag.llm_context import compact_rag_payload def response(text="Unikátny dôkaz o práci osoby. "): return { "status": "ok", "query": "Práca?", "engine": "test", "strategies": ["test"], "complete": None, "truncated": False, "truncation_reasons": [], "index_version": {"version": "test"}, "instructions": ["Cituj iba dodané zdroje."], "answer_format": {}, "sources": [ { "source_id": "S1", "document_path": "pages/eva/README.md", "title": "Eva", "source_url": "https://example.test/eva", "text": text, "query_evidence": {"huge_diagnostic": text * 10}, "retrieval": {"rank": 1}, } ], "context": text, } def test_compact_payload_contains_evidence_once_and_preserves_sources(): original = response() before = copy.deepcopy(original) compact = compact_rag_payload(original) assert original == before assert json.dumps(compact, ensure_ascii=False).count(original["sources"][0]["text"]) == 1 assert "text" not in compact["sources"][0] and "query_evidence" not in compact["sources"][0] assert compact["sources"][0]["source_url"] == "https://example.test/eva" assert compact["index_version"] == original["index_version"] assert compact_rag_payload(compact) == compact def test_entire_tool_json_is_bounded_and_marks_omitted_evidence(): original = response("Veľmi dlhý dôkaz 🧑🏽‍💻. " * 2000) original["sources"].append({**original["sources"][0], "source_id": "S2", "text": "Ďalší dôkaz"}) compact = compact_rag_payload(original, max_tokens=700) used = TOKEN_COUNTER.count(json.dumps(compact, ensure_ascii=False, separators=(",", ":"))) assert used <= 700 and compact["context_budget"]["used_tokens"] == used assert compact["complete"] is False and compact["truncated"] is True assert "llm_context_budget" in compact["truncation_reasons"] assert "KONIEC ZDROJA S1" in compact["context"] assert compact["source_count"] == 1 and "[OBSAH ZDROJA OREZANÝ]" in compact["context"] assert json.loads(compact["context"].splitlines()[1])["truncated"] is True def test_diagnostics_are_explicit_and_never_forwarded_by_projection(): verbose = compact_rag_payload(response(), include_diagnostics=True) assert verbose["diagnostics"]["sources"][0]["query_evidence"] projected = compact_rag_payload(verbose) assert "diagnostics" not in projected assert "huge_diagnostic" not in json.dumps(projected) def test_http_serialization_stays_within_budget_and_diagnostics_are_opt_in(monkeypatch): from fastapi.testclient import TestClient import app.main as main import app.routes as routes original = response("Dlhý dôkaz 🐈 " * 1800) monkeypatch.setattr(main, "embed_query", lambda _: None) monkeypatch.setattr(routes, "build_rag_context", lambda *a, **kw: original) monkeypatch.setenv("RAG_LLM_MAX_TOKENS", "700") with TestClient(main.app) as client: headers = {"X-API-Key": "a" * 64} compact = client.post("/rag", json={"query": "Práca?"}, headers=headers) assert compact.status_code == 200 assert TOKEN_COUNTER.count(compact.text) <= 700 assert "diagnostics" not in compact.json() assert "text" not in compact.json()["sources"][0] verbose = client.post( "/rag", json={"query": "Práca?", "include_diagnostics": True}, headers=headers ) assert verbose.status_code == 200 and verbose.json()["diagnostics"] == original