Files
CyberGril/web
xiaomoandmultica-agent 958c6962fb HEL-908 R1/R2 rework (HEL-909 review): whole-turn reasoning gate + cg.interrupt data channel
R1 — reasoning separation truly closed. The streaming hold leaked wholesale
on real token arrival (review A1: 'private reasoning '*20 beyond the 128-char
bound, no sentence ender; review A2: a Chinese sentence ender released the
hold) AND the adapter consumed the stray closer, so the app-level turn split
could never recover — the previous 'persistence and receipt always clean'
claim was false and is retracted. Per the engineering ruling the adapter now
has two explicit endpoint modes (GOV-006, no heuristics, no new engine):
buffer_turn (TurnReasoningGate) emits NOTHING during generation, splits the
whole turn at stream end and only then delivers the clean visible text; it
is set explicitly for all tiers in config/models.yaml (first visible text
waits for the full generation; cancelled/timed-out turns emit nothing and
end exactly as before — recorded and test-pinned, low latency NOT claimed).
stream (default, backward compatible) keeps explicit-tag/field semantics and
passes stray closers through unmodified so the turn-level split keeps its
signal. Both review strings and the recorded id=12 stream replay clean at
every chunk size through the real adapter and the real app into chat.delta,
the archive and the receipt; normal zh/en preserved byte-for-byte; unclosed
explicit thinking still fails honestly.

R2 — an authoritative interrupt when the LLM already finalized and TTS is
still playing (review evidence C: zero interrupts, old audio played out).
The page's existing hush() entry (ChatPanel untouched) now also publishes
{"v":1} on the room's own reliable data channel topic cg.interrupt
(fire-and-forget: a dropped publish never blocks or breaks the text send);
the worker subscribes data_received, validates topic/sender/payload and
calls session.interrupt(force=True) — the SDK's own programmatic interrupt,
force confirmed against the installed 1.8.8 source (non-force raises
RuntimeError on allow_interruptions=False speech). Failures log loudly; the
listener is unloaded via add_shutdown_callback; the server-cancel path in
llm_chat_api also interrupts with force and logs failures.

Verification (offline, zero real requests): real-SDK harness 8/8 including a
real SpeechHandle with a genuinely pending playout wait released by the
cg.interrupt message after a clean chat.final (evidence-C shape) plus the
full validation matrix; .venv311 full suite 480 passed / 8 skipped (2
test_r3 failures pre-exist at 5625de6); web npm ci from the existing lock,
typecheck 0 errors, lint 0 errors + 1 pre-existing warning, vitest 148/148,
build ok; ruff clean; check_requirements vs origin/main and b8ec832 with the
corrected handoff.

Co-authored-by: multica-agent <github@multica.ai>
2026-10-28 22:21:16 +08:00
..

CyberGril S1 前端(HEL-748)

独立 React / TypeScript / Vite 工程。Python 核心保持原样;不是新的后端。视觉基于获批 HEL-746 R2 的 Token、组件样式和两张参考静帧,页面由真实 React 组件实现,没有嵌入原型 HTML。

cd web
npm ci
npm run dev                  # 仅本机,终端给出 URL
# 普通入口 / 说明真实服务未接通;点入口或访问 /?demo=1
npm run typecheck
npm run lint
npm test
npm run build
npm run preview              # 仅本机预览生产构建

Node 22+ 推荐(本轮 Node 24.16.0 / npm 11.13.0 验证),锁文件已提交。不需要 API Key、模型或 GPU。没有运行时 CDN 请求。

首次进入演示需填写名字、生辰、关系并确认。创建保存在浏览器 cybergril.demo.v1;刷新保留同一位档案。没有切换或清空重建入口。cg.theme 保留主题,sessionStorage 的 cg.draft 保留交流草稿。资料只在当前浏览器/站点 origin;它不代表服务器已创建、记忆已入库或后台仍在生活。保存不可用时会报错,不伪装成功。损坏资料会显示恢复说明,不自动覆盖。

演示场景入口可载入固定的三天/长记录、模拟离开返回、主动消息、断线和操作失败。只有明确 ?demo=1 才有该入口,不能切换隐私规则。首建后生活默认空白;固定样例须主动载入,不自行编造离线生活。

本地演示 JSON 可导出、恢复同一位档案;恢复需输入“恢复”,先下载恢复前数据。它不含真实数据库和媒体文件,不能称为后台完整备份。素材导入只校验演示 JSON 的结构与标识,媒体格式/时长/人物一致性均未检测,清单中的 URL 不会加载。可用示例在 public/demo-material.json。

浏览器测试:先构建,再在已安装 Python Playwright 和 Chromium 及其依赖的环境执行:

python3 web/tests/browser_check.py    # 在仓库根目录
python3 web/tests/import_check.py
python3 -m pytest -q                 # 现有 Python 核心,仓库根目录

脚本在随机 loopback 端口同步测试静态产物,结束关闭浏览器和服务;截图写入 docs/frontend/screenshots/(不进入前端产物)。没有持续预览服务或部署。真实手机、Safari、真实麦克风/摄像头、ASR/TTS、后台生活和口型均未测试。

语音(HEL-878,R5 前端接线):交谈页麦克风按钮按 GET /api/voice/status 诚实显示;用户确认后才申请麦克风、POST /api/voice/session 取短时 token,用 public/vendor/ 下既有 livekit-client 加入房间(来源见 VENDOR.md)。状态、字幕、结束与清理逻辑在 src/voice/。离线检查:npm test(含 tests/voice.test.ts),构建后 node tests/hel878_mock.mjs 8878 + node tests/hel878_shots.mjs <msedge.exe> <outDir> 8878(假 LiveKit + 浏览器假音频设备,不是真实通话)。真实通话、真机、Safari、回声与打断时延均未测。

接线边界、交付与测试记录见 docs/frontend/IMPLEMENTATION.md。