initial: blog-app snapshot
This commit is contained in:
128
.review-tmp/gen-extracted.py
Normal file
128
.review-tmp/gen-extracted.py
Normal file
@@ -0,0 +1,128 @@
|
||||
def generate():
|
||||
start_time = time.time()
|
||||
|
||||
yield f"event: step\ndata: {json.dumps({'step': 'context', 'content': f'已收集上下文: {data_summary}'}, ensure_ascii=False)}\n\n"
|
||||
yield f"event: step\ndata: {json.dumps({'step': 'prompt', 'content': full_prompt_display}, ensure_ascii=False)}\n\n"
|
||||
|
||||
try:
|
||||
resp = fake_post(
|
||||
"http://127.0.0.1:4000/v1/chat/completions",
|
||||
json={"model": "default", "messages": messages, "stream": True, "max_tokens": 1024},
|
||||
stream=True,
|
||||
timeout=120
|
||||
)
|
||||
resp.raise_for_status()
|
||||
|
||||
last_keepalive = time.time()
|
||||
full_text = ""
|
||||
# 滚动 buffer: 分隔符可能跨 chunk (如 "===REAS" + "ONING==="),
|
||||
# 保留尾部最长分隔符前缀长度的字符, 不立即下发
|
||||
DELIM_R = "===REASONING==="
|
||||
DELIM_C = "===CONCLUSION==="
|
||||
holdback = max(len(DELIM_R), len(DELIM_C)) - 1
|
||||
pending = ""
|
||||
reasoning_started = False
|
||||
conclusion_started = False
|
||||
|
||||
def emit_token(t):
|
||||
return f"event: token\ndata: {json.dumps({'text': t}, ensure_ascii=False)}\n\n"
|
||||
|
||||
for line in resp.iter_lines():
|
||||
if not line:
|
||||
continue
|
||||
line = line.decode("utf-8") if isinstance(line, bytes) else line
|
||||
|
||||
now = time.time()
|
||||
if now - last_keepalive > 15:
|
||||
yield ": keepalive\n\n"
|
||||
last_keepalive = now
|
||||
|
||||
if line.startswith("data: "):
|
||||
chunk_data = line[6:]
|
||||
if chunk_data.strip() == "[DONE]":
|
||||
break
|
||||
try:
|
||||
chunk = json.loads(chunk_data)
|
||||
delta = chunk.get("choices", [{}])[0].get("delta", {})
|
||||
token_text = delta.get("content", "")
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if not token_text:
|
||||
continue
|
||||
|
||||
full_text += token_text
|
||||
pending += token_text
|
||||
|
||||
# 循环剥离 pending 里确定安全的部分:
|
||||
# 遇到完整分隔符 -> 发 section_start 事件, 丢弃分隔符;
|
||||
# 遇到分隔符前缀后缀 -> 留在 pending 等待后续 chunk
|
||||
while pending:
|
||||
idx_r = pending.find(DELIM_R)
|
||||
idx_c = pending.find(DELIM_C)
|
||||
# 找最早出现的完整分隔符
|
||||
idx = -1
|
||||
delim = None
|
||||
if idx_r != -1 and (idx_c == -1 or idx_r < idx_c):
|
||||
idx, delim = idx_r, DELIM_R
|
||||
elif idx_c != -1:
|
||||
idx, delim = idx_c, DELIM_C
|
||||
|
||||
if idx != -1:
|
||||
# 分隔符前的正文是安全的, 直接下发
|
||||
if idx > 0:
|
||||
if not reasoning_started:
|
||||
reasoning_started = True
|
||||
yield f"event: reasoning_start\ndata: {{}}\n\n"
|
||||
yield emit_token(pending[:idx])
|
||||
pending = pending[idx + len(delim):]
|
||||
if delim == DELIM_R:
|
||||
if not reasoning_started:
|
||||
reasoning_started = True
|
||||
yield f"event: reasoning_start\ndata: {{}}\n\n"
|
||||
else:
|
||||
if not reasoning_started:
|
||||
# 防御: 模型跳过 REASONING 直接给 CONCLUSION
|
||||
reasoning_started = True
|
||||
yield f"event: reasoning_start\ndata: {{}}\n\n"
|
||||
if not conclusion_started:
|
||||
conclusion_started = True
|
||||
yield f"event: conclusion_start\ndata: {{}}\n\n"
|
||||
continue
|
||||
|
||||
# 无完整分隔符: 检查尾部是否为某分隔符的前缀
|
||||
keep = 0
|
||||
tail = pending[-holdback:] if len(pending) > holdback else pending
|
||||
for d in (DELIM_R, DELIM_C):
|
||||
for k in range(min(len(tail), len(d) - 1), 0, -1):
|
||||
if tail.endswith(d[:k]):
|
||||
keep = max(keep, k)
|
||||
break
|
||||
safe = pending[:len(pending) - keep] if keep else pending
|
||||
pending = pending[len(safe):]
|
||||
if not safe:
|
||||
break
|
||||
if not reasoning_started:
|
||||
reasoning_started = True
|
||||
yield f"event: reasoning_start\ndata: {{}}\n\n"
|
||||
yield emit_token(safe)
|
||||
|
||||
# 流结束: 残余 pending 全部下发
|
||||
if pending:
|
||||
if not reasoning_started:
|
||||
reasoning_started = True
|
||||
yield f"event: reasoning_start\ndata: {{}}\n\n"
|
||||
yield emit_token(pending)
|
||||
pending = ""
|
||||
|
||||
except Exception as e:
|
||||
yield f"event: error\ndata: {json.dumps({'error': f'LLM call failed: {str(e)}'}, ensure_ascii=False)}\n\n"
|
||||
return
|
||||
|
||||
# 防御: 模型没给分隔符时补发 section_start, 保证前端 tab 一定有入口
|
||||
if not reasoning_started:
|
||||
yield f"event: reasoning_start\ndata: {{}}\n\n"
|
||||
if not conclusion_started:
|
||||
yield f"event: conclusion_start\ndata: {{}}\n\n"
|
||||
|
||||
elapsed = round(time.time() - start_time, 1)
|
||||
yield f"event: step\ndata: {json.dumps({'step': 'complete', 'content': f'分析完成, 总耗时 {elapsed} 秒'}, ensure_ascii=False)}\n\n"
|
||||
Reference in New Issue
Block a user