Copies source (no node_modules, dist, .tasks, .wiki, __pycache__) for: - projects-meta-mcp v2.25.0 (TypeScript/Node) - wiki-graph v0.3.1 (TypeScript/Node) - interns-mcp v0.3.3 (Python/FastMCP) .gitignore: exclude lib build artefacts (node_modules, dist, .venv, __pycache__, *.pyc) bootstrap.ps1: add MCP build step — npm install+build for TS servers, venv+pip for Python Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
43 lines
1.5 KiB
Python
43 lines
1.5 KiB
Python
"""OpenAI-compatible HTTP client, persistent per-endpoint."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from openai import OpenAI
|
|
|
|
# Bound every LLM call. Without this the SDK default (600s) lets a stalled
|
|
# endpoint hang the worker thread that ran the tool; enough such threads exhaust
|
|
# FastMCP's pool and wedge the whole stdio server. 90s is well above a healthy
|
|
# deepseek-v4-flash response yet short enough to fail fast and free the thread.
|
|
DEFAULT_REQUEST_TIMEOUT = 90.0
|
|
|
|
|
|
def _resolve_timeout(endpoint_cfg: dict[str, Any]) -> float:
|
|
"""Per-endpoint request timeout in seconds; falls back to the bounded default."""
|
|
return float(endpoint_cfg.get("request_timeout", DEFAULT_REQUEST_TIMEOUT))
|
|
|
|
|
|
def make_client(endpoint_cfg: dict[str, Any], api_key: str) -> OpenAI:
|
|
"""Create a persistent OpenAI client for an endpoint."""
|
|
kwargs: dict[str, Any] = {
|
|
"base_url": endpoint_cfg["base_url"],
|
|
"api_key": api_key,
|
|
"timeout": _resolve_timeout(endpoint_cfg),
|
|
}
|
|
defaults = endpoint_cfg.get("request_defaults", {})
|
|
if "extra_body" in defaults:
|
|
kwargs["default_query"] = {}
|
|
return OpenAI(**kwargs)
|
|
|
|
|
|
class ClientPool:
|
|
"""Manages persistent HTTP clients per endpoint."""
|
|
|
|
def __init__(self) -> None:
|
|
self._clients: dict[str, OpenAI] = {}
|
|
|
|
def get(self, name: str, endpoint_cfg: dict[str, Any], api_key: str) -> OpenAI:
|
|
if name not in self._clients:
|
|
self._clients[name] = make_client(endpoint_cfg, api_key)
|
|
return self._clients[name] |