Skip to content

fix(core): 将空响应视为失败并交给模型重试#809

Open
CookSleep wants to merge 2 commits intoChatLunaLab:v1-devfrom
CookSleep:fix/empty-response-retry
Open

fix(core): 将空响应视为失败并交给模型重试#809
CookSleep wants to merge 2 commits intoChatLunaLab:v1-devfrom
CookSleep:fix/empty-response-retry

Conversation

@CookSleep
Copy link
Copy Markdown
Member

变更说明

  • 将非流式空响应视为 API_REQUEST_FAILED,交给模型层现有 maxRetries 处理。
  • 流式响应改为检查是否收到有效文本或 tool call,避免空流被当作成功返回。
  • 保留纯 tool call 响应的成功路径,不把这类响应误判为空响应失败。

验证

  • yarn fast-build core

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

Warning

Rate limit exceeded

@CookSleep has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 26 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 5 minutes and 26 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a87cf666-7c8c-49d9-b576-19ba98e64826

📥 Commits

Reviewing files that changed from the base of the PR and between d805a89 and 694e00f.

📒 Files selected for processing (1)
  • packages/core/src/llm-core/platform/model.ts

总体概览

该变更改进了流式响应的跟踪和验证逻辑,通过引入 hasResponse 标志来区分是否接收到有意义的内容,并相应调整了验证、重试决策和工具调用检测的实现。

变更内容

变更内容 / 文件 摘要
流式处理验证增强
packages/core/src/llm-core/platform/model.ts
改进了流响应跟踪,新增 hasResponse 标志以区分是否收到有意义的响应内容(工具调用或非空消息内容);调整了验证逻辑仅在存在块但无响应内容时抛出错误;修改重试决策使用 hasResponse 替代 hasChunk;扩展了 _hasToolCallChunk 方法签名支持更广泛的消息类型;为非流式响应添加了额外验证确保返回消息不为空或存在工具调用。

预估代码审查工作量

🎯 3 (Moderate) | ⏱️ ~20 minutes

可能相关的 PR

诗歌

🐰 流的跟踪更精妙,响应区分清楚了,
验证的逻辑更牢,错误的重试也对了,
工具调用的检测宽,消息内容不漏掉,
一只小兔来庆祝,流式处理真到位!✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 标题准确概括了主要变更:将空响应视为失败并由模型层重试,与代码改动内容相符。
Description check ✅ Passed PR 描述清晰地说明了三个主要变更方向,与代码摘要中列举的改动内容相关且有意义。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/core/src/llm-core/platform/model.ts (2)

330-337: 类型签名扩展合理,需修复格式

将参数类型从 AIMessageChunk 扩展为 AIMessage | AIMessageChunk 是合理的,因为现在需要在非流式路径(第 507-509 行)中复用此方法。对 tool_call_chunks 的类型转换是必要的,因为只有 AIMessageChunk 有此属性。

