Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion coworker/providers/anthropic_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,15 +345,20 @@ def __init__(
*,
default_model: str = "claude-sonnet-4-6",
api_key: Optional[str] = None,
base_url: Optional[str] = None,
secrets: Any = None,
thinking_budget: Optional[int] = None,
):
# Mirrors OpenAIProvider: the SDK client is built lazily so engines can be assembled
# before any key exists; the key resolves at call time (explicit → env → SecretStore).
# Tests inject a `client` directly. `thinking_budget` (tokens, from the provider
# profile's optional field) opts every request into extended thinking.
# `base_url` points the same SDK at any Anthropic-protocol-compatible gateway (e.g.
# a third-party Claude/MiniMax endpoint at https://api.<vendor>/anthropic). When None,
# behavior is identical to stock Anthropic (api.anthropic.com).
self._client = client
self._api_key = api_key
self._base_url = base_url
self._secrets = secrets
self.default_model = default_model
self.thinking_budget = thinking_budget or 0
Expand All @@ -369,7 +374,10 @@ def _ensure_client(self) -> Any:
"No Anthropic API key configured. Set ANTHROPIC_API_KEY in the environment, "
"or add your key in Manage → Configure Models."
)
self._client = Anthropic(api_key=key)
kwargs: dict[str, Any] = {"api_key": key}
if self._base_url:
kwargs["base_url"] = self._base_url
self._client = Anthropic(**kwargs)
return self._client

def _request_kwargs(
Expand Down
1 change: 1 addition & 0 deletions coworker/providers/matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class ModelEntry:
"deepseek:deepseek-v4-pro": ModelEntry("DeepSeek V4 Pro · DeepSeek"),
"kimi:kimi-k2.6": ModelEntry("Kimi K2.6 · Moonshot"),
"minimax:MiniMax-M2.5": ModelEntry("MiniMax M2.5 · MiniMax"),
"minimax:MiniMax-M3": ModelEntry("MiniMax M3 · MiniMax"),
"qwen:qwen3-max": ModelEntry("Qwen3 Max · Alibaba"),
"xai:grok-4.3": ModelEntry("Grok 4.3 · xAI"),
"mistral:mistral-large-latest": ModelEntry("Mistral Large · Mistral"),
Expand Down
23 changes: 21 additions & 2 deletions coworker/providers/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,21 @@ def _build_anthropic(profile: dict[str, Any], secrets: Any) -> ProviderClient:
# deferred to first call so the provider can be built before a key exists.
# thinking_budget: hidden profile override — absent/invalid → the default (ON),
# explicit 0 → off (see DEFAULT_THINKING_BUDGET).
# base_url (optional): points the same Anthropic SDK at any Anthropic-protocol gateway
# (e.g. a third-party Claude/MiniMax endpoint). None → stock api.anthropic.com.
from .anthropic_provider import DEFAULT_THINKING_BUDGET

api_key = ((profile or {}).get("api_key") or "").strip() or None
base_url = ((profile or {}).get("base_url") or "").strip() or None
try:
thinking_budget = int(str((profile or {}).get("thinking_budget") or "").strip())
except ValueError:
thinking_budget = DEFAULT_THINKING_BUDGET
return AnthropicProvider(
api_key=api_key, secrets=secrets, thinking_budget=thinking_budget
api_key=api_key,
base_url=base_url,
secrets=secrets,
thinking_budget=thinking_budget,
)


Expand Down Expand Up @@ -229,6 +235,14 @@ def _compat(
secret=True,
placeholder="sk-ant-…",
),
ProviderField(
"base_url",
"Endpoint (optional)",
secret=False,
required=False,
placeholder="https://api.anthropic.com",
help="Leave blank for api.anthropic.com, or point at any Anthropic-protocol-compatible gateway (e.g. a third-party Claude/MiniMax endpoint).",
),
# No thinking_budget field (owner call 2026-07-23): extended thinking is
# on by default; the profile key stays a hidden override (0 = off).
],
Expand Down Expand Up @@ -410,8 +424,13 @@ def verify_provider_key(
key = (api_key or "").strip()
try:
if name == "anthropic":
# Honor a custom `base_url` so Settings → Test can probe third-party
# Anthropic-protocol gateways (e.g. a MiniMax-Claude endpoint). Strip a
# trailing slash and rely on the SDK's /v1 path default; fall back to
# stock api.anthropic.com when no override is set.
base = ((base_url or "").strip().rstrip("/") or "https://api.anthropic.com")
resp = httpx.get(
"https://api.anthropic.com/v1/models",
base + "/v1/models",
headers={"x-api-key": key, "anthropic-version": "2023-06-01"},
timeout=timeout,
)
Expand Down