Initial skills documentation — 25 categories, all SKILL.md + references + scripts

This commit is contained in:
root
2026-07-15 17:42:20 -04:00
commit 1b4fcd4427
976 changed files with 188344 additions and 0 deletions
@@ -0,0 +1,197 @@
# Super Search — Rate Limiting & Caching Reference
Full implementation of `ratelimit.py` used in the Super Search MCP server (`/root/docker/super-search/ratelimit.py`).
## Architecture
```
┌─ Cache HIT? ──→ return cached result
Caller ──→ rate_limited_search(provider)
├─ Cache MISS ──→ acquire rate-limit token (TokenBucket)
│ │
│ ┌──────┘
│ ├─ 200 OK ──→ cache.set(TTL) ──→ return
│ ├─ 429/5xx ──→ _retry_with_backoff (max 3)
│ └─ permanent failure ──→ raise
```
## TokenBucket
Async-aware token bucket with `acquire()` that blocks until a token is available:
```python
class TokenBucket:
def __init__(self, tokens: float, interval: float) -> None:
self._capacity = float(tokens)
self._tokens = float(tokens)
self._interval = float(interval)
self._refill_rate = self._capacity / self._interval
self._last_refill = time.monotonic()
self._lock = asyncio.Lock()
def _refill(self) -> None:
now = time.monotonic()
elapsed = now - self._last_refill
self._tokens = min(self._capacity, self._tokens + elapsed * self._refill_rate)
self._last_refill = now
async def acquire(self) -> None:
async with self._lock:
self._refill()
if self._tokens >= 1.0:
self._tokens -= 1.0
return
wait = (1.0 - self._tokens) / self._refill_rate
await asyncio.sleep(wait)
async with self._lock:
self._refill()
self._tokens -= 1.0
```
## Provider Rate Limits
Configured per provider based on API tier and pricing:
| Provider | Tokens | Interval | Effective Rate | Rationale |
|---------------|--------|----------|---------------|-------------------------------|
| searxng | 30 | 1.0s | 30/sec | Local instance, virtually free |
| exa | 10 | 1.0s | 10/sec | Paid tier, moderate allowance |
| firecrawl | 5 | 60.0s | ~0.08/sec | 1k/month free tier, conserve |
| opencorporates| 1 | 1.0s | ~1/sec | Free tier, no API key |
| courtlistener | 10 | 6.0s | ~1.67/sec | Free tier, ~100/min max |
## TTLCache
Dict-based with async lock and per-key expiration:
```python
class TTLCache:
def __init__(self) -> None:
self._cache: dict[str, tuple[Any, float]] = {} # key → (value, expires_at)
self._lock = asyncio.Lock()
async def get(self, key: str) -> Optional[Any]:
async with self._lock:
entry = self._cache.get(key)
if entry is None:
return None
value, expires_at = entry
if time.monotonic() >= expires_at:
del self._cache[key]
return None
return value
async def set(self, key: str, value: Any, ttl: int) -> None:
async with self._lock:
self._cache[key] = (value, time.monotonic() + ttl)
```
## Cache TTLs
| Provider | TTL | Rationale |
|---------------|--------|--------------------------------------------------|
| searxng | 120s | Web results change frequently |
| exa | 300s | Neural search — moderate freshness |
| opencorporates| 3600s | Company records don't change often |
| courtlistener | 3600s | Court opinions are infrequent additions |
Cache key format: `{provider}:{query.strip().lower()}:{limit}`
## Retry with Backoff
Handles transient failures (429, 503, 502, 504) with exponential backoff:
```python
RETRYABLE_STATUSES: set[int] = {429, 503, 502, 504}
async def _retry_with_backoff(
coro_factory: Callable[[], Any],
provider: str,
max_retries: int = 3,
base_delay: float = 1.0,
) -> Any:
for attempt in range(max_retries + 1):
try:
result = await coro_factory()
return result
except httpx.HTTPStatusError as e:
status = e.response.status_code
if status not in RETRYABLE_STATUSES or attempt == max_retries:
raise
retry_after = e.response.headers.get("Retry-After", "")
if retry_after and retry_after.isdigit():
delay = float(retry_after)
else:
delay = base_delay * (2 ** attempt)
logger.warning(
"Provider %s returned %d (attempt %d/%d). Retrying in %.1fs…",
provider, status, attempt + 1, max_retries, delay,
)
await asyncio.sleep(delay)
```
Delays: 1s → 2s → 4s → 8s. Respects `Retry-After` header when present.
## rate_limited_search Decorator
Composite decorator: cache-check → rate-limit → retry → cache-store:
```python
def rate_limited_search(provider: str):
def decorator(func):
@wraps(func)
async def wrapper(query: str, limit: int = 5, **kwargs):
# 1. Check cache
key = cache_key(provider, query, limit)
cached = await _cache.get(key)
if cached is not None:
return cached
# 2. Acquire rate-limit token
bucket = get_bucket(provider)
await bucket.acquire()
# 3. Execute with retry
ttl = CACHE_TTL.get(provider, 300)
async def _call():
return await func(query, limit=limit, **kwargs)
result = await _retry_with_backoff(_call, provider)
# 4. Cache result (only on success)
if result:
await _cache.set(key, result, ttl)
return result
return wrapper
return decorator
```
## Usage in server.py
```python
from ratelimit import rate_limited_search
@rate_limited_search("opencorporates")
async def _opencorporates_search(query: str, limit: int = 5) -> list[dict]:
"""Search company records via OpenCorporates API."""
# ... implementation — rate limiting + caching handled by decorator
@rate_limited_search("courtlistener")
async def _courtlistener_search(query: str, limit: int = 5) -> list[dict]:
"""Search US court opinions via CourtListener API."""
# ... implementation
```
## Dependencies
- `asyncio` (stdlib)
- `time` (stdlib)
- `logging` (stdlib)
- `functools.wraps` (stdlib)
- `httpx` (for HTTPStatusError type in retry handler only)
No external dependencies required for the rate-limiting module itself.