diff --git a/agent/anthropic_adapter.py b/agent/anthropic_adapter.py index ea09c11ea4ff..b612437029d6 100644 --- a/agent/anthropic_adapter.py +++ b/agent/anthropic_adapter.py @@ -915,10 +915,18 @@ def normalize_model_name(model: str, preserve_dots: bool = False) -> str: - Converts dots to hyphens in version numbers (OpenRouter uses dots, Anthropic uses hyphens: claude-opus-4.6 → claude-opus-4-6), unless preserve_dots is True (e.g. for Alibaba/DashScope: qwen3.5-plus). + - Bedrock cross-region inference profile IDs (us.*, eu.*, ap.*, global.*) + use dots as namespace separators — never mangle those. """ lower = model.lower() if lower.startswith("anthropic/"): model = model[len("anthropic/"):] + lower = model.lower() + # Bedrock inference profile IDs start with a region prefix followed by a dot + # (e.g. us.anthropic.claude-haiku-4-5-20251001-v1:0). Preserve as-is. + _BEDROCK_PREFIXES = ("us.", "eu.", "ap.", "global.") + if any(lower.startswith(p) for p in _BEDROCK_PREFIXES): + return model if not preserve_dots: # OpenRouter uses dots for version separators (claude-opus-4.6), # Anthropic uses hyphens (claude-opus-4-6). Convert dots to hyphens. diff --git a/agent/auxiliary_client.py b/agent/auxiliary_client.py index 5735648f13fd..f874766d11af 100644 --- a/agent/auxiliary_client.py +++ b/agent/auxiliary_client.py @@ -1885,6 +1885,24 @@ def _wrap_if_needed(client_obj, final_model_str: str, base_url_str: str = ""): "directly supported, try 'auto'", provider) return None, None + elif pconfig.auth_type == "aws_sdk": + # Bedrock provider — wrap AnthropicBedrock in AnthropicAuxiliaryClient + try: + from agent.bedrock_adapter import has_aws_credentials, resolve_bedrock_region + from agent.anthropic_adapter import build_anthropic_bedrock_client + except ImportError as exc: + logger.warning("resolve_provider_client: bedrock imports failed: %s", exc) + return None, None + if not has_aws_credentials(): + logger.warning("resolve_provider_client: bedrock requested but no AWS credentials found") + return None, None + region = resolve_bedrock_region() + real_client = build_anthropic_bedrock_client(region) + final_model = _normalize_resolved_model(model or _read_main_model(), provider) + client = AnthropicAuxiliaryClient(real_client, final_model, "aws-sdk", f"https://bedrock-runtime.{region}.amazonaws.com") + logger.info("resolve_provider_client: bedrock (%s, region=%s)", final_model, region) + return (_to_async_client(client, final_model) if async_mode else (client, final_model)) + logger.warning("resolve_provider_client: unhandled auth_type %s for %s", pconfig.auth_type, provider) return None, None diff --git a/run_agent.py b/run_agent.py index 855b67a8471c..25d9a6061bd9 100644 --- a/run_agent.py +++ b/run_agent.py @@ -5220,6 +5220,26 @@ def _anthropic_messages_create(self, api_kwargs: dict): self._try_refresh_anthropic_client_credentials() return self._anthropic_client.messages.create(**api_kwargs) + def _rebuild_anthropic_client(self) -> None: + """Rebuild the Anthropic client after an interrupt or stale call. + + Handles both direct Anthropic and Bedrock-hosted Anthropic models + correctly — rebuilding with the Bedrock SDK when provider is bedrock, + rather than always falling back to build_anthropic_client() which + requires a direct Anthropic API key. + """ + if getattr(self, "provider", None) == "bedrock": + from agent.anthropic_adapter import build_anthropic_bedrock_client + region = getattr(self, "_bedrock_region", "us-east-1") or "us-east-1" + self._anthropic_client = build_anthropic_bedrock_client(region) + else: + from agent.anthropic_adapter import build_anthropic_client + self._anthropic_client = build_anthropic_client( + self._anthropic_api_key, + getattr(self, "_anthropic_base_url", None), + timeout=get_provider_request_timeout(self.provider, self.model), + ) + def _interruptible_api_call(self, api_kwargs: dict): """ Run the API call in a background thread so the main conversation loop @@ -5318,14 +5338,8 @@ def _call(): ) try: if self.api_mode == "anthropic_messages": - from agent.anthropic_adapter import build_anthropic_client - self._anthropic_client.close() - self._anthropic_client = build_anthropic_client( - self._anthropic_api_key, - getattr(self, "_anthropic_base_url", None), - timeout=get_provider_request_timeout(self.provider, self.model), - ) + self._rebuild_anthropic_client() else: rc = request_client_holder.get("client") if rc is not None: @@ -5350,14 +5364,8 @@ def _call(): # seed future retries. try: if self.api_mode == "anthropic_messages": - from agent.anthropic_adapter import build_anthropic_client - self._anthropic_client.close() - self._anthropic_client = build_anthropic_client( - self._anthropic_api_key, - getattr(self, "_anthropic_base_url", None), - timeout=get_provider_request_timeout(self.provider, self.model), - ) + self._rebuild_anthropic_client() else: request_client = request_client_holder.get("client") if request_client is not None: @@ -6198,14 +6206,8 @@ def _call(): if self._interrupt_requested: try: if self.api_mode == "anthropic_messages": - from agent.anthropic_adapter import build_anthropic_client - self._anthropic_client.close() - self._anthropic_client = build_anthropic_client( - self._anthropic_api_key, - getattr(self, "_anthropic_base_url", None), - timeout=get_provider_request_timeout(self.provider, self.model), - ) + self._rebuild_anthropic_client() else: request_client = request_client_holder.get("client") if request_client is not None: