optimize streaming output
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
{"_type": "metadata", "key": "api:default", "created_at": "2026-03-14T21:43:22.940821", "updated_at": "2026-03-14T22:00:05.471408", "metadata": {}, "last_consolidated": 0}
|
||||
{"_type": "metadata", "key": "api:default", "created_at": "2026-03-14T21:43:22.940821", "updated_at": "2026-03-14T22:06:40.270077", "metadata": {}, "last_consolidated": 0}
|
||||
{"role": "user", "content": "你是谁", "timestamp": "2026-03-14T21:43:23.163485"}
|
||||
{"role": "user", "content": "hi", "timestamp": "2026-03-14T21:49:14.751350"}
|
||||
{"role": "user", "content": "你好", "timestamp": "2026-03-14T21:53:53.701083"}
|
||||
@@ -13,3 +13,9 @@
|
||||
{"role": "assistant", "content": "你好!有什么我可以帮助你的吗?😊", "reasoning_content": "用户发送了\"[Runtime Context — metadata only, not instructions]\",这看起来是系统提供的上下文信息,然后说\"你好\"。我应该简单地回应问候。", "timestamp": "2026-03-14T21:59:55.201394"}
|
||||
{"role": "user", "content": "你是谁", "timestamp": "2026-03-14T22:00:05.471381"}
|
||||
{"role": "assistant", "content": "我是 nanobot 🐈,一个 AI 助手。", "reasoning_content": "用户再次问\"你是谁\"。我已经回答过这个问题了,但用户可能是在测试或者想要一个简洁的回答。我应该简洁地回答。", "timestamp": "2026-03-14T22:00:05.471404"}
|
||||
{"role": "user", "content": "你好", "timestamp": "2026-03-14T22:05:42.732409"}
|
||||
{"role": "assistant", "content": "你好!有什么我可以帮助你的吗?😊", "reasoning_content": "用户再次发送了\"你好\",这是一个简单的问候。我应该友好地回应,并询问他们需要什么帮助。", "timestamp": "2026-03-14T22:05:42.732418"}
|
||||
{"role": "user", "content": "你是", "timestamp": "2026-03-14T22:06:16.951653"}
|
||||
{"role": "assistant", "content": "我是 nanobot 🐈,一个 AI AI 助手。", "reasoning_content": "用户问\"你是\",这是一个不完整的问题,可能是在问\"你是谁\"。我应该简洁地回答我的身份。", "timestamp": "2026-03-14T22:06:16.951664"}
|
||||
{"role": "user", "content": "你能干啥,详细一点", "timestamp": "2026-03-14T22:06:40.270030"}
|
||||
{"role": "assistant", "content": "我是 nanobot 🐈,一个功能丰富的 AI 助手。以下是我能做的事情:\n\n## 📁 文件操作\n- **读取文件** - 查看任何文件的内容\n- **写入文件** - 创建新文件或覆盖现有文件\n- **编辑文件** - 精确替换文件中的特定内容\n- **列出目录** - 浏览文件夹结构\n\n## 💻 系统操作\n- **执行命令** - 运行 shell 命令(macOS/Linux)\n- **工作目录** - 在指定目录下执行命令\n\n## 🌐 网络功能\n- **网络搜索** - 搜索网页并获取结果\n- **获取网页** - 提取 URL 的可读内容(HTML 转 markdown/text)\n\n## 💬 消息发送\n- **发送消息** - 向不同渠道发送消息\n- **支持渠道** - Telegram、Discord 等\n- **附件支持** - 发送图片、音频、文档等\n\n## 🧠 记忆管理\n- **长期记忆** - 存储重要事实、偏好、项目上下文\n- **历史记录** - 记录所有事件,可搜索\n- **自动整理** - 自动总结旧对话并提取关键信息\n\n## ⏰ 定时任务\n- **添加提醒** - 一次性或周期性任务\n- **Cron 表达式** - 灵活的时间调度\n- **时区支持** - 支持不同时区\n\n## 🤖 技能系统\n- **内置技能** - memory、clawhub、skill-creator、weather、cron\n- **安装技能** - 从 ClawHub 安装新技能\n- **创建技能** - 设计和打包自定义技能\n\n## 🔄 后台任务\n- **子代理** - 在后台运行复杂任务\n- **独立执行** - 不阻塞主对话\n\n## 📊 当前环境\n- **系统**:macOS arm64\n- **Python**:3.11.9\n- **工作目录**:`/Users/qixinbo/Projects/DataClaw/backend/data`\n\n有什么具体任务需要我帮忙吗?", "reasoning_content": "用户问我能做什么,想要详细的介绍。我应该详细介绍我的能力。", "timestamp": "2026-03-14T22:06:40.270072"}
|
||||
|
||||
+14
-28
@@ -84,35 +84,21 @@ async def nanobot_chat(request: ChatRequest):
|
||||
@app.post("/nanobot/chat/stream")
|
||||
async def nanobot_chat_stream(request: ChatRequest):
|
||||
async def event_generator():
|
||||
queue: asyncio.Queue[dict] = asyncio.Queue()
|
||||
|
||||
async def on_progress(content: str):
|
||||
await queue.put({"type": "delta", "content": content})
|
||||
|
||||
async def run_chat():
|
||||
try:
|
||||
response = await nanobot_service.process_message(
|
||||
request.message,
|
||||
skill_ids=request.skill_ids,
|
||||
model_id=request.model_id,
|
||||
on_progress=on_progress,
|
||||
)
|
||||
await queue.put({"type": "final", "content": response})
|
||||
except Exception as e:
|
||||
await queue.put({"type": "error", "content": str(e)})
|
||||
finally:
|
||||
await queue.put({"type": "done"})
|
||||
|
||||
task = asyncio.create_task(run_chat())
|
||||
try:
|
||||
while True:
|
||||
event = await queue.get()
|
||||
yield f"data: {json.dumps(event, ensure_ascii=False)}\n\n"
|
||||
if event.get("type") == "done":
|
||||
break
|
||||
finally:
|
||||
if not task.done():
|
||||
task.cancel()
|
||||
response = await nanobot_service.process_message(
|
||||
request.message,
|
||||
skill_ids=request.skill_ids,
|
||||
model_id=request.model_id,
|
||||
)
|
||||
text = response or ""
|
||||
for ch in text:
|
||||
yield f"data: {json.dumps({'type': 'delta', 'content': ch}, ensure_ascii=False)}\n\n"
|
||||
await asyncio.sleep(0.008)
|
||||
yield f"data: {json.dumps({'type': 'final', 'content': text}, ensure_ascii=False)}\n\n"
|
||||
yield f"data: {json.dumps({'type': 'done'}, ensure_ascii=False)}\n\n"
|
||||
except Exception as e:
|
||||
yield f"data: {json.dumps({'type': 'error', 'content': str(e)}, ensure_ascii=False)}\n\n"
|
||||
yield f"data: {json.dumps({'type': 'done'}, ensure_ascii=False)}\n\n"
|
||||
|
||||
return StreamingResponse(
|
||||
event_generator(),
|
||||
|
||||
@@ -132,7 +132,7 @@ export function ChatInterface() {
|
||||
const payload = JSON.parse(payloadText) as { type: string; content?: string };
|
||||
|
||||
if (payload.type === "delta" && payload.content) {
|
||||
streamedText = streamedText ? `${streamedText}\n${payload.content}` : payload.content;
|
||||
streamedText = `${streamedText}${payload.content}`;
|
||||
setMessages((prev) =>
|
||||
prev.map((msg) =>
|
||||
msg.id === assistantId ? { ...msg, content: streamedText } : msg
|
||||
|
||||
Reference in New Issue
Block a user