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:
77
ai_router_api.py
Normal file
77
ai_router_api.py
Normal 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)
|
||||
Reference in New Issue
Block a user