intelligence-router/sidecar/manifest.py
root c491779248 Epic: Model Switching via Sidecar — Issues #2-#3
Issue #2: Manifest schema + Sidecar foundation
- sidecar/manifest.py: YAML manifest loading and profile validation
- sidecar/app.py: FastAPI sidecar service with /models/available, /models/status endpoints
- Router GET /v1/models: proxies to sidecar, returns OpenAI-compatible model list
- Tests: 12 manifest tests, 6 sidecar endpoint tests, 3 router tests (21 total)

Issue #3: Sidecar model switch + Router request queue
- Sidecar POST /models/switch: stops current llama-server, starts new one, polls for readiness
- Switch lock prevents concurrent switches (threading.Lock for TestClient compatibility)
- Router request queue: max 10 requests, 120s hard timeout, 429 when full
- Router automatic model detection: extracts model from chat body, matches against sidecar status
- Full proxy endpoint with Sidecar → Main PC routing and fallback chain
- Tests: 5 sidecar switch tests, 4 queue tests, 3 router integration tests (12 total)

Total: 33 tests, all passing
2026-06-15 00:49:24 +00:00

58 lines
1.4 KiB
Python

"""Manifest loading and validation — Issue #2."""
import yaml
from typing import Optional
def validate_profile(profile: dict) -> dict:
"""Validate and normalize a single manifest profile entry.
Required fields: id, name, model_path.
Optional field: flags (defaults to {}).
"""
for field in ("id", "name", "model_path"):
if field not in profile:
raise ValueError(f"Missing required field: {field}")
return {
"id": profile["id"],
"name": profile["name"],
"model_path": profile["model_path"],
"flags": profile.get("flags", {}),
}
def load_manifest(path: str) -> Optional[list]:
"""Load and validate profiles from a YAML manifest file.
Returns a list of validated profile dicts, or None on any error.
"""
try:
with open(path, "r") as f:
content = f.read()
except (FileNotFoundError, OSError):
return None
if not content.strip():
return []
try:
data = yaml.safe_load(content)
except yaml.YAMLError:
return None
if data is None or data == []:
return []
if not isinstance(data, list):
return None
profiles = []
for item in data:
try:
profiles.append(validate_profile(item))
except ValueError:
# Skip invalid profiles rather than failing the whole manifest
continue
return profiles