在llm配置器中加入deepseek Anthropic API格式的支持新页面#7760
Open
MostimaBridges wants to merge 3 commits intoAstrBotDevs:masterfrom
Open
在llm配置器中加入deepseek Anthropic API格式的支持新页面#7760MostimaBridges wants to merge 3 commits intoAstrBotDevs:masterfrom
MostimaBridges wants to merge 3 commits intoAstrBotDevs:masterfrom
Conversation
Contributor
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In
buildProviderSourceSchemayou deep-clone viaJSON.parse(JSON.stringify(schema)), which will silently strip non-JSON-safe values (e.g. functions, Dates); consider using a more robust clone utility orstructuredCloneto avoid unexpected loss of metadata if the schema becomes richer in the future. - The DeepSeek-specific checks (
isDeepSeekAnthropicSource,isDeepSeekOpenAISource,_is_deepseek_provider) currently rely on hardcodedprovider/typestrings and a substring match onapi.deepseek.com/anthropic; it may be worth centralizing these rules or making the URL match slightly more flexible to tolerate regional/alternative DeepSeek Anthropic endpoints.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `buildProviderSourceSchema` you deep-clone via `JSON.parse(JSON.stringify(schema))`, which will silently strip non-JSON-safe values (e.g. functions, Dates); consider using a more robust clone utility or `structuredClone` to avoid unexpected loss of metadata if the schema becomes richer in the future.
- The DeepSeek-specific checks (`isDeepSeekAnthropicSource`, `isDeepSeekOpenAISource`, `_is_deepseek_provider`) currently rely on hardcoded `provider`/`type` strings and a substring match on `api.deepseek.com/anthropic`; it may be worth centralizing these rules or making the URL match slightly more flexible to tolerate regional/alternative DeepSeek Anthropic endpoints.
## Individual Comments
### Comment 1
<location path="astrbot/core/provider/sources/anthropic_source.py" line_range="36-38" />
<code_context>
class ProviderAnthropic(Provider):
+ IMAGE_PLACEHOLDER_TEXT = "[Image Attachment]"
+
@staticmethod
def _ensure_usable_response(
llm_response: LLMResponse,
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Handle dict-style `tool_choice` values that use OpenAI-style `"required"` semantics.
In `_normalize_tool_choice`, the string value `"required"` is mapped to `{ "type": "any" }`, but when `tool_choice` is a dict, only `"auto" | "any" | "none" | "tool"` are handled. A dict like `{ "type": "required" }` currently results in `None` and then defaults to `{ "type": "auto" }`, changing the intended behavior.
To keep behavior consistent and avoid subtle bugs, either handle dict `{"type": "required"}` the same as the string case (map to `{ "type": "any" }`) or explicitly reject unsupported dict values instead of silently falling back to `auto`.
Suggested implementation:
```python
def _normalize_tool_choice(self, tool_choice: Any) -> dict | None:
"""
Normalize various tool_choice formats into Anthropic's expected schema.
Supports:
- String values: "auto" | "any" | "none" | "tool" | "required"
- Dict values: {"type": "...", ...} with the same allowed types
OpenAI-style `"required"` semantics are mapped to Anthropic's `"any"`:
- "required" -> {"type": "any"}
- {"type": "required"} -> {"type": "any"}
"""
# String-style tool_choice
if isinstance(tool_choice, str):
normalized = tool_choice.strip().lower()
if normalized == "required":
# OpenAI "required" => Anthropic "any"
return {"type": "any"}
if normalized in ("auto", "any", "none"):
return {"type": normalized}
# For "tool" as a bare string we cannot know which tool, so treat as "any"
if normalized == "tool":
return {"type": "any"}
return None
# Dict-style tool_choice
if isinstance(tool_choice, dict):
type_value = str(tool_choice.get("type", "")).strip().lower()
# Keep behavior consistent with string-style "required"
if type_value == "required":
return {"type": "any"}
if type_value in ("auto", "any", "none"):
return {"type": type_value}
if type_value == "tool":
# Anthropic expects {"type": "tool", "name": "<tool_name>"}
tool_name = tool_choice.get("name")
if isinstance(tool_name, str) and tool_name:
return {"type": "tool", "name": tool_name}
# If tool name is missing/invalid, fall back to "any" rather than silently "auto"
return {"type": "any"}
# Explicitly reject unsupported dict values instead of silently falling back to "auto"
raise ValueError(
f"Unsupported tool_choice dict type: {tool_choice!r}. "
'Expected "auto", "any", "none", "tool", or "required".'
)
# Unsupported type, let caller decide default behavior
return None
def _is_deepseek_provider(self) -> bool:
provider = str(self.provider_config.get("provider", "")).strip().lower()
if provider == "deepseek":
return True
return "api.deepseek.com/anthropic" in str(self.base_url).strip().lower()
def _supports_image_input(self) -> bool:
```
I had to infer the location and signature of `_normalize_tool_choice` because it wasn’t present in the provided snippet. To integrate this correctly you should:
1. Remove or adjust any existing `_normalize_tool_choice` implementation to avoid duplicate definitions, merging the new dict-handling logic (especially the `"required"` mapping and the explicit `ValueError` for unsupported types) into your current function.
2. Ensure callers of `_normalize_tool_choice` are prepared for the possibility that it now raises `ValueError` for invalid dict-style `tool_choice` values; if you prefer not to raise, you can instead return `None` in that branch, but the `"required"` → `{"type": "any"}` mapping should remain.
3. If your codebase uses different typing conventions (e.g., `Dict[str, Any]` instead of `dict` or no `| None`), adjust the type hints accordingly to match your existing style.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Contributor
There was a problem hiding this comment.
Code Review
This pull request introduces support for DeepSeek's Anthropic-compatible endpoint, including a new provider template and logic for handling thinking configurations and image placeholders. It also enhances the dashboard with provider-specific hints and localized descriptions. A critical issue was identified in the Anthropic provider adapter where enabling thinking for DeepSeek removes the required budget_tokens field, which could lead to API validation errors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #7757
新增 DeepSeek Anthropic API 格式支持,并补充对应的 WebUI 配置入口与文案说明。
Modifications / 改动点
This is NOT a breaking change. / 这不是一个破坏性变更。
Screenshots or Test Results / 运行截图或测试结果
_apply_provider_specific_payload_overrides会将 thinking 归一化为 enabled、仅保留 output_config.effort。Checklist / 检查清单
Summary by Sourcery
Add DeepSeek Anthropic-compatible support across backend provider templates, Anthropic adapter logic, and the WebUI provider configuration experience.
New Features:
Enhancements:
Documentation: