Skip to content

fix: 给kook适配器实现send_streaming方法(降级到普通文本消息发送)来修复启用了流逝输出后可能llm消息发不出去的bug#7749

Open
shuiping233 wants to merge 1 commit intoAstrBotDevs:masterfrom
shuiping233:fix/kook_send_streaming_issue
Open

fix: 给kook适配器实现send_streaming方法(降级到普通文本消息发送)来修复启用了流逝输出后可能llm消息发不出去的bug#7749
shuiping233 wants to merge 1 commit intoAstrBotDevs:masterfrom
shuiping233:fix/kook_send_streaming_issue

Conversation

@shuiping233
Copy link
Copy Markdown
Contributor

@shuiping233 shuiping233 commented Apr 23, 2026

Modifications / 改动点

如题,我看到别的适配器里也有这么做的,那我就抄来了)

相关issue:

image image
  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果


Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

@auto-assign auto-assign Bot requested review from Fridemn and Soulter April 23, 2026 14:14
@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. labels Apr 23, 2026
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In send_streaming, you consume the generator and then call super().send_streaming(generator, use_fallback) after it’s already exhausted; if the intention is to fully fall back to a single non-streaming send, you should likely return after await self.send(buffer) and skip the super call, or rebuild a new generator for the parent call.
  • The use_fallback parameter in send_streaming is currently unused; either wire it into the logic (e.g., conditionally delegating to super().send_streaming) or remove it to avoid confusion.
  • The current approach concatenates the entire streaming content into memory (buffer.chain.extend(...)), which may be problematic for very long streams; consider either chunked sends or a size guard to prevent unbounded accumulation.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `send_streaming`, you consume the `generator` and then call `super().send_streaming(generator, use_fallback)` after it’s already exhausted; if the intention is to fully fall back to a single non-streaming send, you should likely return after `await self.send(buffer)` and skip the super call, or rebuild a new generator for the parent call.
- The `use_fallback` parameter in `send_streaming` is currently unused; either wire it into the logic (e.g., conditionally delegating to `super().send_streaming`) or remove it to avoid confusion.
- The current approach concatenates the entire streaming content into memory (`buffer.chain.extend(...)`), which may be problematic for very long streams; consider either chunked sends or a size guard to prevent unbounded accumulation.

## Individual Comments

### Comment 1
<location path="astrbot/core/platform/sources/kook/kook_event.py" line_range="225-226" />
<code_context>
+        if not buffer:
+            return None
+        buffer.squash_plain()
+        await self.send(buffer)
+        return await super().send_streaming(generator, use_fallback)
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid double-processing and using an already-consumed generator in `send_streaming`.

Here the generator is fully consumed into `buffer`, sent once via `self.send(buffer)`, and then passed to `super().send_streaming(generator, use_fallback)` after it is already exhausted. This makes the superclass call effectively a no-op or fragile if it expects a fresh stream, and risks duplicate or inconsistent output if it also sends content.

Given the docstring that KOOK does not support streaming and this method should fall back to a normal `send`, consider either:
- Only aggregating and calling `await self.send(buffer)` (remove the `super().send_streaming` call), or
- Skipping manual aggregation and just `return await super().send_streaming(generator, use_fallback)` if the base class already provides the fallback.

This avoids using an exhausted generator and prevents double-sending.
</issue_to_address>

### Comment 2
<location path="astrbot/core/platform/sources/kook/kook_event.py" line_range="218-219" />
<code_context>
+        """KOOK 平台不支持流式输出, 调用此方法将回退到普通`send`方法"""
+        buffer = None
+        async for chain in generator:
+            if not buffer:
+                buffer = chain
+            else:
+                buffer.chain.extend(chain.chain)
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Use explicit `is None` check for `buffer` to avoid relying on `MessageChain` truthiness.

Since `buffer` starts as `None`, using `if not buffer:` will also trigger if `MessageChain` is a valid but falsy value (e.g., empty if it implements `__bool__`/`__len__`). That changes behavior between `None` and an empty chain. Use `if buffer is None:` to distinguish the initial state from an empty `MessageChain` and make the intent explicit.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +225 to +226
await self.send(buffer)
return await super().send_streaming(generator, use_fallback)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Avoid double-processing and using an already-consumed generator in send_streaming.

Here the generator is fully consumed into buffer, sent once via self.send(buffer), and then passed to super().send_streaming(generator, use_fallback) after it is already exhausted. This makes the superclass call effectively a no-op or fragile if it expects a fresh stream, and risks duplicate or inconsistent output if it also sends content.

Given the docstring that KOOK does not support streaming and this method should fall back to a normal send, consider either:

  • Only aggregating and calling await self.send(buffer) (remove the super().send_streaming call), or
  • Skipping manual aggregation and just return await super().send_streaming(generator, use_fallback) if the base class already provides the fallback.

This avoids using an exhausted generator and prevents double-sending.

Comment on lines +218 to +219
if not buffer:
buffer = chain
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): Use explicit is None check for buffer to avoid relying on MessageChain truthiness.

Since buffer starts as None, using if not buffer: will also trigger if MessageChain is a valid but falsy value (e.g., empty if it implements __bool__/__len__). That changes behavior between None and an empty chain. Use if buffer is None: to distinguish the initial state from an empty MessageChain and make the intent explicit.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements the send_streaming method for the KOOK platform. Since KOOK does not natively support streaming, the implementation aggregates chunks from an asynchronous generator into a single message buffer before sending. A review comment correctly identified a logic error where the generator is exhausted during aggregation, making the subsequent call to the superclass method with the same generator ineffective.

Comment on lines +225 to +226
await self.send(buffer)
return await super().send_streaming(generator, use_fallback)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

在此处调用 super().send_streaming(generator, use_fallback) 是不正确的,因为 generator 已经在前面的 async for 循环中被完全耗尽(exhausted)。父类方法将接收到一个空的生成器,这可能导致预期的逻辑(如指标统计或默认处理)失效。由于你已经通过 await self.send(buffer) 发送了聚合后的消息,且 self.send 内部已经调用了 super().send() 来处理通用的发送逻辑(如指标上报),因此这里不需要再调用父类的流式发送方法。

Suggested change
await self.send(buffer)
return await super().send_streaming(generator, use_fallback)
await self.send(buffer)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant