feat: add Anthropic Claude Code OAuth provider and adaptive thinking support#5209
Open
Minidoracat wants to merge 3 commits intoAstrBotDevs:masterfrom
Open
feat: add Anthropic Claude Code OAuth provider and adaptive thinking support#5209Minidoracat wants to merge 3 commits intoAstrBotDevs:masterfrom
Minidoracat wants to merge 3 commits intoAstrBotDevs:masterfrom
Conversation
Contributor
There was a problem hiding this comment.
Hey - 我发现了 3 个问题,并且给了一些整体性的反馈:
- 在
ProviderAnthropicOAuth.__init__中,你依然让父类先用api_key构造了一个AsyncAnthropic客户端,然后立刻用auth_token客户端替换它;可以考虑调用super().__init__(..., use_api_key=False),并只初始化你真正需要的部分(keys、timeout 等),以避免多余的客户端构造。 - 1M 上下文模型的处理逻辑(
set_model和get_model_metadata_overrides中的_1M_CONTEXT_MODEL_PREFIXES检查)是重复的;可以考虑抽一个小的辅助方法(例如supports_1m_context(model_id)),把这部分逻辑集中到一处,便于将来新增模型时统一维护。 - 硬编码的
_OAUTH_DEFAULT_HEADERS(特别是 beta 标签和带版本号的标识)可能需要定期更新;你或许可以把它们暴露为配置项,或者至少集中放在某个共享/常量位置,并加上清晰的注释说明何时/如何更新,以避免长期失效。
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `ProviderAnthropicOAuth.__init__`, you're still letting the base class construct an `AsyncAnthropic` client with `api_key` before immediately replacing it with an `auth_token` client; consider calling `super().__init__(..., use_api_key=False)` and initializing only the pieces you need (keys, timeout, etc.) to avoid redundant client construction.
- The 1M-context model handling logic (the `_1M_CONTEXT_MODEL_PREFIXES` checks in both `set_model` and `get_model_metadata_overrides`) is duplicated; consider extracting a small helper (e.g. `supports_1m_context(model_id)`) to keep this logic centralized and easier to update when new models are added.
- The hard-coded `_OAUTH_DEFAULT_HEADERS` (especially the beta tags and versioned identifiers) may need periodic updates; you might want to surface these as configuration or at least group them somewhere shared/constant with a clear comment about when/how to update to avoid them going stale.
## Individual Comments
### Comment 1
<location> `astrbot/dashboard/routes/config.py:43-49` </location>
<code_context>
MAX_FILE_BYTES = 500 * 1024 * 1024
+def _apply_provider_metadata_overrides(
+ provider: Any, model_ids: list[str], metadata_map: dict
+) -> None:
+ override_fn = getattr(provider, "get_model_metadata_overrides", None)
+ if not callable(override_fn):
+ return
+ for mid, overrides in override_fn(model_ids).items():
+ merged = dict(metadata_map.get(mid, {}))
+ if "limit" in overrides:
</code_context>
<issue_to_address>
**issue (bug_risk):** 需要防止 `get_model_metadata_overrides` 返回 `None` 或非字典类型,以避免运行时错误。
这里对 `override_fn(model_ids).items()` 的调用假设该函数总是返回一个字典。如果它返回 `None` 或非映射类型,就会抛异常并导致 metadata 路由失败。可以考虑先做归一化处理,例如 `overrides_map = override_fn(model_ids) or {}`,并在迭代前验证它是否为映射类型。
</issue_to_address>
### Comment 2
<location> `astrbot/core/provider/sources/anthropic_source.py:71-85` </location>
<code_context>
proxy = provider_config.get("proxy", "")
return create_proxy_client("Anthropic", proxy)
+ def _apply_thinking_config(self, payloads: dict) -> None:
+ thinking_type = self.thinking_config.get("type", "")
+ if thinking_type == "adaptive":
+ payloads["thinking"] = {"type": "adaptive"}
+ effort = self.thinking_config.get("effort", "")
+ output_cfg = dict(payloads.get("output_config", {}))
+ if effort:
+ output_cfg["effort"] = effort
+ if output_cfg:
+ payloads["output_config"] = output_cfg
+ elif self.thinking_config.get("budget"):
+ payloads["thinking"] = {
+ "budget_tokens": self.thinking_config.get("budget"),
</code_context>
<issue_to_address>
**suggestion (bug_risk):** 让 `budget` 的处理方式与“仅在 `type` 为空时才生效”的文档行为保持一致。
当前 schema 说明 `budget` 只应在 `type` 为空时生效,但现在的代码会在 `budget` 为真值且 `type` 不等于 `"adaptive"` 时使用它(包括未知的 `type` 值)。为更好地匹配配置约定并避免意外行为,可以更新条件以要求 `type` 为空,例如使用 `elif not thinking_type and self.thinking_config.get("budget"):`,从而在 `type` 被设置时忽略 `budget`。
```suggestion
def _apply_thinking_config(self, payloads: dict) -> None:
thinking_type = self.thinking_config.get("type", "")
if thinking_type == "adaptive":
payloads["thinking"] = {"type": "adaptive"}
effort = self.thinking_config.get("effort", "")
output_cfg = dict(payloads.get("output_config", {}))
if effort:
output_cfg["effort"] = effort
if output_cfg:
payloads["output_config"] = output_cfg
elif not thinking_type and self.thinking_config.get("budget"):
payloads["thinking"] = {
"budget_tokens": self.thinking_config.get("budget"),
"type": "enabled",
}
```
</issue_to_address>
### Comment 3
<location> `astrbot/core/provider/sources/anthropic_oauth_source.py:34-42` </location>
<code_context>
+ "Anthropic Claude Code OAuth provider adapter",
+)
+class ProviderAnthropicOAuth(ProviderAnthropic):
+ def __init__(
+ self,
+ provider_config: dict,
+ provider_settings: dict,
+ ) -> None:
+ # 让父类通过 key 字段解析 API keys 列表(支持多组轮询)
+ super().__init__(provider_config, provider_settings)
+
+ # 使用 auth_token 替换父类的 api_key 客户端(OAuth 使用 Bearer 认证)
+ self.client = AsyncAnthropic(
+ auth_token=self.chosen_api_key,
</code_context>
<issue_to_address>
**suggestion:** 在 OAuth provider 中通过关闭基类的 `use_api_key`,避免构造不必要的 API-key 客户端。
当前的 `super().__init__(provider_config, provider_settings)` 会执行 `_init_api_key`,并构造一个基于 API key 的 `AsyncAnthropic` 客户端,而你会立即用 OAuth 客户端将其覆盖。为了避免这一步多余的构造并让意图更加清晰,可以向基类传入 `use_api_key=False`,并在这里显式设置 `chosen_api_key` / key 轮询逻辑(例如通过 `super().get_keys()`),这样 OAuth 路径就不会再实例化未被使用的 API-key 客户端。
建议实现如下:
```python
class ProviderAnthropicOAuth(ProviderAnthropic):
def __init__(
self,
provider_config: dict,
provider_settings: dict,
) -> None:
# 禁用父类的 API Key 客户端构建,仅复用其通用配置逻辑
# use_api_key=False 避免在基类中构造多余的 AsyncAnthropic 实例
super().__init__(provider_config, provider_settings, use_api_key=False)
# 本地解析 key 字段作为 OAuth 的 auth_token(支持多组轮询)
raw_keys = provider_config.get("key", []) or []
if isinstance(raw_keys, str):
keys = [raw_keys]
else:
keys = list(raw_keys)
# 记录可轮询的 token 列表和当前选中的 token
self._oauth_tokens = keys
self._oauth_token_index = 0
self.chosen_api_key = keys[self._oauth_token_index] if keys else ""
# 使用 auth_token(OAuth Bearer 认证)构建客户端,而不是基类的 api_key 客户端
self.client = AsyncAnthropic(
auth_token=self.chosen_api_key,
timeout=self.timeout,
base_url=self.base_url,
default_headers=_OAUTH_DEFAULT_HEADERS,
http_client=self._create_http_client(provider_config),
)
```
如果基类已经提供了类似 `get_keys()` 或 key 轮询的辅助方法,你可能还想:
1. 用该辅助方法(例如 `keys = self.get_keys()`)替代本地的 `raw_keys` 解析;
2. 可选地,把轮询集成进与其他地方相同的机制中(例如通过一个方法在每次请求前推进 `_oauth_token_index` 并更新 `self.chosen_api_key`)。
</issue_to_address>帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据反馈改进后续的审查建议。
Original comment in English
Hey - I've found 3 issues, and left some high level feedback:
- In
ProviderAnthropicOAuth.__init__, you're still letting the base class construct anAsyncAnthropicclient withapi_keybefore immediately replacing it with anauth_tokenclient; consider callingsuper().__init__(..., use_api_key=False)and initializing only the pieces you need (keys, timeout, etc.) to avoid redundant client construction. - The 1M-context model handling logic (the
_1M_CONTEXT_MODEL_PREFIXESchecks in bothset_modelandget_model_metadata_overrides) is duplicated; consider extracting a small helper (e.g.supports_1m_context(model_id)) to keep this logic centralized and easier to update when new models are added. - The hard-coded
_OAUTH_DEFAULT_HEADERS(especially the beta tags and versioned identifiers) may need periodic updates; you might want to surface these as configuration or at least group them somewhere shared/constant with a clear comment about when/how to update to avoid them going stale.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `ProviderAnthropicOAuth.__init__`, you're still letting the base class construct an `AsyncAnthropic` client with `api_key` before immediately replacing it with an `auth_token` client; consider calling `super().__init__(..., use_api_key=False)` and initializing only the pieces you need (keys, timeout, etc.) to avoid redundant client construction.
- The 1M-context model handling logic (the `_1M_CONTEXT_MODEL_PREFIXES` checks in both `set_model` and `get_model_metadata_overrides`) is duplicated; consider extracting a small helper (e.g. `supports_1m_context(model_id)`) to keep this logic centralized and easier to update when new models are added.
- The hard-coded `_OAUTH_DEFAULT_HEADERS` (especially the beta tags and versioned identifiers) may need periodic updates; you might want to surface these as configuration or at least group them somewhere shared/constant with a clear comment about when/how to update to avoid them going stale.
## Individual Comments
### Comment 1
<location> `astrbot/dashboard/routes/config.py:43-49` </location>
<code_context>
MAX_FILE_BYTES = 500 * 1024 * 1024
+def _apply_provider_metadata_overrides(
+ provider: Any, model_ids: list[str], metadata_map: dict
+) -> None:
+ override_fn = getattr(provider, "get_model_metadata_overrides", None)
+ if not callable(override_fn):
+ return
+ for mid, overrides in override_fn(model_ids).items():
+ merged = dict(metadata_map.get(mid, {}))
+ if "limit" in overrides:
</code_context>
<issue_to_address>
**issue (bug_risk):** Guard against `get_model_metadata_overrides` returning `None` or a non-dict to avoid runtime errors.
This call to `override_fn(model_ids).items()` assumes the function always returns a dict. If it returns `None` or a non-mapping, this will raise and break the metadata route. Consider normalizing, e.g. `overrides_map = override_fn(model_ids) or {}` and validating it’s a mapping before iterating.
</issue_to_address>
### Comment 2
<location> `astrbot/core/provider/sources/anthropic_source.py:71-85` </location>
<code_context>
proxy = provider_config.get("proxy", "")
return create_proxy_client("Anthropic", proxy)
+ def _apply_thinking_config(self, payloads: dict) -> None:
+ thinking_type = self.thinking_config.get("type", "")
+ if thinking_type == "adaptive":
+ payloads["thinking"] = {"type": "adaptive"}
+ effort = self.thinking_config.get("effort", "")
+ output_cfg = dict(payloads.get("output_config", {}))
+ if effort:
+ output_cfg["effort"] = effort
+ if output_cfg:
+ payloads["output_config"] = output_cfg
+ elif self.thinking_config.get("budget"):
+ payloads["thinking"] = {
+ "budget_tokens": self.thinking_config.get("budget"),
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Align `budget` handling with the documented behavior of only applying when `type` is empty.
The schema says `budget` should only apply when `type` is empty, but the code currently uses `budget` whenever it’s truthy and `type` is not `"adaptive"` (including unknown types). To better match the config contract and avoid unexpected behavior, update the condition to require an empty `type`, e.g. `elif not thinking_type and self.thinking_config.get("budget"):` so `budget` is ignored whenever `type` is set.
```suggestion
def _apply_thinking_config(self, payloads: dict) -> None:
thinking_type = self.thinking_config.get("type", "")
if thinking_type == "adaptive":
payloads["thinking"] = {"type": "adaptive"}
effort = self.thinking_config.get("effort", "")
output_cfg = dict(payloads.get("output_config", {}))
if effort:
output_cfg["effort"] = effort
if output_cfg:
payloads["output_config"] = output_cfg
elif not thinking_type and self.thinking_config.get("budget"):
payloads["thinking"] = {
"budget_tokens": self.thinking_config.get("budget"),
"type": "enabled",
}
```
</issue_to_address>
### Comment 3
<location> `astrbot/core/provider/sources/anthropic_oauth_source.py:34-42` </location>
<code_context>
+ "Anthropic Claude Code OAuth provider adapter",
+)
+class ProviderAnthropicOAuth(ProviderAnthropic):
+ def __init__(
+ self,
+ provider_config: dict,
+ provider_settings: dict,
+ ) -> None:
+ # 让父类通过 key 字段解析 API keys 列表(支持多组轮询)
+ super().__init__(provider_config, provider_settings)
+
+ # 使用 auth_token 替换父类的 api_key 客户端(OAuth 使用 Bearer 认证)
+ self.client = AsyncAnthropic(
+ auth_token=self.chosen_api_key,
</code_context>
<issue_to_address>
**suggestion:** Avoid constructing an unnecessary API-key client in the OAuth provider by disabling `use_api_key` in the base class.
`super().__init__(provider_config, provider_settings)` currently runs `_init_api_key` and builds an API-key–based `AsyncAnthropic` client that you immediately overwrite with the OAuth client. To avoid this redundant construction and clarify intent, pass `use_api_key=False` to the base class and explicitly set up `chosen_api_key` / key rotation here (e.g. via `super().get_keys()`), so the OAuth path never instantiates the unused API-key client.
Suggested implementation:
```python
class ProviderAnthropicOAuth(ProviderAnthropic):
def __init__(
self,
provider_config: dict,
provider_settings: dict,
) -> None:
# 禁用父类的 API Key 客户端构建,仅复用其通用配置逻辑
# use_api_key=False 避免在基类中构造多余的 AsyncAnthropic 实例
super().__init__(provider_config, provider_settings, use_api_key=False)
# 本地解析 key 字段作为 OAuth 的 auth_token(支持多组轮询)
raw_keys = provider_config.get("key", []) or []
if isinstance(raw_keys, str):
keys = [raw_keys]
else:
keys = list(raw_keys)
# 记录可轮询的 token 列表和当前选中的 token
self._oauth_tokens = keys
self._oauth_token_index = 0
self.chosen_api_key = keys[self._oauth_token_index] if keys else ""
# 使用 auth_token(OAuth Bearer 认证)构建客户端,而不是基类的 api_key 客户端
self.client = AsyncAnthropic(
auth_token=self.chosen_api_key,
timeout=self.timeout,
base_url=self.base_url,
default_headers=_OAUTH_DEFAULT_HEADERS,
http_client=self._create_http_client(provider_config),
)
```
If the base class already provides helpers like `get_keys()` or key-rotation utilities, you may want to:
1. Replace the local `raw_keys` parsing with a call to that helper (e.g., `keys = self.get_keys()`).
2. Optionally integrate rotation with the same mechanism used elsewhere (e.g., a method that advances `_oauth_token_index` and updates `self.chosen_api_key` before each request).
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Author
|
Thanks for the review! Addressed in 96b7208:
|
Author
|
@sourcery-ai review |
Contributor
There was a problem hiding this comment.
Hey - 我发现了两个问题,并给出了一些总体反馈:
- 在
ProviderAnthropicOAuth.__init__中,你调用super().__init__时没有传入use_api_key=False,这会导致基类先使用api_key构造一个AsyncAnthropic客户端,然后立刻用一个基于auth_token的客户端替换掉它;在这里传入use_api_key=False(只依赖 OAuth token)会更符合意图,也能避免多余的客户端构造。 dashboard/routes/config.py里的_apply_provider_metadata_overrides辅助函数目前只从 overrides 中合并limit字段;如果未来的 overrides 包含其他字段(例如展示名称、能力等),这些字段会被悄然丢弃——可以考虑要么合并所有字段,要么明确文档说明/在实现上强制只支持limit。
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `ProviderAnthropicOAuth.__init__`, you're calling `super().__init__` without `use_api_key=False`, which causes the base class to construct an `AsyncAnthropic` client with `api_key` only to immediately replace it with an `auth_token`-based client; passing `use_api_key=False` (and relying only on the OAuth token) would better reflect the intent and avoid redundant client construction.
- The `_apply_provider_metadata_overrides` helper in `dashboard/routes/config.py` currently only merges the `limit` key from overrides; if future overrides include other fields (e.g., display name, capabilities), they will be silently dropped—consider either merging all keys or explicitly documenting/enforcing that only `limit` is supported.
## Individual Comments
### Comment 1
<location> `astrbot/core/provider/sources/anthropic_oauth_source.py:34-40` </location>
<code_context>
+ "Anthropic Claude Code OAuth provider adapter",
+)
+class ProviderAnthropicOAuth(ProviderAnthropic):
+ def __init__(
+ self,
+ provider_config: dict,
+ provider_settings: dict,
+ ) -> None:
+ # 让父类通过 key 字段解析 API keys 列表(支持多组轮询)
+ super().__init__(provider_config, provider_settings)
+
+ # 使用 auth_token 替换父类的 api_key 客户端(OAuth 使用 Bearer 认证)
+ self.client = AsyncAnthropic(
+ auth_token=self.chosen_api_key,
+ timeout=self.timeout,
+ base_url=self.base_url,
+ default_headers=_OAUTH_DEFAULT_HEADERS,
+ http_client=self._create_http_client(provider_config),
+ )
+
</code_context>
<issue_to_address>
**suggestion:** Avoid double-constructing the Anthropic client in the OAuth subclass.
`ProviderAnthropic.__init__` now accepts `use_api_key` and builds an `AsyncAnthropic` client via `_init_api_key`. This subclass calls `super().__init__(provider_config, provider_settings)` without changing `use_api_key`, so a client is created with `api_key=` and then replaced by one using `auth_token=`. To avoid this redundant construction and keep client initialization clearer, pass `use_api_key=False` here and handle all OAuth-specific client setup in this subclass.
```suggestion
+ def __init__(
+ self,
+ provider_config: dict,
+ provider_settings: dict,
+ ) -> None:
+ # 禁用父类的 API key 客户端初始化,避免重复构造客户端
+ super().__init__(provider_config, provider_settings, use_api_key=False)
```
</issue_to_address>
### Comment 2
<location> `astrbot/dashboard/routes/config.py:43-52` </location>
<code_context>
MAX_FILE_BYTES = 500 * 1024 * 1024
+def _apply_provider_metadata_overrides(
+ provider: Any, model_ids: list[str], metadata_map: dict
+) -> None:
+ override_fn = getattr(provider, "get_model_metadata_overrides", None)
+ if not callable(override_fn):
+ return
+ overrides_map = override_fn(model_ids) or {}
+ for mid, overrides in overrides_map.items():
+ merged = dict(metadata_map.get(mid, {}))
+ if "limit" in overrides:
+ merged["limit"] = {**merged.get("limit", {}), **overrides["limit"]}
+ metadata_map[mid] = merged
+
+
</code_context>
<issue_to_address>
**suggestion:** Metadata override helper only merges `limit` and drops other override keys.
`_apply_provider_metadata_overrides` only merges the `limit` key from each override into `metadata_map`; all other keys are ignored. This matches `ProviderAnthropicOAuth.get_model_metadata_overrides` today, but prevents future providers from overriding additional metadata fields without changing this helper. If you expect more fields later, consider merging the entire override dict (treating `limit` specially if needed) so the helper stays extensible.
</issue_to_address>Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- In
ProviderAnthropicOAuth.__init__, you're callingsuper().__init__withoutuse_api_key=False, which causes the base class to construct anAsyncAnthropicclient withapi_keyonly to immediately replace it with anauth_token-based client; passinguse_api_key=False(and relying only on the OAuth token) would better reflect the intent and avoid redundant client construction. - The
_apply_provider_metadata_overrideshelper indashboard/routes/config.pycurrently only merges thelimitkey from overrides; if future overrides include other fields (e.g., display name, capabilities), they will be silently dropped—consider either merging all keys or explicitly documenting/enforcing that onlylimitis supported.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `ProviderAnthropicOAuth.__init__`, you're calling `super().__init__` without `use_api_key=False`, which causes the base class to construct an `AsyncAnthropic` client with `api_key` only to immediately replace it with an `auth_token`-based client; passing `use_api_key=False` (and relying only on the OAuth token) would better reflect the intent and avoid redundant client construction.
- The `_apply_provider_metadata_overrides` helper in `dashboard/routes/config.py` currently only merges the `limit` key from overrides; if future overrides include other fields (e.g., display name, capabilities), they will be silently dropped—consider either merging all keys or explicitly documenting/enforcing that only `limit` is supported.
## Individual Comments
### Comment 1
<location> `astrbot/core/provider/sources/anthropic_oauth_source.py:34-40` </location>
<code_context>
+ "Anthropic Claude Code OAuth provider adapter",
+)
+class ProviderAnthropicOAuth(ProviderAnthropic):
+ def __init__(
+ self,
+ provider_config: dict,
+ provider_settings: dict,
+ ) -> None:
+ # 让父类通过 key 字段解析 API keys 列表(支持多组轮询)
+ super().__init__(provider_config, provider_settings)
+
+ # 使用 auth_token 替换父类的 api_key 客户端(OAuth 使用 Bearer 认证)
+ self.client = AsyncAnthropic(
+ auth_token=self.chosen_api_key,
+ timeout=self.timeout,
+ base_url=self.base_url,
+ default_headers=_OAUTH_DEFAULT_HEADERS,
+ http_client=self._create_http_client(provider_config),
+ )
+
</code_context>
<issue_to_address>
**suggestion:** Avoid double-constructing the Anthropic client in the OAuth subclass.
`ProviderAnthropic.__init__` now accepts `use_api_key` and builds an `AsyncAnthropic` client via `_init_api_key`. This subclass calls `super().__init__(provider_config, provider_settings)` without changing `use_api_key`, so a client is created with `api_key=` and then replaced by one using `auth_token=`. To avoid this redundant construction and keep client initialization clearer, pass `use_api_key=False` here and handle all OAuth-specific client setup in this subclass.
```suggestion
+ def __init__(
+ self,
+ provider_config: dict,
+ provider_settings: dict,
+ ) -> None:
+ # 禁用父类的 API key 客户端初始化,避免重复构造客户端
+ super().__init__(provider_config, provider_settings, use_api_key=False)
```
</issue_to_address>
### Comment 2
<location> `astrbot/dashboard/routes/config.py:43-52` </location>
<code_context>
MAX_FILE_BYTES = 500 * 1024 * 1024
+def _apply_provider_metadata_overrides(
+ provider: Any, model_ids: list[str], metadata_map: dict
+) -> None:
+ override_fn = getattr(provider, "get_model_metadata_overrides", None)
+ if not callable(override_fn):
+ return
+ overrides_map = override_fn(model_ids) or {}
+ for mid, overrides in overrides_map.items():
+ merged = dict(metadata_map.get(mid, {}))
+ if "limit" in overrides:
+ merged["limit"] = {**merged.get("limit", {}), **overrides["limit"]}
+ metadata_map[mid] = merged
+
+
</code_context>
<issue_to_address>
**suggestion:** Metadata override helper only merges `limit` and drops other override keys.
`_apply_provider_metadata_overrides` only merges the `limit` key from each override into `metadata_map`; all other keys are ignored. This matches `ProviderAnthropicOAuth.get_model_metadata_overrides` today, but prevents future providers from overriding additional metadata fields without changing this helper. If you expect more fields later, consider merging the entire override dict (treating `limit` specially if needed) so the helper stays extensible.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- Use use_api_key=False in OAuth subclass to avoid redundant API-key client construction before replacing with auth_token client - Generalize metadata override helper to merge all dict keys instead of only handling 'limit', improving extensibility
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.
Summary
Add a new Anthropic Claude Code OAuth provider adapter that uses
claude setup-tokengenerated long-lived OAuth tokens (valid 1 year) with Bearer authentication, replacing the need for standard API keys.Key Changes
New Provider:
anthropic_oauth(anthropic_oauth_source.py, 137 lines)ProviderAnthropicwithuse_api_key=Falseto skip redundant API-key client constructionkeyfield for OAuth tokens (multi-token round-robin rotation supported)auth_token=(Bearer auth) instead ofapi_key=anthropic-beta,user-agent,x-app,anthropic-dangerous-direct-browser-access)claude-opus-4-6/claude-sonnet-4-6modelsAdaptive Thinking Support (
anthropic_source.py)adaptivethinking type with configurable effort levels (low,medium,high,max)output_config.effortparameter for the new Anthropic API formatuse_api_keyconstructor parameter to allow subclasses to customize key initializationDashboard & i18n
anthropic_oauthis selected, the hint changes to explainclaude setup-tokenusageHow It Works
claude setup-tokenin terminal to get a long-lived OAuth token (sk-ant-oat01-...)Files Changed
astrbot/core/provider/sources/anthropic_oauth_source.pyastrbot/core/provider/sources/anthropic_source.pyuse_api_keyparam + adaptive thinkingastrbot/core/config/default.pyanthropic_oauthastrbot/core/provider/manager.pyanthropic_oauthastrbot/dashboard/routes/config.pydashboard/src/composables/useProviderSources.tsdashboard/src/i18n/locales/*/features/provider.jsondashboard/src/i18n/locales/*/features/config-metadata.jsonSupersedes #5175.
摘要
新增 Anthropic Claude Code OAuth 提供商适配器,使用
claude setup-token生成的长效 OAuth 令牌(有效期 1 年)进行 Bearer 认证,替代标准 API Key。主要变更
新提供商:
anthropic_oauthProviderAnthropic,通过use_api_key=False跳过冗余的 API Key 客户端构造key字段存放 OAuth 令牌(支持多组令牌轮询)auth_token=(Bearer 认证)创建 Anthropic 客户端anthropic-beta、user-agent、x-app、anthropic-dangerous-direct-browser-access)claude-opus-4-6/claude-sonnet-4-6模型自动设置 1M 上下文窗口自适应思考支持
adaptive思考类型,支持可配置的思考深度等级(low、medium、high、max)output_config.effort参数Dashboard 与 i18n
使用方式
claude setup-token获取长效 OAuth 令牌(sk-ant-oat01-...)运行截图