projects/repobot/v4/static/index.html
71 lignes · 2.6 KoLe code et les sorties des programmes sont reproduits tels qu’ils ont tourné : commentaires et sorties sont donc en chinois.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>RepoBot · httpx 答疑助手</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 760px; margin: 0 auto; padding: 16px; line-height: 1.6; }
#log { white-space: pre-wrap; }
.user { font-weight: 600; margin-top: 16px; }
.step { color: #888; font-size: 13px; }
.meta { color: #888; font-size: 12px; margin-bottom: 8px; }
form { display: flex; gap: 8px; margin-top: 16px; }
input { flex: 1; padding: 8px; font-size: 15px; }
</style>
</head>
<body>
<h2>RepoBot</h2>
<p class="meta">httpx 答疑助手。会查官方文档和源码,并注明出处。</p>
<div id="log"></div>
<form id="form"><input id="q" placeholder="比如:httpx 默认会跟随重定向吗?" autofocus><button>发送</button></form>
<script>
const log = document.getElementById("log");
const history = [];
function add(cls, text) {
const div = document.createElement("div");
div.className = cls;
div.textContent = text;
log.appendChild(div);
return div;
}
document.getElementById("form").onsubmit = async (e) => {
e.preventDefault();
const input = document.getElementById("q");
const message = input.value.trim();
if (!message) return;
input.value = "";
add("user", "你:" + message);
const answer = add("answer", "");
// EventSource 只能发 GET,这里用 fetch 发 POST,再自己按行解析 SSE
const resp = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message, history }),
});
const reader = resp.body.getReader();
const decoder = new TextDecoder();
let buffer = "", text = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const events = buffer.split("\n\n");
buffer = events.pop(); // 最后一段可能还没收完整,留到下次
for (const e of events) {
if (!e.startsWith("data: ")) continue;
const ev = JSON.parse(e.slice(6));
if (ev.type === "tool") log.insertBefore(Object.assign(document.createElement("div"), { className: "step", textContent: "正在调用 " + ev.name + " " + ev.args }), answer);
if (ev.type === "token") { text += ev.text; answer.textContent = text; }
if (ev.type === "done") add("meta", `${ev.steps} 次模型调用,${ev.tool_calls} 次工具调用,${ev.cost} 美元`);
}
}
history.push({ role: "user", content: message }, { role: "assistant", content: text });
};
</script>
</body>
</html>