"""开发档 Mock 模型服务:OpenAI 兼容 /chat/completions SSE 流式回声。 仅供本地联调 Run 流水线(事件序列、芽体渲染、对账),不模拟任何真实模型能力。 生产部署不启动本服务(与 /v1/webhooks/v1/payments/mock 同一开发档惯例)。 启动:uv run python scripts/mock_model.py (127.0.0.1:8001) 鉴权:任意 Bearer 均接受(联调端点,只监听回环地址)。 """ import asyncio import json from fastapi import FastAPI, Request from fastapi.responses import StreamingResponse app = FastAPI(title="Hunjing Dev Mock Model", docs_url=None) REPLY = ( "收到了。按当前镜域的状态,我先把这件事拆成两步:先核对读书会的偏好记忆," "再给出一份可归真的结果。这个回复由开发档 Mock 模型逐字流出," "用于验证 content.block.delta 事件序列、临时芽体渲染与 message.completed 对账。" ) @app.get("/healthz") async def healthz() -> dict[str, str]: return {"status": "ok", "env": "development", "mock": "true"} @app.post("/v1/chat/completions") async def chat_completions(request: Request) -> StreamingResponse: body = await request.json() user_text = "" for m in reversed(body.get("messages", [])): if m.get("role") == "user" and m.get("content"): user_text = str(m["content"])[-60:] break text = f"(mock 回应 · 你说:「{user_text}」)\n{REPLY}" async def gen(): # 逐字流出,模拟真实 TTFT 与节奏 for ch in text: await asyncio.sleep(0.02) yield ( "data: " + json.dumps( {"choices": [{"index": 0, "delta": {"content": ch}, "finish_reason": None}]}, ensure_ascii=False, ) + "\n\n" ) yield ( "data: " + json.dumps( {"choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}]}, ensure_ascii=False, ) + "\n\n" ) yield ( "data: " + json.dumps( { "choices": [], "usage": { "prompt_tokens": 32, "completion_tokens": len(text), "total_tokens": 32 + len(text), }, }, ensure_ascii=False, ) + "\n\n" ) yield "data: [DONE]\n\n" return StreamingResponse(gen(), media_type="text/event-stream")