Stopping a `.stream()` input runs the handler once more on the last chunk

使用 gr.Audio (麦克风)或 gr.Image (摄像头)的 .stream() 流式输入时,用户点击 Stop 后助手会再用最后一个 chunk 多执行一次,导致 Stopping a `.stream()` input runs the handler once more on the

快速结论:使用 gr.Audio(麦克风)或 gr.Image(摄像头)的 .stream() 流式输入时,用户点击 Stop 后助手会再用最后一个 chunk 多执行一次,导致 Stopping a `.stream()` input runs the handler once more on the last chunk;优先排查停止流程是否触发了重复的流式回调。

适用环境:Issue 已确认 Gradio 6.30.0(main 分支 de986c723 同样存在),Linux,Chromium(无头模式下用假麦克风复现)。

最快修复方案:暂无确认的一步修复方案。Issue 关闭时未记录已验证的修复提交或配置项。

注意事项:下面“解决步骤”中的处理方式是围绕该行为的规避思路,并非 Issue 验证过的官方修复;升级 Gradio 前请先确认目标版本是否已处理 #13982。

问题场景

在 Gradio Blocks 应用中使用流式输入组件(gr.Audio(sources=["microphone"], type="numpy", streaming=True),摄像头 gr.Image 的流式模式同理),通过 mic.stream(handler, mic, out, stream_every=0.5) 为每个 chunk 调用一次处理函数。录制约 3 秒后点击 Stop,处理函数会额外执行一次,收到的正是上一个已处理过的 chunk。

在复现脚本中,浏览器共发送 5 个 chunk 后发送 close 请求,处理函数却执行了 6 次。第 6 次在 close 请求后约 1 ms 发生,音频数据的 md5 与第 5 次完全相同。页面显示的是第 5 次调用的输出,但运行历史保存的却是第 6 次。

报错原文

Stopping a `.stream()` input runs the handler once more on the last chunk

call 1 t=1792134495.795 md5=4f2072d545
call 2 t=1792134496.299 md5=4efad0ff28
call 3 t=1792134496.808 md5=f5ef03f913
call 4 t=1792134497.329 md5=c551e98624
call 5 t=1792134497.872 md5=d84fd7095c
call 6 t=1792134498.278 md5=d84fd7095c

The page shows "call 5: md5 d84fd7095c".

原因分析

根据 Issue 讨论,问题出在流式输入的停止流程:close 请求到达后,服务端仍针对最后一个 chunk 再触发一次处理函数,而不是只结束流。这不是用户代码重复调用导致的。

评论中补充了另一个现象:当处理函数是 generator,且 Stop 在第二个 chunk 到来之前触发时,这次多余调用会在同一 event id 下继续同一个 generator,其下一次 yield 以 diff 形式(例如 [["append", [], "x"]])被发送。由于客户端对流事件跳过 apply_diff_stream,process_completed 消息把这段原始 diff 当作输出数据,浏览器运行历史于是保存了 diff 而不是完整输出。#13979 在服务端通过保存上一次完整输出来规避该现象,但根本解决方式是停止时不再重跑处理函数。

可能受影响的场景:处理函数含写入数据库、追加 gr.State、模型推理或 GPU / ZeroGPU 耗时操作时,这些副作用会执行两次;实时转写等累积型处理会把最后一个 chunk 加两遍。

环境排查

  • 确认 Gradio 版本是否为 6.30.0 或包含该问题的 main 分支提交;升级前后分别复现一次。
  • 确认触发组件是流式 gr.Audio 麦克风或流式 gr.Image 摄像头。
  • 确认 .stream() 的 stream_every 设置,并与浏览器实际发送的 chunk 数对比。
  • 检查处理函数是否包含副作用(数据库写入、gr.State 追加、推理、GPU 调用)或是 generator。
  • 在浏览器端查看运行历史保存的输出是否与页面显示不一致。

解决步骤

  1. 先在服务端日志中打印每次调用的时间戳和 chunk 哈希,确认 close 请求之后是否还有一次调用,并比对它与上一次 chunk 的哈希是否相同。Issue 中的日志已给出这种确认方式。
  2. 若日志确认最后一次调用的 chunk 与上一次完全相同,说明命中本 Issue,而非业务代码重复订阅。
  3. 可优先尝试:在处理函数中对最后一个 chunk 做幂等判断,例如记录上一次 chunk 的哈希或序号,遇到相同 chunk 直接返回上一次结果,避免副作用重复执行。
  4. 可优先尝试:将数据库写入、gr.State 追加、模型推理等副作用移出流式处理函数,或改为按累积结果去重后落库。
  5. 若使用 generator 形式的处理函数,注意 Stop 前只收到一个 chunk 时可能出现 diff 被当作输出的情况;可优先尝试在客户端或运行历史展示层保存上一次完整输出,#13979 就是按这个思路在服务端处理的。
  6. 关注 gradio-app/gradio 后续版本或 #13982、#13979 的修复进展,升级后重新用精简复现脚本验证。

验证方法

用 Issue 中的复现脚本录制约 3 秒并点击 Stop,对比服务端日志中的调用次数与浏览器发送的 chunk 数:若 close 请求后不再出现与最后一个 chunk 哈希相同的额外调用,即说明问题已消除。同时检查页面显示输出与运行历史保存的输出是否一致。

参考来源

gradio-app/gradio #13982

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 29516

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注