feat(cerbero): HTTP client with bearer + bot-tag + retry
Client minimale verso Cerbero MCP unified server con header Authorization Bearer e X-Bot-Tag su ogni richiesta. Retry esponenziale solo su ConnectionError (non su 4xx). 4xx/5xx -> RuntimeError. Switch da httpx a requests per allineamento con libreria mock `responses` (httpx richiederebbe respx). httpx resta in deps per usi futuri. Aggiunto types-requests ai dev deps per mypy strict. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from types import TracebackType
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_exponential
|
||||
|
||||
|
||||
class CerberoClient:
|
||||
"""Client HTTP minimale verso Cerbero MCP unified server."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
base_url: str,
|
||||
token: str,
|
||||
bot_tag: str,
|
||||
timeout_seconds: float = 10.0,
|
||||
) -> None:
|
||||
self.base_url = base_url.rstrip("/")
|
||||
self.token = token
|
||||
self.bot_tag = bot_tag
|
||||
self.timeout_seconds = timeout_seconds
|
||||
self._session = requests.Session()
|
||||
self._session.headers.update(
|
||||
{
|
||||
"Authorization": f"Bearer {token}",
|
||||
"X-Bot-Tag": bot_tag,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
)
|
||||
|
||||
def close(self) -> None:
|
||||
self._session.close()
|
||||
|
||||
def __enter__(self) -> CerberoClient:
|
||||
return self
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> None:
|
||||
self.close()
|
||||
|
||||
@retry(
|
||||
stop=stop_after_attempt(3),
|
||||
wait=wait_exponential(multiplier=0.5, min=0.5, max=4.0),
|
||||
retry=retry_if_exception_type(requests.ConnectionError),
|
||||
reraise=True,
|
||||
)
|
||||
def call_tool(self, exchange: str, tool: str, args: dict[str, Any]) -> Any:
|
||||
url = f"{self.base_url}/mcp-{exchange}/tools/{tool}"
|
||||
resp = self._session.post(url, json=args, timeout=self.timeout_seconds)
|
||||
if resp.status_code >= 400:
|
||||
raise RuntimeError(
|
||||
f"Cerbero {exchange}/{tool} returned {resp.status_code}: {resp.text}"
|
||||
)
|
||||
return resp.json()
|
||||
Reference in New Issue
Block a user