Files
2026-09-21 11:19:07 +08:00

79 lines
2.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""开发档 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")