398234748f
Seven sites hand-rolled `float(headers.get("Retry-After"))` (anon_auth,
shared_metrics_sender, gemini_native_adapter, extract_api_error_context,
nous_rate_guard, skills_hub_github, skills_hub_clawhub x2) and silently
dropped RFC 7231 HTTP-date values that the conversation loop already honours
via agent/retry_utils.py::parse_retry_after_seconds. They now call it; per-site
caps/floors stay at the call site.
The free-text "resets in / quotaResetDelay / retry after N s" regexes lived in
two tables (agent_runtime_helpers vs credential_pool) whose "resets in"
grammars diverged: the pool accepted only integer `Nhr Nmin` while the error
context accepted h/hr/hours + m/min/minutes + s/seconds with decimals. One table
(agent/retry_utils.py::RETRY_DELAY_PATTERNS / reset_delay_from_message) using
the wider grammar, so a pooled credential's cooldown and the UI's reset time
now agree.
47 lines
2.1 KiB
Python
47 lines
2.1 KiB
Python
"""Invariant: the LLM transport (``agent/process_bootstrap``) and the platform adapters
|
|
(``gateway/platforms/base``) answer "is this host in NO_PROXY" with the same matcher, so a
|
|
corporate ``NO_PROXY=10.0.0.0/8`` bypasses the proxy for a self-hosted ``10.x`` model endpoint
|
|
exactly as it does for Telegram/Discord/Slack.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from agent.process_bootstrap import _get_proxy_for_base_url
|
|
from agent.proxy_bypass import should_bypass_proxy
|
|
from gateway.platforms.base import is_host_excluded_by_no_proxy, resolve_proxy_url
|
|
|
|
_PROXY_KEYS = ("HTTPS_PROXY", "HTTP_PROXY", "ALL_PROXY", "https_proxy", "http_proxy", "all_proxy",
|
|
"NO_PROXY", "no_proxy")
|
|
|
|
|
|
@pytest.fixture
|
|
def proxy_env(monkeypatch):
|
|
for key in _PROXY_KEYS:
|
|
monkeypatch.delenv(key, raising=False)
|
|
monkeypatch.setenv("HTTPS_PROXY", "http://proxy.corp:3128")
|
|
monkeypatch.setattr("gateway.platforms.base.gateway_trust_env", lambda: True)
|
|
monkeypatch.setattr("gateway.platforms.base._detect_macos_system_proxy", lambda: None)
|
|
return monkeypatch
|
|
|
|
|
|
@pytest.mark.parametrize("no_proxy, host", [
|
|
("10.0.0.0/8", "10.1.2.3"),
|
|
("*.internal", "svc.internal"),
|
|
("localhost,.corp.example", "llm.corp.example"),
|
|
("api.example.com:8443", "api.example.com:8443"),
|
|
])
|
|
def test_llm_and_adapter_paths_bypass_the_same_entries(proxy_env, no_proxy, host):
|
|
proxy_env.setenv("NO_PROXY", no_proxy)
|
|
assert should_bypass_proxy(host)
|
|
assert _get_proxy_for_base_url(f"https://{host}/v1") is None
|
|
assert resolve_proxy_url(target_hosts=host) is None
|
|
assert is_host_excluded_by_no_proxy(host.split(":")[0]) or ":" in host # Slack passes bare hosts
|
|
|
|
|
|
def test_non_matching_host_keeps_the_proxy_on_both_paths(proxy_env):
|
|
proxy_env.setenv("NO_PROXY", "10.0.0.0/8,*.internal")
|
|
assert _get_proxy_for_base_url("https://api.openai.com/v1") == "http://proxy.corp:3128"
|
|
assert resolve_proxy_url(target_hosts="api.telegram.org") == "http://proxy.corp:3128"
|
|
assert not is_host_excluded_by_no_proxy("slack.com")
|
|
assert is_host_excluded_by_no_proxy("files.slack.com", "slack.com") # explicit value wins
|