- 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)
78 lines
2.3 KiB
Python
78 lines
2.3 KiB
Python
#!/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)
|