Skip to content

Conversation

@Soulter
Copy link
Member

@Soulter Soulter commented Dec 26, 2025

Modifications / 改动点

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

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


Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ 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.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

Summary by Sourcery

通过验证工具调用数据并始终提供 max_tokens 值,确保 Anthropic 提供方的请求更加健壮。

Bug Fixes:

  • 仅在 tool_calls 为列表时才进行迭代,避免在构建 Anthropic 负载时出现错误。
  • 当未提供 max_tokens 时,将其默认设置为 1024,防止 Anthropic 的非流式和流式请求遗漏 max_tokens
  • 放宽 text_chat_stream 参数的默认值设置,使得在没有 prompt、图片 URL 或上下文的调用情况下也能更安全地处理。
Original summary in English

Summary by Sourcery

Ensure Anthropic provider requests are robust by validating tool call data and always supplying a max_tokens value.

Bug Fixes:

  • Avoid errors in Anthropic payload construction by only iterating tool_calls when it is a list.
  • Prevent Anthropic non-streaming and streaming requests from omitting max_tokens by defaulting it to 1024 when absent.
  • Relax text_chat_stream argument defaults so calls without prompt, image URLs, or contexts are handled more safely.

@dosubot dosubot bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Dec 26, 2025
@Soulter Soulter changed the title fix: ensure max_tokens is set and validate tool_calls type in ProviderAnthropic fix: anthropic chat provider query error Dec 26, 2025
@Soulter Soulter merged commit 19541d9 into master Dec 26, 2025
4 of 5 checks passed
Copy link
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 - 我发现了 2 个问题,并给出了一些整体性的反馈:

  • 硬编码的 max_tokens = 1024 默认值有点像“魔法数字”;建议从配置或共享常量中派生这个值,这样就可以根据不同模型/提供方进行调整,而不是固定写死在代码里。
  • 在校验 tool_calls 时,isinstance(message["tool_calls"], list) 会在值不是列表时静默忽略;如果这是用户输入,你可能需要把单个 tool_call 规范化为列表,或者至少进行日志记录/抛错,以便让无效结构更容易被发现。
  • text_chat_stream 的默认值从 ... 改为 None 会改变缺失参数的检测方式;请再次确认调用方或内部逻辑没有依赖 Ellipsis 作为区分“未设置”和“明确传入 None”的哨兵值。
用于 AI Agent 的提示词
请根据以下代码审查意见进行修改:

## 整体评论
- 硬编码的 `max_tokens = 1024` 默认值有点像“魔法数字”;建议从配置或共享常量中派生这个值,这样就可以根据不同模型/提供方进行调整,而不是固定写死在代码里。
- 在校验 `tool_calls` 时,`isinstance(message["tool_calls"], list)` 会在值不是列表时静默忽略;如果这是用户输入,你可能需要把单个 `tool_call` 规范化为列表,或者至少进行日志记录/抛错,以便让无效结构更容易被发现。
-`text_chat_stream` 的默认值从 `...` 改为 `None` 会改变缺失参数的检测方式;请再次确认调用方或内部逻辑没有依赖 `Ellipsis` 作为区分“未设置”和“明确传入 None”的哨兵值。

## 单条评论

### 评论 1
<location> `astrbot/core/provider/sources/anthropic_source.py:71` </location>
<code_context>
                 if isinstance(message["content"], str):
                     blocks.append({"type": "text", "text": message["content"]})