🛠️ 修复 Prettier 格式
     private _hasToolCallChunk(message?: AIMessage | AIMessageChunk): boolean {
         return (
             (message?.tool_calls?.length ?? 0) > 0 ||
-            ((message as AIMessageChunk | undefined)?.tool_call_chunks?.length ??
-                0) > 0 ||
+            ((message as AIMessageChunk | undefined)?.tool_call_chunks
+                ?.length ?? 0) > 0 ||
             (message?.invalid_tool_calls?.length ?? 0) > 0
         )
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/llm-core/platform/model.ts` around lines 330 - 337, The
_hasToolCallChunk method signature change to accept AIMessage | AIMessageChunk
is fine; fix the formatting so it meets Prettier/style rules: ensure the
conditional expression is line-broken and indented cleanly, keep the explicit
cast for accessing tool_call_chunks on AIMessageChunk (e.g., (message as
AIMessageChunk | undefined)?.tool_call_chunks) and align the nullish coalescing
checks for tool_calls, tool_call_chunks, and invalid_tool_calls so the
parentheses and operators are on the correct lines to satisfy the project's
formatter.

256-260: 代码逻辑正确,但有格式问题

hasResponse 的追踪逻辑正确地区分了"收到数据块"和"收到有效内容"。静态分析工具指出此处有格式问题。

🛠️ 修复 Prettier 格式
                     hasResponse =
                         hasResponse ||
                         hasTool ||
-                        getMessageContent(chunk.message.content).trim().length > 0
+                        getMessageContent(chunk.message.content).trim()
+                            .length > 0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/llm-core/platform/model.ts` around lines 256 - 260,
代码逻辑无误但存在 Prettier 格式问题:在包含 hasResponse 更新和 yield chunk 的代码块(涉及变量
hasResponse、hasTool、getMessageContent 和 chunk)应用项目的 Prettier/格式化规则以修复换行与缩进,使表达式和
yield 语句符合统一风格(例如将条件表达式和 yield 分为单独行并确保正确空格/分号),然后保存以通过静态检查。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/core/src/llm-core/platform/model.ts`:
- Around line 330-337: The _hasToolCallChunk method signature change to accept
AIMessage | AIMessageChunk is fine; fix the formatting so it meets
Prettier/style rules: ensure the conditional expression is line-broken and
indented cleanly, keep the explicit cast for accessing tool_call_chunks on
AIMessageChunk (e.g., (message as AIMessageChunk | undefined)?.tool_call_chunks)
and align the nullish coalescing checks for tool_calls, tool_call_chunks, and
invalid_tool_calls so the parentheses and operators are on the correct lines to
satisfy the project's formatter.
- Around line 256-260: 代码逻辑无误但存在 Prettier 格式问题:在包含 hasResponse 更新和 yield chunk
的代码块(涉及变量 hasResponse、hasTool、getMessageContent 和 chunk)应用项目的
Prettier/格式化规则以修复换行与缩进,使表达式和 yield 语句符合统一风格(例如将条件表达式和 yield
分为单独行并确保正确空格/分号),然后保存以通过静态检查。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fa60dab4-877f-439b-b914-d78f7e70a20d

📥 Commits

Reviewing files that changed from the base of the PR and between 086a1f7 and d805a89.

📒 Files selected for processing (1)
  • packages/core/src/llm-core/platform/model.ts

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 improves response validation in ChatLunaChatModel by introducing a hasResponse flag to track whether a stream or completion contains actual content or tool calls, ensuring that empty responses are correctly identified as failures. Feedback suggests optimizing the streaming loop to avoid redundant string processing once a response is detected and notes that the final validation check is redundant for streaming paths already handled by internal assertions.

Comment on lines +256 to +259
hasResponse =
hasResponse ||
hasTool ||
getMessageContent(chunk.message.content).trim().length > 0
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.

medium

在流式响应的循环中,hasResponse 的判断逻辑可以进行微调以提高效率。一旦 hasResponse 变为 true,后续的 chunk 就不再需要调用 getMessageContenttrim() 进行检查。考虑到流式响应可能包含大量细碎的 chunk,这种优化在处理高吞吐量响应时是有益的。

Suggested change
hasResponse =
hasResponse ||
hasTool ||
getMessageContent(chunk.message.content).trim().length > 0
if (!hasResponse) {
hasResponse =
hasTool ||
getMessageContent(chunk.message.content).trim().length > 0
}

Comment on lines +504 to +514
if (
getMessageContent(response.message.content).trim()
.length < 1 &&
this._hasToolCallChunk(
response.message as AIMessage | AIMessageChunk
) !== true
) {
throw new ChatLunaError(
ChatLunaErrorCode.API_REQUEST_FAILED
)
}
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.

medium

这里的空响应检查逻辑在 options.streamtrue 时是冗余的。因为 _streamResponseChunks 内部已经通过 _ensureChunksReceived 确保了 hasResponsetrue 才会正常返回。如果流式响应为空,_streamResponseChunks 会直接抛出 API_REQUEST_FAILED。虽然保留此处的检查作为兜底是安全的,但将其逻辑明确区分或仅针对非流式路径(_completion)会更清晰。

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant