Skip to content

PawzoChat v0.1.7

Choose a tag to compare

@iwyxdxl iwyxdxl released this 18 May 12:05

新增

  • 朋友圈:新增独立的「朋友圈」模块,UI 入口在主导航;用户与角色可以共用一条 feed 互相发动态、点赞和评论。每个角色可配置「是否可发动态」(publishers)、「是否参与评论」(repliers)、评论概率 0–100、以及是否将本次朋友圈互动写入该角色记忆;动态封面图、post / reply 提示词模板可在设置中自定义;底层数据按 moments.json 原子写入到 data/moments/,图片按动态 ID 隔离在 data/moments/images/<mom_id>/
  • 朋友圈生成与回复链路:单 worker 串行调度,所有 LLM 调用(动态生成 / 评论 / 用户被回复后的 counter-reply)共用一把跨 workflow 互斥锁,避免并发触发上游限流;用户在评论区追问被回复角色时,由后台 FIFO 队列处理,不会与正在进行的对话轮次交错;动态作者已支持调用 generate_image 工具自动配图(最多一张),系统会将动态文本和配图作为多模态输入提供给评论角色
  • 朋友圈记忆联动:默认开启「写入记忆」时,角色自己发布的动态、对用户朋友圈的评论会以第一人称摘要写入该角色的记忆;用户之间或角色之间的互动则按产品决策刻意不写入
  • 角色创建 / 导入 / 删除时同步维护朋友圈名单:新角色默认自动加入 publishers 与 repliers,删除角色时清理 publishers / repliers / reply_probabilities / memory_enabled
  • 内置工具 view_reference_image:让对话中的 LLM 自查当前角色的形象参考图(头像或自定义参考图)。通过 pending_images + [图片 ID:xxx] 占位符的统一通道注册,再让 LLM 用已有的 recognize_image 能力适配器读取图片内容——避开 OpenAI / Gemini 工具结果会把图片摊平为占位文本、以及 Anthropic 通道会把参考图作为新图二次推送给用户的问题;当生图功能关闭或角色无可用参考图时,自动从工具列表中过滤
  • generate_image 新增 use_reference_image 参数:纯风景 / 物品 / 食物等不需要出现人物的画面,AI 可在调用时主动传 false 跳过角色形象参考图;用户在角色配置里设为 ref_mode=none 时本参数无效(不会被反向打开)
  • 插件 MCP 访问(ctx.mcp):新增 list_servers() / list_tools() / call_tool(name, arguments),按声明的权限分别需要 mcp.read(列表)和 mcp.invoke(调用);工具调用错误不抛异常,而是以 工具调用出错: ... 文本块返回,与 LLM tool_use 路径同语义
  • 插件发布 in-process LLM 工具(ctx.mcp.register_tool):插件可注册命名空间为 plugin_<id>__<name> 的工具,与 MCP-server 工具和内置能力并列出现在 LLM 工具列表与 MCP 概览页 / 插件详情页;生命周期跟随插件,disable / reload / 加载失败时自动反注册。新增权限 mcp.publish
  • 插件文件消息支持:ctx.messaging.send_message 新增 files 参数([{"path": "...", "name": "...", "mime": "..."}]),可推送非图片文件(doc / pdf / zip 等)。微信通道会先复制文件到 data/chats/<persona>/files/<随机前缀>__<原名> 以保证 Web 预览可用,再走新的 CDN 上传链路(transport/cdn.upload_filetransport/sender.send_fileILinkClient.build_file_message,沿用图片相同的 AES-ECB 加密 + 重试上限 3 次)
  • 插件 LLM 复用角色绑定:ctx.llm 新增 chat_as_persona(persona_id, messages, ...) 复用角色绑定的 provider / model / temperature / max_tokens,每次调用重读 persona 配置;新增 list_providers() 列出已注册 provider 名、get_persona_binding(persona_id) 返回角色当前绑定快照
  • LLMResponse 新增 reasoning_content 字段;OpenAI 兼容渠道在 tool_use 多轮循环中回传 reasoning_content 给上游,避免 DeepSeek-v4 / o1 风格代理在 thinking-mode 模型上拒绝继续请求
  • /api/mcp/tools 现在为每个工具返回 owner 标签(builtin / plugin:<id> / mcp:<server> / "" 表示来自 capability_adapters 配置项),前端据此对来自插件 / 内置的工具隐藏编辑与删除入口

修复

  • 修复 MCP stdio 启动失败:原路径启发式把任何含 / 的字符串当作本地路径并重写到 APP_HOME,导致 npm scoped 包名 (@scope/name)、--key=value 标志、https:// / file:// URL 被破坏,npx 启动的官方 MCP 服务器(如 @playwright/mcp)以 ENOENT 失败、被 SDK 包装为 Connection closed。收紧规则:仅显式相对路径前缀 (./ / ../)、绝对路径或已知路径后缀(.py / .exe / .sh 等)会被解析为路径
  • 修复 MCP 连接生命周期:原先用手工 __aenter__ / __aexit__ 栈,anyio 任务组的 enter 与 exit 跨 asyncio task 执行(每次 run_coroutine_threadsafe 都是新 task),SDK 仅以 DEBUG 日志吞掉失败、实际清理交给 asyncgen GC。改为一个长存的 _lifecycle_task 在同一 task 内持有 transport + ClientSession 上下文,断开时通过 _shutdown_event 在限时窗口内优雅退出,必要时再 cancel
  • 修复 MCP 工具结果中的图片丢失:MCP 服务器可能以三种形式返回图片(ImageContent 块 / EmbeddedResource / TextContent 内嵌 data:image/...;base64,... URI 或 markdown 包装的同 URI),新增 services/mcp_image_extractor.py 统一识别并落盘到 data/chats/<persona_id>/images/mcp_*.<ext>,再以独立 assistant 消息分发;带格式嗅探(PNG / JPEG / GIF / WebP / BMP)、单图 ≤20MB、单次工具调用最多 8 张、目录 resolve 后强校验不能逃出 chats/generate_image 等会自行注册出站图片的工具不再被二次抽取
  • 修复 OpenAI 兼容渠道偶发 SSE 误返:部分中继(个别 grok 代理等)在 stream=False 时仍返回 text/event-stream,原先直接报格式错误。新增 _looks_like_sse_stream + _parse_sse_completion,按行重组 data: 切片为单个 LLMResponse(含文本、tool_calls、reasoning_content,并尊重 finish_reason
  • 修复朋友圈刷新按钮在 feed 生成中无可视反馈:刷新图标在 is_generating=true 时持续转圈,状态来源于 /api/moments/state

内部

  • 新增模块:pawzochat/services/moments.pypawzochat/store/moments.pypawzochat/web/routes/api_moments.pypawzochat/web/static/modules/moments.jspawzochat/utils/profile.pypawzochat/services/mcp_image_extractor.pypawzochat/mcp/builtin/view_reference_image.py
  • 新增路径常量:MOMENTS_DIR / MOMENTS_IMAGES_DIR / MOMENTS_STORE_PATH
  • ConfigManager 默认结构新增 moments 节(publishers / repliers / reply_probabilities / memory_enabled / prompts.post / prompts.reply
  • 新增路由:GET/POST /api/momentsGET/DELETE /api/moments/<id>POST/DELETE /api/moments/<id>/likePOST /api/moments/<id>/repliesDELETE /api/moments/<id>/replies/<rid>POST /api/moments/refreshGET /api/moments/stateGET/PUT /api/moments/settingsGET/POST/DELETE /api/moments/coverGET /api/moments/images/<id>/<filename>
  • CapabilityAdapterRegistry 由「内置 vs 配置」二分改为 owner 标签三态("" 配置项可重载、builtin 程序内置保留、plugin:<id> 插件保留),register(adapter, owner=...)unregister_owner(prefix) 配套;reload(adapters_cfg) 只清理空 owner 的条目
  • MemoryService._load_profile_name 抽离为 pawzochat/utils/profile.py.load_profile_name() 单一来源
  • 插件 API 扩展:PluginContext 新增 mcp 字段;MCPFacade 新增 register_tool(name, description, parameters, handler, *, namespace=...)ExtensionManager 通过 set_capability_registry()start() 之前注入注册表,并在插件 disable / reload / 加载失败时调用 _unregister_plugin_tools() 清理;KNOWN_PERMISSIONS 新增 mcp.publish
  • MessagingFacade._normalize_files / _persist_files_for_persona 负责文件块校验、复制和 text/image/file 三类内容块的合并构建
  • services/chat.py 工具调用循环新增 MCP 图片回收阶段;_build_tools 顺序调整为先按 policy 过滤、再按生图开关过滤、最后按参考图可用性过滤 view_reference_image_build_image_tool_guidance 接受 active_tool_names 仅注入实际暴露给本轮的工具的提示词
  • 移除 MCP list_resources / list_prompts 未使用桩函数与 manager 内冗余的聚合任务包装

完整变更日志见 CHANGELOG.md