-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathdriver.py
More file actions
129 lines (112 loc) · 4.02 KB
/
Copy pathdriver.py
File metadata and controls
129 lines (112 loc) · 4.02 KB
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
"""Ad-hoc real-API eval driver for PaperHub (uncommitted benchmark harness).
Drives the user's live backend on :8000 exactly as the frontend would:
POST /sessions -> session_id
POST /papers -> attach a reference (library:<pc_id>, cheap dedup hit)
POST /chat -> stream SSE, collect routing intent / tokens / deck / run_id
Run with: uv run python benchmark/driver.py (smoke test)
Imported by run_eval.py for the full 20-case sweep.
"""
from __future__ import annotations
import json
from dataclasses import dataclass, field
from typing import Any
import httpx
BASE = "http://127.0.0.1:8000"
@dataclass
class ChatResult:
run_id: int | None = None
session_id: int | None = None
intent: str | None = None
routing: dict[str, Any] | None = None
final: str = ""
deck: dict[str, Any] | None = None
search_results: list[dict[str, Any]] = field(default_factory=list)
error: str | None = None
events: list[str] = field(default_factory=list)
def create_session() -> int:
r = httpx.post(f"{BASE}/sessions", timeout=30)
r.raise_for_status()
return int(r.json()["session_id"])
def add_paper(session_id: int, pc_id: int) -> dict[str, Any]:
"""Attach an already-ingested paper_content row to the session (dedup hit)."""
r = httpx.post(
f"{BASE}/papers",
json={"session_id": session_id, "paper_id": f"library:{pc_id}"},
timeout=120,
)
r.raise_for_status()
return r.json()
def _parse_sse(text: str) -> list[tuple[str, str]]:
"""Yield (event, data) pairs from a raw SSE byte stream."""
out: list[tuple[str, str]] = []
event = "message"
data_lines: list[str] = []
for line in text.splitlines():
if line == "":
if data_lines:
out.append((event, "\n".join(data_lines)))
event = "message"
data_lines = []
continue
if line.startswith("event:"):
event = line[len("event:"):].strip()
elif line.startswith("data:"):
data_lines.append(line[len("data:"):].strip())
if data_lines:
out.append((event, "\n".join(data_lines)))
return out
def chat(
session_id: int,
user_message: str,
*,
current_view_page: int = 0,
slide_attached: bool = False,
timeout: float = 1800.0,
) -> ChatResult:
res = ChatResult(session_id=session_id)
tokens: list[str] = []
payload = {
"session_id": session_id,
"user_message": user_message,
"current_view_page": current_view_page,
"slide_attached": slide_attached,
}
with httpx.stream(
"POST", f"{BASE}/chat", json=payload, timeout=timeout
) as r:
r.raise_for_status()
buf = ""
for chunk in r.iter_text():
buf += chunk
for event, data in _parse_sse(buf):
res.events.append(event)
try:
obj = json.loads(data) if data else {}
except json.JSONDecodeError:
obj = {}
if event == "session":
res.run_id = obj.get("run_id")
elif event == "routing_decision":
res.routing = obj.get("decision") or obj
res.intent = (res.routing or {}).get("intent")
elif event == "token":
tokens.append(obj.get("text", ""))
elif event == "final":
res.final = obj.get("content", "") or "".join(tokens)
elif event == "deck":
res.deck = obj
elif event == "search_results":
res.search_results = obj.get("candidates", [])
elif event == "error":
res.error = obj.get("message", "")
if not res.final and tokens:
res.final = "".join(tokens)
return res
if __name__ == "__main__":
sid = create_session()
print("session:", sid)
info = add_paper(sid, 52) # Attention Is All You Need
print("added:", info["title"], "cache_hit=", info.get("cache_hit"))
out = chat(sid, "What is the purpose of multi-head attention in this paper?")
print("run_id:", out.run_id, "intent:", out.intent)
print("final[:600]:", out.final[:600])