refactor: remove Anthropic OAuth provider implementation and related metadata overrides

This commit is contained in:
Soulter
2026-02-21 23:40:04 +08:00
parent 478cc32de1
commit dbeadb6833
7 changed files with 1 additions and 182 deletions
-12
View File
@@ -981,18 +981,6 @@ CONFIG_METADATA_2 = {
"proxy": "",
"anth_thinking_config": {"type": "", "budget": 0, "effort": ""},
},
"Anthropic (Claude Code OAuth)": {
"id": "anthropic_claude_code_oauth",
"provider": "anthropic",
"type": "anthropic_oauth",
"provider_type": "chat_completion",
"enable": True,
"api_base": "https://api.anthropic.com",
"timeout": 120,
"proxy": "",
"anth_thinking_config": {"type": "", "budget": 0, "effort": ""},
"key": [],
},
"Moonshot": {
"id": "moonshot",
"provider": "moonshot",
-4
View File
@@ -309,10 +309,6 @@ class ProviderManager:
from .sources.anthropic_source import (
ProviderAnthropic as ProviderAnthropic,
)
case "anthropic_oauth":
from .sources.anthropic_oauth_source import (
ProviderAnthropicOAuth as ProviderAnthropicOAuth,
)
case "googlegenai_chat_completion":
from .sources.gemini_source import (
ProviderGoogleGenAI as ProviderGoogleGenAI,
@@ -1,140 +0,0 @@
from collections.abc import AsyncGenerator
from anthropic import AsyncAnthropic
from astrbot.core.provider.entities import LLMResponse
from ..register import register_provider_adapter
from .anthropic_source import ProviderAnthropic
_OAUTH_DEFAULT_HEADERS = {
"anthropic-beta": "claude-code-20250219,oauth-2025-04-20,context-1m-2025-08-07",
"user-agent": "claude-cli/1.0.0 (external, cli)",
"x-app": "cli",
"anthropic-dangerous-direct-browser-access": "true",
}
_CLAUDE_CODE_SYSTEM_PREFIX = (
"You are Claude Code, Anthropic's official CLI for Claude.\n\n"
)
# 支持 1M 上下文窗口的模型前缀(需配合 context-1m beta header)。
# 新增 4.6+ 模型时需同步更新此列表。
_1M_CONTEXT_MODEL_PREFIXES = (
"claude-opus-4-6",
"claude-sonnet-4-6",
)
@register_provider_adapter(
"anthropic_oauth",
"Anthropic Claude Code OAuth provider adapter",
)
class ProviderAnthropicOAuth(ProviderAnthropic):
def __init__(
self,
provider_config: dict,
provider_settings: dict,
) -> None:
# 禁用父类的 API key 客户端初始化,避免重复构造客户端
super().__init__(provider_config, provider_settings, use_api_key=False)
# 手动解析 key 列表(父类跳过了 _init_api_key
self.api_keys: list = self.get_keys()
self.chosen_api_key: str = self.api_keys[0] if self.api_keys else ""
# 使用 auth_tokenOAuth 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),
)
def set_model(self, model_name: str) -> None:
super().set_model(model_name)
if any(model_name.startswith(p) for p in _1M_CONTEXT_MODEL_PREFIXES):
if self.provider_config.get("max_context_tokens", 0) <= 0:
self.provider_config["max_context_tokens"] = 1_000_000
def get_model_metadata_overrides(self, model_ids: list[str]) -> dict[str, dict]:
overrides = {}
for mid in model_ids:
if any(mid.startswith(p) for p in _1M_CONTEXT_MODEL_PREFIXES):
overrides[mid] = {"limit": {"context": 1_000_000}}
return overrides
def set_key(self, key: str) -> None:
self.chosen_api_key = key
# 切换 key 时需要重建客户端以使用新的 auth_token
self.client = AsyncAnthropic(
auth_token=key,
timeout=self.timeout,
base_url=self.base_url,
default_headers=_OAUTH_DEFAULT_HEADERS,
http_client=self._create_http_client(self.provider_config),
)
async def get_models(self) -> list[str]:
return await super().get_models()
async def test(self, timeout: float = 45.0) -> None:
await super().test(timeout)
async def text_chat(
self,
prompt=None,
session_id=None,
image_urls=None,
func_tool=None,
contexts=None,
system_prompt=None,
tool_calls_result=None,
model=None,
extra_user_content_parts=None,
**kwargs,
) -> LLMResponse:
system_prompt = _CLAUDE_CODE_SYSTEM_PREFIX + (system_prompt or "")
return await super().text_chat(
prompt=prompt,
session_id=session_id,
image_urls=image_urls,
func_tool=func_tool,
contexts=contexts,
system_prompt=system_prompt,
tool_calls_result=tool_calls_result,
model=model,
extra_user_content_parts=extra_user_content_parts,
**kwargs,
)
async def text_chat_stream(
self,
prompt=None,
session_id=None,
image_urls=None,
func_tool=None,
contexts=None,
system_prompt=None,
tool_calls_result=None,
model=None,
extra_user_content_parts=None,
**kwargs,
) -> AsyncGenerator[LLMResponse, None]:
system_prompt = _CLAUDE_CODE_SYSTEM_PREFIX + (system_prompt or "")
async for llm_response in super().text_chat_stream(
prompt=prompt,
session_id=session_id,
image_urls=image_urls,
func_tool=func_tool,
contexts=contexts,
system_prompt=system_prompt,
tool_calls_result=tool_calls_result,
model=model,
extra_user_content_parts=extra_user_content_parts,
**kwargs,
):
yield llm_response
-21
View File
@@ -40,23 +40,6 @@ from .util import (
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, {}))
for key, value in overrides.items():
if isinstance(value, dict):
merged[key] = {**merged.get(key, {}), **value}
else:
merged[key] = value
metadata_map[mid] = merged
def try_cast(value: Any, type_: str):
if type_ == "int":
try:
@@ -744,8 +727,6 @@ class ConfigRoute(Route):
if meta:
metadata_map[model_id] = meta
_apply_provider_metadata_overrides(provider, models, metadata_map)
ret = {
"models": models,
"provider_id": provider_id,
@@ -891,8 +872,6 @@ class ConfigRoute(Route):
if meta:
metadata_map[model_id] = meta
_apply_provider_metadata_overrides(inst, models, metadata_map)
# 销毁实例(如果有 terminate 方法)
terminate_fn = getattr(inst, "terminate", None)
if inspect.iscoroutinefunction(terminate_fn):
@@ -241,9 +241,7 @@ export function useProviderSources(options: UseProviderSourcesOptions) {
// 为 provider source 的 id 字段添加自定义 hint
if (customSchema.provider?.items?.id) {
customSchema.provider.items.id.hint = tm('providerSources.hints.id')
customSchema.provider.items.key.hint = editableProviderSource.value?.type === 'anthropic_oauth'
? tm('providerSources.hints.oauthToken')
: tm('providerSources.hints.key')
customSchema.provider.items.key.hint = tm('providerSources.hints.key')
customSchema.provider.items.api_base.hint = tm('providerSources.hints.apiBase')
}
// 为 proxy 字段添加描述和提示
@@ -114,7 +114,6 @@
"hints": {
"id": "Provider source ID (not provider ID)",
"key": "API key for authentication",
"oauthToken": "Run `claude setup-token` in your terminal to get a long-lived OAuth token, then paste it here. Token is valid for 1 year.",
"apiBase": "Custom API endpoint URL",
"proxy": "HTTP/HTTPS proxy address, e.g. http://127.0.0.1:7890. Only affects this provider's API requests, doesn't interfere with Docker internal networking."
},
@@ -115,7 +115,6 @@
"hints": {
"id": "提供商源唯一 ID(不是提供商 ID)",
"key": "API 密钥",
"oauthToken": "在终端运行 `claude setup-token` 获取长期有效的 OAuth Token,然后粘贴到此处。Token 有效期为 1 年。",
"apiBase": "自定义 API 端点 URL",
"proxy": "HTTP/HTTPS 代理地址,格式如 http://127.0.0.1:7890。仅对该提供商的 API 请求生效,不影响 Docker 内网通信。"
},