mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-07 18:27:45 +02:00
[eric] web: subscription-OAuth WebSearch 401s on token rotation, so keep the reliable DuckDuckGo fallback for those users
This commit is contained in:
@@ -1213,18 +1213,22 @@ class AgentManager:
|
||||
getattr(global_settings, "connection_mode", "own_key") == "openswarm-pro"
|
||||
or bool(getattr(global_settings, "anthropic_api_key", None))
|
||||
)
|
||||
# Both 9Router provider ids `claude` (subscription OAuth) and
|
||||
# `anthropic` (direct API / Pro proxy) satisfy this check.
|
||||
_9r_has_anthropic = False
|
||||
# Collect the active 9Router anthropic-family provider ids so the
|
||||
# web-search reliability check (below) can distinguish a STABLE
|
||||
# credential (direct `anthropic`) from subscription OAuth
|
||||
# (`claude`/`claude-code`), whose hosted-WebSearch delegation 401s on
|
||||
# token rotation. Subscription OAuth must NOT suppress the DDG fallback.
|
||||
_9r_provider_ids: list[str] = []
|
||||
try:
|
||||
from backend.apps.nine_router import get_providers as _9r_providers
|
||||
_conns = await _9r_providers()
|
||||
_9r_has_anthropic = any(
|
||||
isinstance(c, dict)
|
||||
_9r_provider_ids = [
|
||||
c.get("provider")
|
||||
for c in _conns
|
||||
if isinstance(c, dict)
|
||||
and c.get("provider") in ("claude", "claude-code", "anthropic")
|
||||
and c.get("isActive")
|
||||
for c in _conns
|
||||
)
|
||||
]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -1258,12 +1262,14 @@ class AgentManager:
|
||||
# Post-fix: non-Claude primaries always register openswarm-web,
|
||||
# which cascades Gemini-native → OpenAI-native → subscriptions
|
||||
# → DDG, only falling to Anthropic if everything else missing.
|
||||
from backend.apps.agents.tools.web import anthropic_web_search_is_reliable
|
||||
_has_anthropic_path = (
|
||||
not _is_custom_session
|
||||
and _primary_is_claude
|
||||
and (
|
||||
bool(getattr(global_settings, "anthropic_api_key", None))
|
||||
or _9r_has_anthropic
|
||||
and anthropic_web_search_is_reliable(
|
||||
has_direct_anthropic_key=bool(getattr(global_settings, "anthropic_api_key", None)),
|
||||
is_pro=(getattr(global_settings, "connection_mode", "own_key") == "openswarm-pro"),
|
||||
provider_ids=_9r_provider_ids,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -18,6 +18,24 @@ _USER_AGENT = (
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
)
|
||||
|
||||
# 9router provider ids that authenticate with a STABLE token. Subscription OAuth
|
||||
# ('claude'/'claude-code') is deliberately excluded: the CLI's built-in WebSearch
|
||||
# delegates to Haiku via the subscription's rotating OAuth token, which 401s
|
||||
# intermittently ("Invalid bearer token, reset after 2m"). Only stable creds are
|
||||
# reliable enough to suppress our free DuckDuckGo fallback.
|
||||
_STABLE_ANTHROPIC_PROVIDERS = ("anthropic",)
|
||||
|
||||
|
||||
def anthropic_web_search_is_reliable(*, has_direct_anthropic_key: bool,
|
||||
is_pro: bool, provider_ids) -> bool:
|
||||
"""Whether the Anthropic-hosted WebSearch path is reliable enough to suppress
|
||||
the DuckDuckGo fallback. A subscription-OAuth-only user is NOT reliable (its
|
||||
web-search delegation 401s on token rotation), so those users keep the free,
|
||||
always-working DDG path instead of a flaky hosted one."""
|
||||
if has_direct_anthropic_key or is_pro:
|
||||
return True
|
||||
return any(p in _STABLE_ANTHROPIC_PROVIDERS for p in (provider_ids or []))
|
||||
|
||||
|
||||
def _truncate(text: str, limit: int = _MAX_OUTPUT_BYTES) -> str:
|
||||
if len(text) > limit:
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"""WebSearch path reliability: subscription OAuth must fall back to DuckDuckGo.
|
||||
|
||||
The 401 'Invalid bearer token (reset after 2m)' the user hit comes from the CLI's
|
||||
built-in WebSearch delegating to Haiku via a subscription's rotating OAuth token.
|
||||
So a subscription-OAuth-only user must NOT be treated as having a reliable hosted
|
||||
search path, they keep the free, always-working DDG fallback instead.
|
||||
"""
|
||||
|
||||
from backend.apps.agents.tools.web import anthropic_web_search_is_reliable as ok
|
||||
|
||||
|
||||
def test_direct_api_key_is_reliable():
|
||||
assert ok(has_direct_anthropic_key=True, is_pro=False, provider_ids=[]) is True
|
||||
|
||||
|
||||
def test_pro_is_reliable():
|
||||
assert ok(has_direct_anthropic_key=False, is_pro=True, provider_ids=[]) is True
|
||||
|
||||
|
||||
def test_direct_anthropic_9router_provider_is_reliable():
|
||||
assert ok(has_direct_anthropic_key=False, is_pro=False, provider_ids=["anthropic"]) is True
|
||||
|
||||
|
||||
def test_subscription_oauth_only_is_NOT_reliable():
|
||||
# the bug: this used to count as reliable -> suppressed DDG -> 401s on rotation
|
||||
assert ok(has_direct_anthropic_key=False, is_pro=False, provider_ids=["claude"]) is False
|
||||
assert ok(has_direct_anthropic_key=False, is_pro=False, provider_ids=["claude-code"]) is False
|
||||
assert ok(has_direct_anthropic_key=False, is_pro=False, provider_ids=["claude", "claude-code"]) is False
|
||||
|
||||
|
||||
def test_nothing_is_not_reliable():
|
||||
assert ok(has_direct_anthropic_key=False, is_pro=False, provider_ids=[]) is False
|
||||
assert ok(has_direct_anthropic_key=False, is_pro=False, provider_ids=None) is False
|
||||
|
||||
|
||||
def test_mixed_subscription_plus_direct_is_reliable():
|
||||
# if the user ALSO has a stable direct anthropic connection, hosted search is fine
|
||||
assert ok(has_direct_anthropic_key=False, is_pro=False,
|
||||
provider_ids=["claude", "anthropic"]) is True
|
||||
Reference in New Issue
Block a user