root 8348520bdf feat: implement LLM Gateway with multi-provider support
Implement a unified LLM Gateway supporting multiple API formats and providers:

Features:
- OpenAI Chat Completions, Responses API, and Anthropic Messages API
- Provider adapters for OpenAI, Anthropic, Azure OpenAI, Google Gemini, AWS Bedrock
- Model aliasing with weighted round-robin load balancing
- Virtual API keys with RPM/TPM rate limiting
- Budget control at key and project levels
- Request logging, usage statistics, and audit logs
- Fallback/retry with circuit breaker pattern
- Admin CRUD APIs for providers, projects, keys, models, usage
- Provider health checks

Tech stack:
- FastAPI with async SQLAlchemy 2.0
- SQLite with aiosqlite
- bcrypt for API key hashing, AES-256 for provider key encryption
- Docker containerization

Tests: 18 passing integration tests for admin API endpoints

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 15:39:21 +08:00

49 lines
1.9 KiB
Python

"""API Key model."""
from datetime import datetime
from decimal import Decimal
from uuid import uuid4
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, Numeric, String, Text
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.db.database import Base
class APIKey(Base):
"""Virtual API Key for authentication and usage tracking."""
__tablename__ = "api_keys"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid4()))
key_hash: Mapped[str] = mapped_column(String(128), unique=True, nullable=False, index=True)
key_prefix: Mapped[str] = mapped_column(String(20), nullable=False) # e.g., "sk-proj_abc..."
name: Mapped[str] = mapped_column(String(100), nullable=False)
# Project relationship
project_id: Mapped[str | None] = mapped_column(String(36), ForeignKey("projects.id"))
project: Mapped["Project"] = relationship("Project", backref="api_keys")
enabled: Mapped[bool] = mapped_column(Boolean, default=True)
expires_at: Mapped[datetime | None] = mapped_column(DateTime)
# Rate Limits
rpm_limit: Mapped[int | None] = mapped_column(Integer)
tpm_limit: Mapped[int | None] = mapped_column(Integer)
# Budget
budget_limit: Mapped[Decimal | None] = mapped_column(Numeric(10, 2))
budget_period: Mapped[str | None] = mapped_column(String(20)) # daily, weekly, monthly
# Permissions
allowed_models: Mapped[str | None] = mapped_column(Text) # JSON array
# Usage Stats
current_usage: Mapped[Decimal] = mapped_column(Numeric(10, 2), default=Decimal("0"))
total_requests: Mapped[int] = mapped_column(Integer, default=0)
# Timestamps
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
)