refactor: retire the KB-recall bridge — WT #383 native reference_knowledge (b167)
Worldtree #383 shipped native Tier-3 reference_knowledge (v1.0.0b167, live on :8081 + demo): every Tier-3 agent context now carries the tool automatically, with evidence packets (note_id + path provenance, confidence bucket) and a server-side grounding rule. That supersedes the interim consumer-side memory_context pinning bridge (slice 3), so it is deleted per its INV-KB-1 retire seam. Removed: - src/ratatoskr/kb_bridge.py + tests/test_kb_bridge.py (the whole module). - server.py: the pin_kb_context import + the single turn-path call-site (reverted to the pre-bridge wt.stream_turn call), the SSE keepalive that only covered the consult delay, and the bridge-only agent_id plumbing (TurnHandle.agent_id + the submit read). - index.html: agent_id dropped from the turn POST body. - test_web_server.py: TestKbBridgeWiring (tested the removed call-site). Kept: - wt.stream_turn's memory_context param (inert SDK-parity passthrough; worldtree-dev concurred it stays) + its forwarding tests. - the non-str content 400 guard (general input hygiene, not bridge-specific). Retirement LIVE-VERIFIED before deletion: a Donut session on :8081/b167 carries builtin_tools=['reference_knowledge']; she called it and grounded in the DCC Collapse content fully in-voice, degrading gracefully on absent content. 524 green. Contract marks slice-3 RETIRED (historical record retained). #383 closed.
This commit is contained in:
@@ -1575,85 +1575,3 @@ class TestTtsEndpoint:
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
class TestKbBridgeWiring:
|
||||
"""The KB-recall bridge seam (slice 3, INV-KB-1): the turn path calls
|
||||
pin_kb_context and forwards its result as memory_context on the turn POST.
|
||||
Proves the ONE call-site is wired end-to-end (the consult itself is unit-tested
|
||||
in test_kb_bridge.py; here we stub it to assert the plumbing)."""
|
||||
|
||||
@respx.mock
|
||||
def test_interview_agent_pins_context_into_turn_post(self, monkeypatch) -> None:
|
||||
from ratatoskr.web import server
|
||||
from ratatoskr.web.server import create_app
|
||||
|
||||
seen = {}
|
||||
|
||||
async def _fake_pin(question, agent_id, *, client):
|
||||
seen["question"] = question
|
||||
seen["agent_id"] = agent_id
|
||||
return [{"kind": "corpus_reference", "text": "grounded passage"}]
|
||||
|
||||
monkeypatch.setattr(server, "pin_kb_context", _fake_pin)
|
||||
route = respx.post("https://w.example/sessions/s-1/messages").mock(
|
||||
return_value=_sse_resp(_sse_chunk("42:1", _DONE_BODY))
|
||||
)
|
||||
c = TestClient(create_app(_mock_client_factory()))
|
||||
tid = c.post(
|
||||
"/api/turns/s-1",
|
||||
json={"content": "who is Carl?", "agent_id": "ratatoskr:donut"},
|
||||
).json()["turn_id"]
|
||||
with c.stream("GET", f"/api/turns/s-1/stream?turn_id={tid}") as resp:
|
||||
b"".join(resp.iter_bytes())
|
||||
# The browser-sent agent_id reached the bridge, and its result rode the POST.
|
||||
assert seen == {"question": "who is Carl?", "agent_id": "ratatoskr:donut"}
|
||||
body = json.loads(route.calls.last.request.content)
|
||||
assert body["memory_context"] == [
|
||||
{"kind": "corpus_reference", "text": "grounded passage"}
|
||||
]
|
||||
|
||||
@respx.mock
|
||||
def test_non_interview_agent_pins_nothing(self) -> None:
|
||||
# Real pin_kb_context: a non-allowlisted agent short-circuits to [] (no consult),
|
||||
# so memory_context is absent from the turn POST body (SDK omits None).
|
||||
from ratatoskr.web.server import create_app
|
||||
|
||||
route = respx.post("https://w.example/sessions/s-1/messages").mock(
|
||||
return_value=_sse_resp(_sse_chunk("42:1", _DONE_BODY))
|
||||
)
|
||||
c = TestClient(create_app(_mock_client_factory()))
|
||||
tid = c.post(
|
||||
"/api/turns/s-1", json={"content": "hi", "agent_id": "mimir"}
|
||||
).json()["turn_id"]
|
||||
with c.stream("GET", f"/api/turns/s-1/stream?turn_id={tid}") as resp:
|
||||
b"".join(resp.iter_bytes())
|
||||
body = json.loads(route.calls.last.request.content)
|
||||
assert "memory_context" not in body or body["memory_context"] is None
|
||||
|
||||
@respx.mock
|
||||
def test_failed_consult_still_streams_the_turn(self, monkeypatch) -> None:
|
||||
# A degraded/failed KB consult (returns []) must NOT block or fail the character
|
||||
# turn — it streams to `done` with no pinned context (INV-KB-3, server-level;
|
||||
# heid-code-review F9: the turn-path failure seam was untested end to end).
|
||||
from ratatoskr.web import server
|
||||
from ratatoskr.web.server import create_app
|
||||
|
||||
async def _degraded_pin(question, agent_id, *, client):
|
||||
return [] # consult failed/timed-out internally → degraded to []
|
||||
|
||||
monkeypatch.setattr(server, "pin_kb_context", _degraded_pin)
|
||||
respx.post("https://w.example/sessions/s-1/messages").mock(
|
||||
return_value=_sse_resp(
|
||||
_sse_chunk("42:1", {"type": "text", "content": "hi"})
|
||||
+ _sse_chunk("42:2", _DONE_BODY)
|
||||
)
|
||||
)
|
||||
c = TestClient(create_app(_mock_client_factory()))
|
||||
tid = c.post(
|
||||
"/api/turns/s-1",
|
||||
json={"content": "who is Carl?", "agent_id": "ratatoskr:donut"},
|
||||
).json()["turn_id"]
|
||||
with c.stream("GET", f"/api/turns/s-1/stream?turn_id={tid}") as resp:
|
||||
raw = b"".join(resp.iter_bytes())
|
||||
events = [e["event"] for e in _parse_browser_sse(raw)]
|
||||
assert "text" in events and "done" in events # the turn completed normally
|
||||
|
||||
Reference in New Issue
Block a user