Add app credentials, account-scoped idempotency and per-app terminal notifications. Consolidate the unreleased migration baseline, preserve rule descriptions as AI context, and regenerate provider contracts with regression coverage.
132 lines
6.0 KiB
Python
132 lines
6.0 KiB
Python
from datetime import datetime
|
|
from typing import Any
|
|
from uuid import uuid4
|
|
|
|
from sqlalchemy import (
|
|
JSON,
|
|
Boolean,
|
|
DateTime,
|
|
Float,
|
|
ForeignKey,
|
|
Index,
|
|
Integer,
|
|
String,
|
|
UniqueConstraint,
|
|
false,
|
|
func,
|
|
)
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from ai_platform.infrastructure.persistence.database import Base
|
|
|
|
|
|
class AppCredential(Base):
|
|
__tablename__ = "app_credentials"
|
|
app_id: Mapped[str] = mapped_column(String(128), primary_key=True)
|
|
verifier: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
|
|
active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
lead_scoring: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False
|
|
)
|
|
|
|
|
|
class AppCredentialAuditEvent(Base):
|
|
__tablename__ = "app_credential_audit_events"
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
app_id: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
actor: Mapped[str] = mapped_column(String(254), nullable=False)
|
|
action: Mapped[str] = mapped_column(String(16), nullable=False)
|
|
occurred_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), nullable=False
|
|
)
|
|
|
|
|
|
class AdminUser(Base):
|
|
__tablename__ = "admin_users"
|
|
email: Mapped[str] = mapped_column(String(254), primary_key=True)
|
|
password_hash: Mapped[str] = mapped_column(String(512), nullable=False)
|
|
|
|
|
|
class AdminSession(Base):
|
|
__tablename__ = "admin_sessions"
|
|
id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
|
admin_email: Mapped[str] = mapped_column(ForeignKey("admin_users.email"), nullable=False)
|
|
csrf_hash: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
|
|
|
|
class AIProfile(Base):
|
|
__tablename__ = "ai_profiles"
|
|
version: Mapped[str] = mapped_column(String(64), primary_key=True)
|
|
model: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
provider: Mapped[str] = mapped_column(
|
|
String(32), default="aihub", server_default="aihub", nullable=False
|
|
)
|
|
base_url: Mapped[str | None] = mapped_column(String(2048))
|
|
system_prompt: Mapped[str] = mapped_column(String(8000), nullable=False)
|
|
timeout_seconds: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
limits: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
|
|
active: Mapped[bool] = mapped_column(
|
|
Boolean, default=False, server_default=false(), nullable=False
|
|
)
|
|
|
|
|
|
class AIProfileAuditEvent(Base):
|
|
__tablename__ = "ai_profile_audit_events"
|
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
|
actor: Mapped[str] = mapped_column(String(254), nullable=False)
|
|
version: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
provider: Mapped[str] = mapped_column(String(32), nullable=False)
|
|
occurred_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), nullable=False
|
|
)
|
|
|
|
|
|
class ScoringRun(Base):
|
|
__tablename__ = "scoring_runs"
|
|
__table_args__ = (
|
|
UniqueConstraint("app_id", "account_id", "request_id", name="uq_run_app_account_request"),
|
|
)
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid4()))
|
|
app_id: Mapped[str] = mapped_column(ForeignKey("app_credentials.app_id"), index=True)
|
|
account_id: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
request_id: Mapped[str] = mapped_column(String(36), nullable=False)
|
|
fingerprint: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
request: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False)
|
|
profile_version: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
status: Mapped[str] = mapped_column(String(24), default="pending", index=True)
|
|
rule_score: Mapped[int | None] = mapped_column(Integer)
|
|
ai_score: Mapped[int | None] = mapped_column(Integer)
|
|
ai_confidence: Mapped[float | None] = mapped_column(Float)
|
|
ai_reasoning: Mapped[str | None] = mapped_column(String(2_000))
|
|
error_code: Mapped[str | None] = mapped_column(String(64))
|
|
final_score: Mapped[int | None] = mapped_column(Integer)
|
|
degraded: Mapped[bool] = mapped_column(Boolean, default=False, server_default=false())
|
|
result_mode: Mapped[str | None] = mapped_column(String(32))
|
|
breakdown: Mapped[list[dict[str, Any]] | None] = mapped_column(JSON)
|
|
ai_evidence: Mapped[list[dict[str, Any]] | None] = mapped_column(JSON)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), index=True
|
|
)
|
|
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
|
|
|
|
class ScoringNotification(Base):
|
|
__tablename__ = "scoring_notifications"
|
|
__table_args__ = (Index("ix_scoring_notifications_due", "dead", "next_attempt_at"),)
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid4()))
|
|
run_id: Mapped[str] = mapped_column(
|
|
ForeignKey("scoring_runs.id", ondelete="CASCADE"), unique=True, nullable=False
|
|
)
|
|
app_id: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
account_id: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
request_id: Mapped[str] = mapped_column(String(36), nullable=False)
|
|
status: Mapped[str] = mapped_column(String(24), nullable=False)
|
|
attempts: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
|
next_attempt_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
delivered_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
dead: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|