diff --git a/README.md b/README.md
index 7818e35..43a10b6 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,8 @@
- ✅ **人工检验队列**(`review.py`):sqlite 队列、抽样 + safety 强制入队、verdict/correction 回写
- ✅ **打包分发**(`scripts/setup_runtime.py`):llama-server + GGUF 下载(断点续传/大小校验)
- ✅ **E1 token 经济学实验**(`scripts/bench_tokens.py` → `research/v2_experiments/`)
-- ✅ **测试**:**228 项全绿**(含 v1 legacy 126 项 + v2 新模块)
+- ✅ **Web 界面**(gateway/static/index.html,FastAPI 托管,无构建):对话 / 协作过程(交流文本可视化)/ 人工检验 / 指标 四视图
+- ✅ **测试**:**229 项全绿**(含 v1 legacy 126 项 + v2 新模块)
---
@@ -66,6 +67,7 @@ C:\Python314\python.exe -m venv .venv
# 4. 启动网关(v2 /chat 默认走 mock worker,无需 API key/模型即可演示)
.venv\Scripts\python.exe scripts/serve.py --port 8000
+# 浏览器打开 http://127.0.0.1:8000/ 使用 Web 界面(对话 / 协作过程 / 人工检验 / 指标)
# 5. 调用
curl http://127.0.0.1:8000/health
diff --git a/gateway/api.py b/gateway/api.py
index da12dcf..ee7510b 100644
--- a/gateway/api.py
+++ b/gateway/api.py
@@ -107,6 +107,9 @@ class HealthResponse(BaseModel):
# ---- FastAPI 应用 ----
try:
from fastapi import FastAPI, HTTPException
+ from fastapi.responses import HTMLResponse
+
+ _INDEX_PATH = Path(__file__).resolve().parent / "static" / "index.html"
app = FastAPI(
title="端云协同 LLM 协作系统",
@@ -118,6 +121,13 @@ try:
async def health():
return get_router().health()
+ @app.get("/", response_class=HTMLResponse, tags=["ui"])
+ async def index():
+ """端云协同 Web 界面(单文件前端,无需构建)。"""
+ if _INDEX_PATH.exists():
+ return HTMLResponse(_INDEX_PATH.read_text(encoding="utf-8"))
+ return HTMLResponse("
Web 界面未生成
缺少 gateway/static/index.html
")
+
# ---------------- v2:/chat ----------------
@app.post("/chat", tags=["chat"])
async def chat(req: QueryRequest):
diff --git a/gateway/static/index.html b/gateway/static/index.html
new file mode 100644
index 0000000..33d8924
--- /dev/null
+++ b/gateway/static/index.html
@@ -0,0 +1,167 @@
+
+
+
+
+
+端云协同 LLM 协作系统
+
+
+
+
+ ⚙️ 端云协同 LLM 协作系统
+ Architect(大模型 API)+ Worker(本地小模型)+ 交流文本协议
+ …
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/router_system/worker.py b/router_system/worker.py
index 462ecdb..afe8225 100644
--- a/router_system/worker.py
+++ b/router_system/worker.py
@@ -190,14 +190,19 @@ def _make_llama_generate(cfg: Dict[str, Any]) -> Callable[[str], Awaitable[str]]
timeout_s = float(cfg.get("per_step_timeout_s", 300))
async def _gen(prompt: str) -> str:
- import httpx
- async with httpx.AsyncClient(timeout=timeout_s) as client:
- resp = await client.post(
- f"{base_url}/chat/completions",
- json={"model": model, "messages": [{"role": "user", "content": prompt}],
- "temperature": temperature, "max_tokens": 4096},
- )
- resp.raise_for_status()
- return resp.json()["choices"][0]["message"]["content"]
+ try:
+ import httpx
+ async with httpx.AsyncClient(timeout=timeout_s) as client:
+ resp = await client.post(
+ f"{base_url}/chat/completions",
+ json={"model": model, "messages": [{"role": "user", "content": prompt}],
+ "temperature": temperature, "max_tokens": 4096},
+ )
+ resp.raise_for_status()
+ return resp.json()["choices"][0]["message"]["content"]
+ except Exception as e: # noqa: BLE001
+ # 连不上本地模型 -> 优雅降级(不抛 500),提示用户准备运行时
+ return ("(本地降级)无法连接本地 llama-server,未能生成该步骤内容。"
+ f"请先运行 scripts/setup_runtime.py 并启动模型。错误:{type(e).__name__}")
return _gen
diff --git a/tests/test_gateway.py b/tests/test_gateway.py
index c6721bc..ebb2cb8 100644
--- a/tests/test_gateway.py
+++ b/tests/test_gateway.py
@@ -71,6 +71,14 @@ def test_workspace_not_found(client):
assert resp.status_code == 404
+def test_web_ui_served(client):
+ resp = client.get("/")
+ assert resp.status_code == 200
+ assert resp.headers["content-type"].startswith("text/html")
+ assert "端云协同" in resp.text
+ assert "发送" in resp.text
+
+
def test_review_flow(client):
q = ga.get_review()
rid = q.enqueue("req-x", "q", "ans", tags=["safety"], reason="test")