Skip to content

Harden WeCom pull streaming and add compatible Dify snapshot refresh configuration#2053

Closed
bjhx2003 wants to merge 43 commits intolangbot-app:masterfrom
bjhx2003:feat/wecom
Closed

Harden WeCom pull streaming and add compatible Dify snapshot refresh configuration#2053
bjhx2003 wants to merge 43 commits intolangbot-app:masterfrom
bjhx2003:feat/wecom

Conversation

@bjhx2003
Copy link
Copy Markdown

@bjhx2003 bjhx2003 commented Mar 11, 2026

概述 / Overview

本次 PR 主要增强了企业微信智能机器人 Pull 模式下的流式消息处理能力,并补齐 Dify 流式快照刷新配置,同时保持默认行为尽量兼容 master。

This PR mainly improves pull-mode streaming for the WeCom AI bot, adds Dify snapshot refresh configuration, and keeps default behavior as compatible with master as possible.

主要改动 / Key changes

  1. 增强企微 Pull 流式生命周期处理

    • 为 WeCom pull 模式增加轮询等待时间、最大生命周期、首字等待占位文案等配置
    • 在 follow-up 轮询超时、异常、无新 chunk 等场景下,使用最后快照或错误提示强制 finish,避免企微持续轮询后展示官方兜底文案
    • 队列消费时仅保留最新快照,避免旧片段堆积导致显示滞后

    Harden WeCom pull streaming lifecycle

    • add polling timeout, max stream lifetime, and pending placeholder related settings
    • force-finish stale or failed streams with the latest snapshot or an error fallback
    • keep only the latest snapshot in the queue to avoid stale chunk buildup
  2. 改进企微回复与流水线流式处理

    • respback 在流式模式下跳过强制延迟,提升首字响应速度
    • 流式回复时使用最新 chunk 的 is_final 标记,避免 finish 状态错误

    Improve streaming response handling in pipeline stages

    • skip forced delay in streaming mode to reduce first-token latency
    • use the latest chunk final flag when replying stream snapshots
  3. 优化企微消息转换

    • WecomBotMessageConverter 在转文本时保留引用内容
    • 非纯文本组件尽可能转换为可读文本,避免回复内容丢失

    Improve WeCom message conversion

    • preserve quote content when converting messages to text
    • convert non-plain components to readable text where possible
  4. 补齐 Dify 流式快照刷新策略

    • 新增 pipeline output 配置:
      • output.dify-stream.chunk-batch-size
      • output.dify-stream.flush-window-enabled
      • output.dify-stream.flush-window-ms
    • 处理 workflow_finished 场景,保证流式输出能正确 finish
    • 恢复 Dify workflow answer 提取兼容逻辑,避免回归

    Add Dify snapshot refresh strategy

    • add pipeline output config:
      • output.dify-stream.chunk-batch-size
      • output.dify-stream.flush-window-enabled
      • output.dify-stream.flush-window-ms
    • handle workflow_finished correctly to ensure stream completion
    • restore Dify workflow answer extraction compatibility logic
  5. 配置迁移与兼容默认值

    • 将 Dify 专属的流式快照配置迁移到 pipeline output 配置
    • 前端仅在选择 dify-service-api 作为 runner 时显示 dify-stream 配置项
    • 默认值尽量兼容 master:
      • WeCom poll timeout 默认 500ms
      • placeholder 默认关闭
      • Dify chunk batch size 默认 8
      • flush window 默认关闭

    Config migration and compatibility defaults

    • move Dify-specific stream snapshot settings to pipeline output config
    • show the dify-stream section only when the selected runner is dify-service-api
    • keep defaults compatible with master where possible:
      • WeCom poll timeout defaults to 500ms
      • placeholder disabled by default
      • Dify chunk batch size defaults to 8
      • flush window disabled by default
  6. Docker 构建优化

    • 优化 .dockerignore
    • 调整 Dockerfile 复制顺序与依赖安装顺序,尽量利用 Docker 层缓存
    • 启动命令保持与 master 一致

    Docker build optimization

    • optimize .dockerignore
    • improve Dockerfile layer usage for better build caching
    • keep the startup command aligned with master
  7. 测试

    • 补充并更新定向测试,覆盖:
      • final 收口
      • flush window 行为
      • placeholder fallback
      • latest snapshot
      • 配置来源迁移
      • 默认值兼容行为

    Tests

    • add/update targeted tests covering:
      • final stream completion
      • flush window behavior
      • pending placeholder fallback
      • latest snapshot consumption
      • config source migration
      • compatibility defaults behavior

更改前后对比截图 / Screenshots

请在此部分粘贴更改前后对比截图(可以是界面截图、控制台输出、对话截图等):
Please paste the screenshots of changes before and after here (can be interface screenshots, console output, conversation screenshots, etc.):

修改前 / Before:

修改后 / After:
机器人配置,当选择企微智能机器人时增加如下配置,首字等待占位功能默认关闭,开启后可在n秒未收到首字时先回复消息占位,后续真实回复时会替换掉消息。

image

流水线配置,当选择dify服务api时,在输出处理tab,可以调整chunk大小,和开启时间窗口结合策略,默认关闭兼容

image

检查清单 / Checklist

已通过以下测试:

uv run pytest -q tests/unit_tests/pipeline/test_wecombot_dify_minfix.py -q

结果:

- 13 项测试通过

Validated with:

uv run pytest -q tests/unit_tests/pipeline/test_wecombot_dify_minfix.py -q

Result:

- 13 tests passed

### 更改前后对比截图 / Screenshots

> 请在此部分粘贴更改前后对比截图(可以是界面截图、控制台输出、对话截图等):
> Please paste the screenshots of changes before and after here (can be interface screenshots, console output, conversation screenshots, etc.):
>
> 修改前 / Before:
>
> - 企业微信 Pull 模式下,流式消息在部分场景中不能正确 finish,可能持续 follow-up 并最终展示官方兜底文案
> - Dify 流式快照刷新策略参数缺少清晰归属,且默认行为与旧逻辑不完全兼容
> - 流式模式下仍会走强制延迟,首字体验较差
>
> 修改后 / After:
>
> - 企业微信 Pull 模式可在超时、异常、无新 chunk 等场景下正确收口
> - Dify 流式快照刷新参数迁移到 `output.dify-stream`,并通过默认值保持旧行为兼容
> - 流式模式跳过强制延迟,首字响应更快
> - 队列仅保留最新快照,减少旧片段堆积导致的显示滞后
>
> (如需要,我会在提交前补充控制台日志截图 / 对话截图)
> (Screenshots can be added before submission if needed.)


### PR 作者完成 / For PR author

*请在方括号间写`x`以打勾 / Please tick the box with `x`*

