from __future__ import annotations import uuid from datetime import datetime from typing import Any from sqlalchemy import DateTime, Float, ForeignKey, Integer, String, Text, func from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.types import JSON from app.db.base_class import Base class AgentRun(Base): __tablename__ = "agent_runs" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) run_id: Mapped[str] = mapped_column(String(50), unique=True, index=True) agent: Mapped[str] = mapped_column(String(30), index=True) source: Mapped[str] = mapped_column(String(30)) user_id: Mapped[str | None] = mapped_column(String(100), nullable=True) task_id: Mapped[str | None] = mapped_column( ForeignKey("agent_assets.id"), nullable=True, index=True ) ontology_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict) route_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict) permission_level: Mapped[str] = mapped_column(String(30), default="read") status: Mapped[str] = mapped_column(String(20), index=True) result_summary: Mapped[str | None] = mapped_column(Text(), nullable=True) error_message: Mapped[str | None] = mapped_column(Text(), nullable=True) started_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), index=True ) finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) task_asset = relationship("AgentAsset", back_populates="scheduled_runs") tool_calls = relationship( "AgentToolCall", back_populates="run", cascade="all, delete-orphan", order_by="asc(AgentToolCall.created_at)", ) semantic_parse_logs = relationship( "SemanticParseLog", back_populates="run", cascade="all, delete-orphan", order_by="asc(SemanticParseLog.created_at)", ) class AgentToolCall(Base): __tablename__ = "agent_tool_calls" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) run_id: Mapped[str] = mapped_column(ForeignKey("agent_runs.run_id"), index=True) tool_type: Mapped[str] = mapped_column(String(30)) tool_name: Mapped[str] = mapped_column(String(100)) request_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict) response_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict) status: Mapped[str] = mapped_column(String(20)) duration_ms: Mapped[int] = mapped_column(Integer, default=0) error_message: Mapped[str | None] = mapped_column(Text(), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) run = relationship("AgentRun", back_populates="tool_calls") class SemanticParseLog(Base): __tablename__ = "semantic_parse_logs" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) run_id: Mapped[str] = mapped_column(ForeignKey("agent_runs.run_id"), index=True) user_id: Mapped[str | None] = mapped_column(String(100), nullable=True) raw_query: Mapped[str] = mapped_column(Text()) scenario: Mapped[str] = mapped_column(String(50), index=True) intent: Mapped[str] = mapped_column(String(50), index=True) entities_json: Mapped[list[Any]] = mapped_column(JSON, default=list) time_range_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict) metrics_json: Mapped[list[Any]] = mapped_column(JSON, default=list) constraints_json: Mapped[list[Any]] = mapped_column(JSON, default=list) risk_flags_json: Mapped[list[Any]] = mapped_column(JSON, default=list) permission_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict) confidence: Mapped[float] = mapped_column(Float, default=0.0) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) run = relationship("AgentRun", back_populates="semantic_parse_logs")