feat(HRT-200): AI Router — Multi-provider LLM routing with failover

- 4 provider adapters: OpenAI (SDK), Anthropic (SDK), Google (google-genai), Mistral (direct HTTP)
- Core router with automatic failover + exponential backoff
- Flask blueprint with /api/v1/ai/* endpoints
- Auth via token-broker verify endpoint
- DB models for ai_providers, ai_model_mapping, ai_router_log
- /health endpoint (parallel provider check), /usage stats
- 21 unit tests (all passing)
This commit is contained in:
CTO H3R7Tech
2026-05-24 10:21:36 +02:00
parent 837cddb406
commit 4b766cb908
13 changed files with 1234 additions and 0 deletions

77
ai_router_api.py Normal file
View File

@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""
AI Router API — Multi-provider LLM routing with failover
Port: 8783 | DB: SQLite ai_router.db
HRT-200 — AI Router (Multi-provider + failover)
Endpoints:
GET /api/v1/ai/health — Check all providers health
GET /api/v1/ai/models — List available models
POST /api/v1/ai/chat — Chat completion with auto-failover
GET /api/v1/ai/admin/providers — List configured providers
POST /api/v1/ai/admin/providers — Upsert a provider
POST /api/v1/ai/admin/model-mappings— Upsert a model mapping
DELETE /api/v1/ai/admin/providers/:id — Remove a provider
GET /api/v1/ai/usage — Usage logs
GET /api/v1/ai/usage/summary — Aggregated usage stats
"""
import logging
import logging.handlers
import os
import sys
from flask import Flask, jsonify
from flask_cors import CORS
LOG_DIR = os.path.join(os.path.dirname(__file__), "ai_router", "logs")
os.makedirs(LOG_DIR, exist_ok=True)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] ai-router: %(name)s: %(message)s",
handlers=[
logging.StreamHandler(sys.stdout),
logging.handlers.RotatingFileHandler(
os.path.join(LOG_DIR, "ai_router.log"),
maxBytes=5 * 1024 * 1024,
backupCount=3,
),
],
)
logger = logging.getLogger("ai_router")
PORT = int(os.environ.get("AI_ROUTER_PORT", "8783"))
def create_app():
app = Flask(__name__)
CORS(app)
from ai_router.api import register_ai_router
from ai_router.models import init_db
init_db()
register_ai_router(app)
@app.errorhandler(404)
def not_found(e):
return jsonify({"error": "not_found", "message": "Route not found"}), 404
@app.errorhandler(500)
def internal_error(e):
logger.error(f"Internal error: {e}")
return jsonify({"error": "internal_error", "message": "Internal server error"}), 500
return app
if __name__ == "__main__":
logger.info("=" * 60)
logger.info("AI Router API starting...")
logger.info(f"Port: {PORT}")
logger.info("=" * 60)
app = create_app()
debug = os.environ.get("FLASK_ENV", "production") == "development"
app.run(host="0.0.0.0", port=PORT, debug=debug)