- [ ] 阅读仓库[贡献指引](https://github.com/langbot-app/LangBot/blob/master/CONTRIBUTING.md)了吗? / Have you read the [contribution guide](https://github.com/langbot-app/LangBot/blob/master/CONTRIBUTING.md)?
- [ ] 与项目所有者沟通过了吗? / Have you communicated with the project maintainer?
- [x] 我确定已自行测试所作的更改,确保功能符合预期。 / I have tested the changes and ensured they work as expected.

### 项目维护者完成 / For project maintainer

- [ ] 相关 issues 链接了吗? / Have you linked the related issues?
- [x] 配置项写好了吗?迁移写好了吗?生效了吗? / Have you written the configuration items? Have you written the migration? Has it taken effect?
- [ ] 依赖加到 pyproject.toml 和 core/bootutils/deps.py 了吗 / Have you added the dependencies to pyproject.toml and core/bootutils/deps.py?
- [ ] 文档编写了吗? / Have you written the documentation?

蒋金龙(夜雨) and others added 19 commits March 10, 2026 10:16
- Reduce stream_poll_timeout from 0.15s to 0.05s for faster response
- Change yield condition from % 8 to % 2 for higher push frequency
- Skip plugin events for intermediate streaming chunks to reduce latency
- Fix yield condition to ensure is_final is always sent
- Add msg_id_map cleanup to prevent memory bloat
- Update version to 4.9.0-wecom.1

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move dependency installation before source code copy to leverage
Docker layer caching. Code changes no longer trigger dependency
reinstallation, reducing build time from 2-3 minutes to 10-20 seconds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use `uv sync --no-dev` to exclude dev dependencies
- Remove gcc after build with apt-get purge
- Clean apt cache with rm -rf /var/lib/apt/lists/*
- Expand .dockerignore to exclude docs, tests, and other non-runtime files

Image size reduced from 2.07GB to 1.67GB (19% reduction).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 对齐版本号到 4.9.0.post2,并同步 pyproject、包版本与 uv.lock\n- 将企微通道层配置保留在机器人适配器中,新增占位文案延迟配置\n- 将 chunk 批大小与时间窗口下沉到流水线输出配置,补齐默认值与 UI 元数据\n- 将 Dify 流式输出改为 chunk 阈值或时间窗口双触发,并保证 final 立即收口\n- 优化 pull 模式占位文案逻辑,仅在首字超时后返回占位文案\n- 补充企微与 Dify 定向单元测试,覆盖双阈值 flush、占位延迟与最终收口场景
- Add workflow_finished event handling to set is_final=True
- Add yielded_final flag to prevent duplicate final chunk yield
- Fix WeCom continuous polling issue when Dify workflow ends

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add PullPendingPlaceholderEnabled config option (default: true)
- Show delay and content fields only when enabled via visibleOn
- Pass effective values to WecomBotClient based on switch state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add _summarize_stream_text helper for content logging
- Add publish/consume action logs with seq, content stats
- Track cleared queue items and publish sequence
- Help troubleshoot WeCom pull mode polling issues

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add FULL_TEXT and HYBRID search types
- Implement RRF (Reciprocal Rank Fusion) for hybrid search
- Change add to upsert for idempotent document insertion
- Upgrade chromadb dependency to >=1.0.0,<2.0.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace hide-exception and block-failed-request-output with exception-handling
- Add three strategies: hide, show-hint, show-error
- Add failure-hint field for customizable error message
- Add database migration dbm021 for config conversion
- Remove wecom-stream stage (moved to adapter config)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tibility

- Move filters out of retrieval_settings to avoid empty results
- Pass sender_id and session_name in retrieval settings
- Some plugins (e.g. LangRAG) pass filters directly to vector_search

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update tests to use adapter config instead of pipeline wecom-stream
- Add test for workflow_finished event handling
- Add test for ignoring empty message chunks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dosubot dosubot Bot added size:XXL This PR changes 1000+ lines, ignoring generated files. eh: Improve enhance: 现有功能的改进 / improve current features IM: wecom 企业微信 适配器相关 / WeCom and WeComCS adapter related javascript Pull requests that update Javascript code m: Platform 机器人管理相关 / Bots management labels Mar 11, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 11, 2026

@RockChinQ
Copy link
Copy Markdown
Member

感谢 PR!可否提供一下修改前后的截图,例如 配置页面截图、运行效果截图?我们将更快开始 review

@bjhx2003
Copy link
Copy Markdown
Author

bjhx2003 commented Mar 12, 2026

感谢 PR!可否提供一下修改前后的截图,例如 配置页面截图、运行效果截图?我们将更快开始 review
@RockChinQ 已经提交了修改后的截图

inputs.update(query.variables)
messsage_idx = 0
is_final = False
stream_completed = False
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个参数每一处都是和is_final对应的,为什么不直接用is_final呢

if rendered_text:
content_parts.append(rendered_text)

return ''.join(content_parts)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里直接把引用消息和消息拼接在一起有点不妥吧?


# 如果未开启首字等待占位,则将延迟设为0且占位文案设为空
effective_placeholder_delay = pending_placeholder_delay_ms / 1000 if pending_placeholder_enabled else 0
effective_placeholder = pending_placeholder if pending_placeholder_enabled else ''
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里首字占位,其实我觉着放在creat_message_card这里(主要当时我写钉钉和飞书的时候以为只有卡片流式来着所以函数名就这样了)这样创建首字在进入模型前就能开始,个人感觉比较合理。

is_stream_mode = await query.adapter.is_stream_output_supported() and has_chunks

random_delay = random.uniform(*random_range)
# 流式模式下跳过强制延迟,确保首字快速响应
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

就像wecom那边我说的,如果是在creat_message_card中创建首字消息,这里就不用这么处理了


def __init__(self, config: dict, logger: EventLogger):
enable_webhook = config.get('enable-webhook', True)
if not enable_webhook:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这边方便的话把websocket给一起合入就更好了

@fdc310
Copy link
Copy Markdown
Member

fdc310 commented Mar 27, 2026

你好,请问改pr中dify相关的修改的能不能单独提一个pr

@bjhx2003
Copy link
Copy Markdown
Author

bjhx2003 commented Mar 30, 2026

你好,请问改pr中dify相关的修改的能不能单独提一个pr

因为好久没有review,我以为你们不搞了,我这边又做了很多处理,所以就合到了同一个分支处理了 @fdc310

@fdc310
Copy link
Copy Markdown
Member

fdc310 commented Mar 30, 2026

你好,请问改pr中dify相关的修改的能不能单独提一个pr

因为好久没有review,我以为你们不搞了,我这边又做了很多处理,所以就合到了同一个分支处理了 @fdc310

我的锅,我其实很早就review了并留言,但是忘了推出去 (*꒦ິ⌓꒦ີ),直到前两天

@bjhx2003
Copy link
Copy Markdown
Author

bjhx2003 commented Mar 30, 2026

你好,请问改pr中dify相关的修改的能不能单独提一个pr

因为好久没有review,我以为你们不搞了,我这边又做了很多处理,所以就合到了同一个分支处理了 @fdc310

我的锅,我其实很早就review了并留言,但是忘了推出去 (*꒦ິ⌓꒦ີ),直到前两天

@fdc310 我尝试看看能不能拆开吧,因为后面我对微信客服接入和 企微应用接管微信客服处理方面都做了改造,还引入了redis,整体是一个比较大的动作。

@fdc310
Copy link
Copy Markdown
Member

fdc310 commented Mar 30, 2026

你好,请问改pr中dify相关的修改的能不能单独提一个pr

因为好久没有review,我以为你们不搞了,我这边又做了很多处理,所以就合到了同一个分支处理了 @fdc310

我的锅,我其实很早就review了并留言,但是忘了推出去 (*꒦ິ⌓꒦ີ),直到前两天

@fdc310 我尝试看看能不能拆开吧,因为后面我对微信客服接入和 企微应用接管微信客服处理方面都做了改造,还引入了redis,整体是一个比较大的动作。

好的,感谢。如果可以的话尽量每一块开一个分支,包括您这边提到的位置客服,企微应用和redis相关的都单独pr,然后我们这边的话暂时还没有使用redis的规划,可能暂时不会引入

@bjhx2003
Copy link
Copy Markdown
Author

你好,请问改pr中dify相关的修改的能不能单独提一个pr

因为好久没有review,我以为你们不搞了,我这边又做了很多处理,所以就合到了同一个分支处理了 @fdc310

我的锅,我其实很早就review了并留言,但是忘了推出去 (*꒦ິ⌓꒦ີ),直到前两天

@fdc310 我尝试看看能不能拆开吧,因为后面我对微信客服接入和 企微应用接管微信客服处理方面都做了改造,还引入了redis,整体是一个比较大的动作。

好的,感谢。如果可以的话尽量每一块开一个分支,包括您这边提到的位置客服,企微应用和redis相关的都单独pr,然后我们这边的话暂时还没有使用redis的规划,可能暂时不会引入

@fdc310 了解,不过目前都是纯内存的,对于企业级应用来说没有redis + 单点还是不够稳定

@RockChinQ
Copy link
Copy Markdown
Member

RockChinQ commented Mar 30, 2026 via email

@RockChinQ
Copy link
Copy Markdown
Member

PR 内容不太适合广泛利用,如果需要合入,请拆分为独立功能再提pr~感谢

@RockChinQ RockChinQ closed this Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

eh: Improve enhance: 现有功能的改进 / improve current features IM: wecom 企业微信 适配器相关 / WeCom and WeComCS adapter related javascript Pull requests that update Javascript code m: Platform 机器人管理相关 / Bots management size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants