Spaces:
Running
Running
File size: 6,620 Bytes
4f45521 3177137 00bdb21 3177137 4f45521 3177137 36673b7 3177137 00bdb21 3177137 00bdb21 3177137 fcd5303 3177137 39fd271 00bdb21 fcd5303 00bdb21 a9fcad0 eb514f3 daae350 eb514f3 daae350 eb514f3 a9fcad0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | import pytest
from agent.schemas import Answer, Citation, Referral
from app.main import THINKING_NOTE, _pipeline, render, respond
@pytest.fixture(autouse=True)
def _disable_guard(monkeypatch):
# the guard is exercised in tests/agent/test_guard.py; here it would try to
# load a real model / hit the DB, so switch it off for the render/respond tests
monkeypatch.setenv("TORCHDOCS_GUARD", "0")
def test_render_includes_answer_citations_referrals():
answer = Answer(
answer_md="Use `torch.optim.SGD`.",
torch_version="2.12",
citations=[
Citation(
url="https://docs.pytorch.org/docs/stable/generated/torch.optim.SGD.html",
anchor="torch.optim.SGD",
title="torch.optim.SGD",
)
],
referrals=[Referral(url="https://deepwiki.com/pytorch/pytorch", reason="source")],
)
md = render(answer)
assert "Use `torch.optim.SGD`." in md
assert "**Sources**" in md
assert "torch.optim.SGD.html#torch.optim.SGD" in md
assert "**Beyond these docs**" in md
assert "PyTorch 2.12" in md
# license link under the citations: text is the license name
assert "[BSD-3-Clause](https://github.com/pytorch/pytorch/blob/main/LICENSE)" in md
def test_render_no_license_note_without_citations():
# an answer that quoted nothing (empty index) shouldn't claim a source license
answer = Answer(answer_md="I could not find anything.", torch_version="unknown")
assert "BSD-3-Clause" not in render(answer)
def test_pipeline_empty_question():
assert "Ask me something" in _pipeline(" ")
def test_pipeline_never_crashes_and_never_leaks_the_error(monkeypatch):
def boom(q, **k):
raise RuntimeError("db-host.internal:5432 down")
monkeypatch.setattr("app.main.answer_routed", boom)
out = _pipeline("how do I use SGD?")
# the user gets a generic line; the exception text (hosts, slugs, config)
# goes to the logs only
assert "went wrong" in out
assert "db-host.internal" not in out
def test_respond_streams_the_thinking_note_then_the_answer(monkeypatch):
# the UI generator shows immediate feedback, then swaps in the final answer;
# the LAST value is what gradio_client (the smoke test) receives
monkeypatch.setattr(
"app.main.answer_routed", lambda q, **k: Answer(answer_md="the answer")
)
chunks = list(respond("how do I use SGD?"))
assert chunks[0] == THINKING_NOTE
assert "the answer" in chunks[-1] and chunks[-1] != THINKING_NOTE
def _cited_answer(text):
return Answer(
answer_md=text,
citations=[
Citation(
url="https://docs.pytorch.org/docs/stable/generated/torch.optim.SGD.html",
anchor="sgd",
title="torch.optim.SGD",
)
],
)
def test_respond_regenerates_when_the_cited_docs_drifted(monkeypatch):
# stale-while-revalidate: the answer ships first; when the freshness pass
# reports the cited page drifted, a REGENERATED answer is swapped in with
# the note — the last yielded value is what the user ends up seeing
from app.main import FRESHNESS_NOTE
calls = {"n": 0}
def routed(q, **k):
calls["n"] += 1
return _cited_answer("stale answer" if calls["n"] == 1 else "fresh answer")
monkeypatch.setattr("app.main.answer_routed", routed)
monkeypatch.setattr("index.freshness.refresh_pages", lambda urls: set(urls))
chunks = list(respond("how do I use SGD?"))
assert "fresh answer" in chunks[-1] and FRESHNESS_NOTE in chunks[-1]
assert calls["n"] == 2 # answered once, regenerated once
def test_respond_freshness_is_silent_when_nothing_drifted(monkeypatch):
monkeypatch.setattr("app.main.answer_routed", lambda q, **k: _cited_answer("the answer"))
monkeypatch.setattr("index.freshness.refresh_pages", lambda urls: set())
from app.main import THINKING_SPINNER
chunks = list(respond("how do I use SGD?"))
assert "the answer" in chunks[-1] and "regenerated" not in chunks[-1]
# the spinner under the answer is cleared once the check finishes
assert all(frame not in chunks[-1] for frame in THINKING_SPINNER)
def test_respond_keeps_the_answer_visible_while_verifying(monkeypatch):
# while the freshness check runs, the user must keep READING the answer —
# under it just a bare spinner (the wheel, no words), updated in place
import time
from app.main import THINKING_SPINNER
monkeypatch.setattr("app.main.THINKING_TICK", 0.02)
monkeypatch.setattr("app.main.answer_routed", lambda q, **k: _cited_answer("the answer"))
def slow_check(urls):
time.sleep(0.15) # several ticks → several verifying frames
return set()
monkeypatch.setattr("index.freshness.refresh_pages", slow_check)
chunks = list(respond("how do I use SGD?"))
verifying = [
c for c in chunks if "the answer" in c and any(f in c for f in THINKING_SPINNER)
]
assert verifying # the check was visible: answer + wheel together
# the wheel is BARE — no explanatory text rides along with it
assert all(c.endswith("</sub>") and "…" not in c.split("<sub>")[1] for c in verifying)
assert all(f not in chunks[-1] for f in THINKING_SPINNER) # gone when done
def test_respond_freshness_failure_never_disturbs_the_answer(monkeypatch):
monkeypatch.setattr("app.main.answer_routed", lambda q, **k: _cited_answer("the answer"))
def boom(urls):
raise RuntimeError("neon hiccup")
monkeypatch.setattr("index.freshness.refresh_pages", boom)
chunks = list(respond("how do I use SGD?"))
assert "the answer" in chunks[-1] # the shown answer stands
def test_respond_animates_the_wait_so_it_never_looks_frozen(monkeypatch):
# a multi-second answer must show MOVING feedback, not a single frozen line:
# between the note and the answer the generator emits animated spinner frames
import time
from app.main import THINKING_SPINNER
monkeypatch.setattr("app.main.THINKING_TICK", 0.02) # tick fast so frames land quickly
def slow(q, **k):
time.sleep(0.15) # several ticks → several frames while "drafting"
return Answer(answer_md="the answer")
monkeypatch.setattr("app.main.answer_routed", slow)
chunks = list(respond("how do I use SGD?"))
assert chunks[0] == THINKING_NOTE and "the answer" in chunks[-1]
frames = chunks[1:-1]
assert frames # the wait produced animation, not a frozen note
assert any(any(c in f for c in THINKING_SPINNER) for f in frames)
|