资讯详情

第 22-1 篇:chat.html——前端如何消费 SSE

📅 2026/9/27 22:55:41 | 华诺云谱 👁 阅读
第 22-1 篇:chat.html——前端如何消费 SSE
上一篇21-3《内嵌 HTMLgen_embedded_web.py 怎么把页面塞进二进制》下一篇22-2《/admin API状态、配置、加载、转换》源码精读篇本文为源码/方法论精读无独立实测文中数字均引述仓库 docs 的板端实测记录一句话导读chat.html 前端如何消费 SSE服务端会发、前端怎么收——为何不用只读的 EventSource而用 fetch 加 ReadableStream 逐行切事件发送前又怎样借 admin 状态卡住未加载模型。关键词chat.html、SSE、ReadableStream、EventSource、admin API服务端 SSE 会发了20-3前端/chat页面怎么收这篇读src/serve/chat.html的流式消费代码——它没用EventSource那是 GET-only而是fetchReadableStream逐行读。顺带看它如何用/admin/api/status在发送前卡住模型没加载就不许发。1. 知识点POST 流式为什么不能用 EventSourceEventSource是浏览器原生 SSE 客户端但它只支持 GET——而/v1/chat/completions要 POST JSON。所以前端只能用fetch拿到响应流后自己按 SSE 格式切事件fetch(url, {method:POST, body: json}) → resp.body.getReader() // ReadableStream → 按 \n\n 切出每个事件 → 剥掉 data: 前缀 → JSON.parse → 追加到文本框这套手搓 SSE 消费和引擎手搓 SSE 生产vllm_http.c是对称的生产端不引库、消费端也不引库两端加起来 ~100 行。这正是零依赖风格的完整闭环——依赖是双向省掉的。2. 对应代码chat.html 的流式函数chat.html的关键锚点/* 162 行附近发送前查引擎状态 */ const s await fetch(/admin/api/status).then(r r.json()); // 模型没加载(load_state!2)就不许发 /* 397 行请求体带上 stream 开关 */ stream: $(p_stream).value 1 /* 447 行起流式主循环 streamRequest */ async function streamRequest(bodyObj, aiText, t0, onRaw){ const resp await fetch(/v1/chat/completions, { ... }); // POST const reader resp.body.getReader(); // ReadableStream // 边读边切 data: 事件逐段 append统计 genCount / 计算 tok/s 显示 }值得注意的两个工程点打字机效果的实现onRaw逐段回调把原文追加进文本框——页面上一个字一个字出现不是 CSS 动画是 SSE 每 50ms 一个 token 的真实节奏与 20-3 实测一致状态先行发送按钮的可用性由/admin/api/status的load_state决定162–170 行——前端先问模型加载了吗这比发了再等 400体验好一个数量级也说明 22-2 的 admin API 不是后端自嗨前端真在消费它。3. 改动后果前端把 SSE 当 JSON 读会怎样把streamRequest里逐事件切分改成等全部读完再 JSON.parse新手常犯后果实测可推演、也可在浏览器 DevTools 复现改前请求发出 ~130ms 后文本框开始出字角色帧先到随后逐 token 追加 改后整个请求期间文本框空白生成完才一次性出现整段——打字机效果消失 且请求超时风险上升长回答要等全部 decode 完才落盘到响应体这个改动后果不需要跑代码就能理解SSE 的全部价值在时间分布上消费端一旦缓冲生产端的逐 token 就白做了。在 DevTools 的 Network 面板看EventStream类型的响应体是逐块到达、还是攒包是最直接的验证。4. 学员调试任务A 档浏览器 板端 serve打开http://板IP:8801/chatDevTools → Network → 发一条流式消息点开该请求看响应体是否逐块增长EventStream 视图在streamRequest里加一行console.log(performance.now(), chunk)观察每个 chunk 的时间戳应 ~50ms 一跳把p_stream切到一次性 JSON对比 Network 面板里响应体一次性到达与流式的差异。B 档纯读源码读chat.html145–200 与 400–490回答① 为什么用getReader()手动切事件而不是resp.text()提示后者要等流结束②genCount/streamT0148–149 行被用来算什么、对用户体验有什么意义③ 若模型在流式中途被卸载auto_unload前端会看到什么、代码里有没有处理预期输出一张 DevTools 抓到的 SSE 分块时间轴截图 对你板子首字延迟与逐字节奏的读数。收尾本篇源码点名chat.html状态门 162–170、streamRequest 447 起开源仓库Kestrel-LLM (Gitee)AGPL-3.0-or-later 或商业许可二选一下篇预告前端偷偷调的那些/admin/api/*服务端到底给了什么22-2 逐个拆状态/配置/加载/转换四组接口的返回结构。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