-                if "tool_calls" in message:
+                if "tool_calls" in message and isinstance(message["tool_calls"], list):
                     for tool_call in message["tool_calls"]:
                         blocks.append(  # noqa: PERF401
</code_context>

<issue_to_address>
**issue (bug_risk):** 仅对 `tool_calls` 使用列表类型判断,可能会静默丢弃有效的可迭代值。

之前任何为真值的 `tool_calls` 可迭代对象都会被遍历;现在非列表的可迭代对象(例如元组、生成器)会被静默忽略。为避免这种行为变化悄然发生,建议要么通过 `message["tool_calls"] = list(message["tool_calls"])` 进行规范化,要么在 `tool_calls` 存在但不是列表时进行抛错/记录日志,让类型问题被暴露出来而不是被跳过。
</issue_to_address>

### 评论 2
<location> `astrbot/core/provider/sources/anthropic_source.py:135` </location>
<code_context>

         extra_body = self.provider_config.get("custom_extra_body", {})

+        if "max_tokens" not in payloads:
+            payloads["max_tokens"] = 1024
+
</code_context>

<issue_to_address>
**suggestion:** 在两个地方硬编码 `max_tokens=1024` 可能缺乏灵活性,而且容易与配置或模型上限产生偏差。

现在只要调用方省略 `max_tokens`,就会使用这个默认值,这可能会相较于之前的行为截断响应长度,并且 `_query``_query_stream` 中都重复了这段逻辑。建议从 `provider_config` 或模型特定的映射中派生一个统一的默认值,并在两个位置复用它,这样更便于调整并与模型/部署的限制保持一致。

建议实现如下:

```python
        extra_body = self.provider_config.get("custom_extra_body", {})

        # 从 provider_config/model 限制中推导可配置的默认 max_tokens
        default_max_tokens = self.provider_config.get("max_tokens", 1024)

        if "max_tokens" not in payloads and default_max_tokens is not None:
            payloads["max_tokens"] = default_max_tokens

        completion = await self.client.messages.create(

````_query_stream` 方法(或等价的流式路径)中很可能存在类似的 `if "max_tokens" not in payloads:` 代码块。
在那里的替换方式也相同:去掉硬编码的 `1024`,改为计算 `default_max_tokens = self.provider_config.get("max_tokens", 1024)`,在 `payloads` 中缺少 `max_tokens` 时使用该值作为后备。 
如果你有模型特定的上限,可以把 `self.provider_config.get("max_tokens", 1024)` 替换成基于模型的查找(例如通过 `self.model` 或部署名称),但保留相同的整体模式。
</issue_to_address>

Sourcery 对开源项目免费使用——如果你喜欢这些 Review,欢迎帮忙分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的 Review。
Original comment in English

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

  • The hardcoded max_tokens = 1024 default is a bit of a magic number; consider deriving it from configuration or a shared constant so it can be tuned per-model/provider rather than being fixed in code.
  • When validating tool_calls, isinstance(message["tool_calls"], list) silently drops non-list values; if this is user input, you may want to normalize single tool_calls into a list or at least log/raise to make invalid shapes more visible.
  • Changing text_chat_stream defaults from ... to None alters how missing arguments are detected; double-check that callers or internal logic are not relying on Ellipsis as a sentinel to distinguish between “unset” and “explicitly passed None.”
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The hardcoded `max_tokens = 1024` default is a bit of a magic number; consider deriving it from configuration or a shared constant so it can be tuned per-model/provider rather than being fixed in code.
- When validating `tool_calls`, `isinstance(message["tool_calls"], list)` silently drops non-list values; if this is user input, you may want to normalize single tool_calls into a list or at least log/raise to make invalid shapes more visible.
- Changing `text_chat_stream` defaults from `...` to `None` alters how missing arguments are detected; double-check that callers or internal logic are not relying on `Ellipsis` as a sentinel to distinguish between “unset” and “explicitly passed None.”

## Individual Comments

### Comment 1
<location> `astrbot/core/provider/sources/anthropic_source.py:71` </location>
<code_context>
                 if isinstance(message["content"], str):
                     blocks.append({"type": "text", "text": message["content"]})
-                if "tool_calls" in message:
+                if "tool_calls" in message and isinstance(message["tool_calls"], list):
                     for tool_call in message["tool_calls"]:
                         blocks.append(  # noqa: PERF401
</code_context>

<issue_to_address>
**issue (bug_risk):** Guarding on list type for `tool_calls` may silently drop valid iterable values.

Previously any truthy `tool_calls` iterable would be iterated; now non-list iterables (e.g., tuples, generators) are silently ignored. To avoid this behavior change going unnoticed, either normalize with `message["tool_calls"] = list(message["tool_calls"])` or raise/log when `tool_calls` is present but not a list so type issues are surfaced rather than skipped.
</issue_to_address>

### Comment 2
<location> `astrbot/core/provider/sources/anthropic_source.py:135` </location>
<code_context>

         extra_body = self.provider_config.get("custom_extra_body", {})

+        if "max_tokens" not in payloads:
+            payloads["max_tokens"] = 1024
+
</code_context>

<issue_to_address>
**suggestion:** Hard-coded `max_tokens=1024` in two places may be inflexible and easy to drift from config or model limits.

This default now applies whenever callers omit `max_tokens`, which may truncate responses relative to prior behavior, and is duplicated in both `_query` and `_query_stream`. Consider deriving a single default from `provider_config` or a model-specific mapping and reusing it in both places so it’s easier to adjust and aligned with model/deployment limits.

Suggested implementation:

```python
        extra_body = self.provider_config.get("custom_extra_body", {})

        # Derive a configurable default max_tokens, aligned with provider_config/model limits
        default_max_tokens = self.provider_config.get("max_tokens", 1024)

        if "max_tokens" not in payloads and default_max_tokens is not None:
            payloads["max_tokens"] = default_max_tokens

        completion = await self.client.messages.create(

```

There is likely a similar `if "max_tokens" not in payloads:` block in the `_query_stream` method (or equivalent streaming path). 
Apply the same replacement there: remove the hard-coded `1024` and instead compute `default_max_tokens = self.provider_config.get("max_tokens", 1024)` and use that as the fallback when `max_tokens` is absent from `payloads`. 
If you have model-specific limits, you can substitute `self.provider_config.get("max_tokens", 1024)` with a model-aware lookup (e.g., from `self.model` or deployment name) while keeping the same pattern.
</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.

if isinstance(message["content"], str):
blocks.append({"type": "text", "text": message["content"]})
if "tool_calls" in message:
if "tool_calls" in message and isinstance(message["tool_calls"], list):
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): 仅对 tool_calls 使用列表类型判断,可能会静默丢弃有效的可迭代值。

之前任何为真值的 tool_calls 可迭代对象都会被遍历;现在非列表的可迭代对象(例如元组、生成器)会被静默忽略。为避免这种行为变化悄然发生,建议要么通过 message["tool_calls"] = list(message["tool_calls"]) 进行规范化,要么在 tool_calls 存在但不是列表时进行抛错/记录日志,让类型问题被暴露出来而不是被跳过。

Original comment in English

issue (bug_risk): Guarding on list type for tool_calls may silently drop valid iterable values.

Previously any truthy tool_calls iterable would be iterated; now non-list iterables (e.g., tuples, generators) are silently ignored. To avoid this behavior change going unnoticed, either normalize with message["tool_calls"] = list(message["tool_calls"]) or raise/log when tool_calls is present but not a list so type issues are surfaced rather than skipped.

@dosubot dosubot bot added the area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. label Dec 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants