Files
smart-org/apps/api/app/infrastructure/db/models.py
T
mjalmoudy94 da5a2a5d1a agy تنها ارائه‌دهندهٔ مدل؛ حذف بخش ارائه‌دهنده‌ها و افزودن زنجیرهٔ انتخاب مدل
- حذف کامل providerهای mock و openai_compatible: دامنه، آداپوترها، رجیستری، کانفیگ، seed و رابطٔ مدیریت
- بخش «ارائه‌دهنده‌ها» از کنسول حذف شد؛ ورود اکانت‌ها فقط از صفحهٔ اکانت‌های agy
- صفحهٔ مدل‌ها بازطراحی شد: ارائه‌دهنده → اکانت → مدل (کاتالوگ لحظه‌ای و زندهٔ همان اکانت از Google) → افورت استدلال؛ بدون هیچ ورود دستی نام مدل
- مدل‌ها ستون reasoning_effort گرفتند؛ آداپتور آن را به thinkingConfig (بودجهٔ ۱۰۲۴ تا ۳۲۷۶۸) ترجمه می‌کند و فقط به مدل‌های استدلالی می‌فرستد
- مهاجرت c2d3e4f5b6a7: افزودن reasoning_effort و حذف ردیف‌های providerهای قدیمی
- تست‌ها: ۲۴۶ تست بک‌اند و ۵۱ تست فرانت سبز؛ ruff و mypy و tsc و eslint تمیز
2026-10-13 10:01:11 +03:30

912 lines
40 KiB
Python

"""SQLAlchemy ORM tables.
This is the *only* module that knows about the relational schema. The domain
layer is expressed as plain dataclasses in ``app.domain.*``; repositories map
between the two. Keeping the schema here means a migration change never forces
a change to domain logic.
"""
from __future__ import annotations
from datetime import datetime
from sqlalchemy import (
JSON,
Boolean,
DateTime,
Float,
ForeignKey,
Index,
Integer,
String,
Text,
UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column
from app.core.database import Base
from app.domain.base import new_id, utcnow
def _pk() -> Mapped[str]:
return mapped_column(String(32), primary_key=True, default=new_id)
def _created() -> Mapped[datetime]:
return mapped_column(DateTime(timezone=True), default=utcnow, nullable=False)
# ---------------------------------------------------------------------------
# Identity & Access
# ---------------------------------------------------------------------------
class UserRow(Base):
__tablename__ = "users"
id: Mapped[str] = _pk()
email: Mapped[str] = mapped_column(String(320), unique=True, index=True, nullable=False)
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
name: Mapped[str] = mapped_column(String(160), default="", nullable=False)
avatar_url: Mapped[str | None] = mapped_column(String(512), nullable=True)
is_platform_owner: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = _created()
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utcnow, onupdate=utcnow, nullable=False
)
class OrganizationRow(Base):
__tablename__ = "organizations"
id: Mapped[str] = _pk()
name: Mapped[str] = mapped_column(String(160), nullable=False)
slug: Mapped[str] = mapped_column(String(64), unique=True, index=True, nullable=False)
owner_user_id: Mapped[str] = mapped_column(ForeignKey("users.id"), nullable=False, index=True)
created_at: Mapped[datetime] = _created()
class MembershipRow(Base):
__tablename__ = "memberships"
__table_args__ = (
UniqueConstraint("organization_id", "user_id", name="uq_membership_org_user"),
Index("ix_membership_org_role", "organization_id", "role"),
)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
user_id: Mapped[str] = mapped_column(
ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
)
role: Mapped[str] = mapped_column(String(16), default="member", nullable=False)
status: Mapped[str] = mapped_column(String(16), default="active", nullable=False)
created_at: Mapped[datetime] = _created()
class InviteRow(Base):
__tablename__ = "invites"
__table_args__ = (Index("ix_invite_org_email", "organization_id", "email"),)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
email: Mapped[str] = mapped_column(String(320), nullable=False)
role: Mapped[str] = mapped_column(String(16), default="member", nullable=False)
token_hash: Mapped[str] = mapped_column(String(128), unique=True, nullable=False)
invited_by_user_id: Mapped[str | None] = mapped_column(
ForeignKey("users.id"), nullable=True, index=True
)
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
accepted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
created_at: Mapped[datetime] = _created()
# ---------------------------------------------------------------------------
# AI Control Plane (platform-owner only)
# ---------------------------------------------------------------------------
class AiProviderRow(Base):
__tablename__ = "ai_providers"
id: Mapped[str] = _pk()
name: Mapped[str] = mapped_column(String(120), unique=True, nullable=False)
#: agy is the only provider type; the column stays a string so old rows
#: (mock / openai_compatible, deleted by migration c2d3e4f5b6a7) cannot
#: break the enum mapping.
type: Mapped[str] = mapped_column(String(32), default="agy", nullable=False)
base_url: Mapped[str] = mapped_column(String(512), default="", nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = _created()
class AiModelCredentialRow(Base):
__tablename__ = "ai_model_credentials"
id: Mapped[str] = _pk()
provider_id: Mapped[str] = mapped_column(
ForeignKey("ai_providers.id", ondelete="CASCADE"), nullable=False, index=True
)
name: Mapped[str] = mapped_column(String(120), nullable=False)
encrypted_api_key: Mapped[str] = mapped_column(Text, default="", nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = _created()
# --- agy account columns (empty for API-key credentials) -----------------
#: Google account email this credential belongs to; also the rotation label.
email: Mapped[str] = mapped_column(String(320), default="", nullable=False)
#: Antigravity ``cloudaicompanionProject`` id resolved during login.
project_id: Mapped[str] = mapped_column(String(160), default="", nullable=False)
#: ``active`` | ``cooldown`` | ``error`` — see ``CredentialStatus``.
status: Mapped[str] = mapped_column(String(16), default="active", nullable=False)
#: When set and in the future, the rotation pool skips this account.
cooldown_until: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
last_error: Mapped[str] = mapped_column(String(512), default="", nullable=False)
last_used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
class AiModelRow(Base):
__tablename__ = "ai_models"
__table_args__ = (
UniqueConstraint("provider_id", "internal_name", name="uq_model_provider_name"),
)
id: Mapped[str] = _pk()
provider_id: Mapped[str] = mapped_column(
ForeignKey("ai_providers.id", ondelete="CASCADE"), nullable=False, index=True
)
internal_name: Mapped[str] = mapped_column(String(160), nullable=False)
display_name_for_admin: Mapped[str] = mapped_column(String(160), nullable=False)
capabilities: Mapped[list] = mapped_column(JSON, default=list, nullable=False)
input_price_per_1m: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
output_price_per_1m: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
#: agy reasoning effort ("" = provider default); see ``AiModel``.
reasoning_effort: Mapped[str] = mapped_column(String(16), default="", nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = _created()
class RoutingPolicyRow(Base):
__tablename__ = "routing_policies"
id: Mapped[str] = _pk()
name: Mapped[str] = mapped_column(String(120), unique=True, nullable=False)
description: Mapped[str] = mapped_column(Text, default="", nullable=False)
mode: Mapped[str] = mapped_column(String(16), default="balanced", nullable=False)
fallback_policy_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
budget_limits_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = _created()
class RateCardRow(Base):
__tablename__ = "rate_cards"
id: Mapped[str] = _pk()
name: Mapped[str] = mapped_column(String(120), nullable=False)
unit: Mapped[str] = mapped_column(String(40), unique=True, nullable=False)
customer_price: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
internal_cost_estimate: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = _created()
# ---------------------------------------------------------------------------
# Projects & Git
# ---------------------------------------------------------------------------
class ProjectRow(Base):
__tablename__ = "projects"
__table_args__ = (
UniqueConstraint("organization_id", "slug", name="uq_project_org_slug"),
# The project list is always scoped to one organization and ordered by
# creation, so one composite index serves both the filter and the sort.
Index("ix_project_org_created", "organization_id", "created_at"),
)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
name: Mapped[str] = mapped_column(String(160), nullable=False)
slug: Mapped[str] = mapped_column(String(64), nullable=False)
description: Mapped[str] = mapped_column(Text, default="", nullable=False)
created_by_user_id: Mapped[str | None] = mapped_column(
ForeignKey("users.id"), nullable=True, index=True
)
default_branch: Mapped[str] = mapped_column(String(80), default="main", nullable=False)
created_at: Mapped[datetime] = _created()
class ProjectRepositoryRow(Base):
__tablename__ = "project_repositories"
id: Mapped[str] = _pk()
project_id: Mapped[str] = mapped_column(
ForeignKey("projects.id", ondelete="CASCADE"), unique=True, nullable=False, index=True
)
provider: Mapped[str] = mapped_column(String(32), default="gitea", nullable=False)
owner: Mapped[str] = mapped_column(String(120), default="", nullable=False)
repo_name: Mapped[str] = mapped_column(String(160), default="", nullable=False)
clone_url: Mapped[str] = mapped_column(String(512), default="", nullable=False)
web_url: Mapped[str] = mapped_column(String(512), default="", nullable=False)
default_branch: Mapped[str] = mapped_column(String(80), default="main", nullable=False)
created_at: Mapped[datetime] = _created()
class ProjectGitCredentialRow(Base):
__tablename__ = "project_git_credentials"
id: Mapped[str] = _pk()
project_id: Mapped[str] = mapped_column(
ForeignKey("projects.id", ondelete="CASCADE"), unique=True, nullable=False, index=True
)
username: Mapped[str] = mapped_column(String(160), nullable=False)
encrypted_password: Mapped[str] = mapped_column(Text, nullable=False)
created_at: Mapped[datetime] = _created()
# ---------------------------------------------------------------------------
# Preview domain routing
# ---------------------------------------------------------------------------
class PreviewPortRow(Base):
__tablename__ = "preview_ports"
__table_args__ = (
UniqueConstraint("project_id", "service_name", name="uq_preview_port_project_service"),
UniqueConstraint("port", name="uq_preview_port_port"),
)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
project_id: Mapped[str] = mapped_column(
ForeignKey("projects.id", ondelete="CASCADE"), nullable=False, index=True
)
service_name: Mapped[str] = mapped_column(String(80), default="web", nullable=False)
port: Mapped[int] = mapped_column(Integer, nullable=False)
status: Mapped[str] = mapped_column(String(16), default="active", nullable=False)
created_at: Mapped[datetime] = _created()
class PreviewDomainRow(Base):
__tablename__ = "preview_domains"
__table_args__ = (
UniqueConstraint("host", name="uq_preview_domain_host"),
Index("ix_preview_domain_org_created", "organization_id", "created_at"),
)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
project_id: Mapped[str] = mapped_column(
ForeignKey("projects.id", ondelete="CASCADE"), nullable=False, index=True
)
host: Mapped[str] = mapped_column(String(255), nullable=False)
port: Mapped[int] = mapped_column(Integer, nullable=False)
status: Mapped[str] = mapped_column(String(16), default="active", nullable=False)
protected: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
created_by_user_id: Mapped[str | None] = mapped_column(
ForeignKey("users.id"), nullable=True, index=True
)
created_at: Mapped[datetime] = _created()
# ---------------------------------------------------------------------------
# Sandbox
# ---------------------------------------------------------------------------
class SandboxTemplateRow(Base):
__tablename__ = "sandbox_templates"
id: Mapped[str] = _pk()
name: Mapped[str] = mapped_column(String(120), unique=True, nullable=False)
provider: Mapped[str] = mapped_column(String(32), default="mock", nullable=False)
image: Mapped[str] = mapped_column(String(255), default="python:3.13-slim", nullable=False)
description: Mapped[str] = mapped_column(Text, default="", nullable=False)
default_cpu: Mapped[float] = mapped_column(Float, default=1.0, nullable=False)
default_memory_mb: Mapped[int] = mapped_column(Integer, default=1024, nullable=False)
default_disk_mb: Mapped[int] = mapped_column(Integer, default=2048, nullable=False)
is_system: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = _created()
class WorkspaceRow(Base):
__tablename__ = "workspaces"
__table_args__ = (Index("ix_workspace_project_created", "project_id", "created_at"),)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
project_id: Mapped[str] = mapped_column(
ForeignKey("projects.id", ondelete="CASCADE"), nullable=False, index=True
)
template_id: Mapped[str] = mapped_column(
ForeignKey("sandbox_templates.id"), nullable=False, index=True
)
provider: Mapped[str] = mapped_column(String(32), default="mock", nullable=False)
external_workspace_id: Mapped[str | None] = mapped_column(String(160), nullable=True)
status: Mapped[str] = mapped_column(String(16), default="pending", nullable=False)
created_at: Mapped[datetime] = _created()
class SandboxSessionRow(Base):
__tablename__ = "sandbox_sessions"
id: Mapped[str] = _pk()
workspace_id: Mapped[str] = mapped_column(
ForeignKey("workspaces.id", ondelete="CASCADE"), nullable=False, index=True
)
external_session_id: Mapped[str | None] = mapped_column(String(160), nullable=True)
status: Mapped[str] = mapped_column(String(16), default="starting", nullable=False)
started_at: Mapped[datetime] = _created()
stopped_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
last_active_at: Mapped[datetime] = _created()
class CommandRunRow(Base):
__tablename__ = "command_runs"
id: Mapped[str] = _pk()
session_id: Mapped[str] = mapped_column(
ForeignKey("sandbox_sessions.id", ondelete="CASCADE"), nullable=False, index=True
)
command: Mapped[str] = mapped_column(Text, nullable=False)
exit_code: Mapped[int | None] = mapped_column(Integer, nullable=True)
stdout_ref: Mapped[str] = mapped_column(Text, default="", nullable=False)
stderr_ref: Mapped[str] = mapped_column(Text, default="", nullable=False)
duration_ms: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
truncated: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
created_at: Mapped[datetime] = _created()
class SnapshotRow(Base):
__tablename__ = "snapshots"
id: Mapped[str] = _pk()
workspace_id: Mapped[str] = mapped_column(
ForeignKey("workspaces.id", ondelete="CASCADE"), nullable=False, index=True
)
name: Mapped[str] = mapped_column(String(120), nullable=False)
external_snapshot_id: Mapped[str | None] = mapped_column(String(160), nullable=True)
created_at: Mapped[datetime] = _created()
class PreviewUrlRow(Base):
__tablename__ = "preview_urls"
id: Mapped[str] = _pk()
workspace_id: Mapped[str] = mapped_column(
ForeignKey("workspaces.id", ondelete="CASCADE"), nullable=False, index=True
)
port: Mapped[int] = mapped_column(Integer, nullable=False)
url: Mapped[str] = mapped_column(String(512), nullable=False)
created_at: Mapped[datetime] = _created()
class SandboxResourceLimitRow(Base):
__tablename__ = "sandbox_resource_limits"
__table_args__ = (UniqueConstraint("scope", "scope_id", name="uq_limit_scope"),)
id: Mapped[str] = _pk()
scope: Mapped[str] = mapped_column(String(32), default="organization", nullable=False)
scope_id: Mapped[str | None] = mapped_column(String(32), nullable=True)
cpu: Mapped[float] = mapped_column(Float, default=1.0, nullable=False)
memory_mb: Mapped[int] = mapped_column(Integer, default=1024, nullable=False)
disk_mb: Mapped[int] = mapped_column(Integer, default=2048, nullable=False)
timeout_seconds: Mapped[int] = mapped_column(Integer, default=120, nullable=False)
max_output_bytes: Mapped[int] = mapped_column(Integer, default=200_000, nullable=False)
network_policy: Mapped[str] = mapped_column(String(16), default="deny_all", nullable=False)
# ---------------------------------------------------------------------------
# Workforce: templates, employees, chat, tasks, runs, artifacts
# ---------------------------------------------------------------------------
class AgentTemplateRow(Base):
__tablename__ = "agent_templates"
id: Mapped[str] = _pk()
name: Mapped[str] = mapped_column(String(120), unique=True, nullable=False)
role_key: Mapped[str] = mapped_column(String(32), nullable=False)
description: Mapped[str] = mapped_column(Text, default="", nullable=False)
default_prompt: Mapped[str] = mapped_column(Text, default="", nullable=False)
default_tools_json: Mapped[list] = mapped_column(JSON, default=list, nullable=False)
default_routing_policy_id: Mapped[str | None] = mapped_column(String(32), nullable=True)
is_system: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = _created()
class EmployeeRow(Base):
__tablename__ = "employees"
__table_args__ = (Index("ix_employee_org_created", "organization_id", "created_at"),)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
project_id: Mapped[str | None] = mapped_column(
ForeignKey("projects.id", ondelete="SET NULL"), nullable=True, index=True
)
template_id: Mapped[str | None] = mapped_column(String(32), nullable=True)
name: Mapped[str] = mapped_column(String(160), nullable=False)
role_key: Mapped[str] = mapped_column(String(32), nullable=False)
status: Mapped[str] = mapped_column(String(16), default="active", nullable=False)
routing_policy_id: Mapped[str | None] = mapped_column(String(32), nullable=True)
budget_limit_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
created_at: Mapped[datetime] = _created()
class PromptVersionRow(Base):
__tablename__ = "prompt_versions"
__table_args__ = (
UniqueConstraint("employee_id", "version", name="uq_prompt_employee_version"),
)
id: Mapped[str] = _pk()
employee_id: Mapped[str] = mapped_column(
ForeignKey("employees.id", ondelete="CASCADE"), nullable=False, index=True
)
version: Mapped[int] = mapped_column(Integer, nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
created_by_user_id: Mapped[str | None] = mapped_column(
ForeignKey("users.id"), nullable=True, index=True
)
created_at: Mapped[datetime] = _created()
class EmployeeToolPermissionRow(Base):
__tablename__ = "employee_tool_permissions"
__table_args__ = (
UniqueConstraint("employee_id", "tool_key", name="uq_employee_tool"),
)
id: Mapped[str] = _pk()
employee_id: Mapped[str] = mapped_column(
ForeignKey("employees.id", ondelete="CASCADE"), nullable=False, index=True
)
tool_key: Mapped[str] = mapped_column(String(80), nullable=False)
allowed: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
policy_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
class ThreadRow(Base):
__tablename__ = "threads"
__table_args__ = (Index("ix_thread_org_created", "organization_id", "created_at"),)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
project_id: Mapped[str | None] = mapped_column(
ForeignKey("projects.id", ondelete="SET NULL"), nullable=True, index=True
)
title: Mapped[str] = mapped_column(String(255), default="", nullable=False)
created_by_user_id: Mapped[str | None] = mapped_column(
ForeignKey("users.id"), nullable=True, index=True
)
created_at: Mapped[datetime] = _created()
class MessageRow(Base):
__tablename__ = "messages"
# A conversation is always read as "one thread, oldest first", so the
# composite index serves the filter and the ordering in one lookup.
__table_args__ = (Index("ix_message_thread_created", "thread_id", "created_at"),)
id: Mapped[str] = _pk()
thread_id: Mapped[str] = mapped_column(
ForeignKey("threads.id", ondelete="CASCADE"), nullable=False, index=True
)
role: Mapped[str] = mapped_column(String(16), nullable=False)
content: Mapped[str] = mapped_column(Text, default="", nullable=False)
employee_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
created_at: Mapped[datetime] = _created()
class TaskRow(Base):
__tablename__ = "tasks"
__table_args__ = (
Index("ix_task_org_status", "organization_id", "status"),
Index("ix_task_org_created", "organization_id", "created_at"),
)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
project_id: Mapped[str | None] = mapped_column(
ForeignKey("projects.id", ondelete="SET NULL"), nullable=True, index=True
)
thread_id: Mapped[str | None] = mapped_column(
ForeignKey("threads.id", ondelete="SET NULL"), nullable=True, index=True
)
title: Mapped[str] = mapped_column(String(255), nullable=False)
description: Mapped[str] = mapped_column(Text, default="", nullable=False)
status: Mapped[str] = mapped_column(String(24), default="new", nullable=False)
assigned_employee_id: Mapped[str | None] = mapped_column(
String(32), nullable=True, index=True
)
created_by_user_id: Mapped[str | None] = mapped_column(
ForeignKey("users.id"), nullable=True, index=True
)
created_at: Mapped[datetime] = _created()
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utcnow, onupdate=utcnow, nullable=False
)
class RunRow(Base):
__tablename__ = "runs"
# A run has no organization_id of its own: it is scoped through its task,
# which is the object that carries the tenant. Indexing a column that does
# not exist here would be wrong, so the composite stays on task_id.
__table_args__ = (Index("ix_run_task_created", "task_id", "created_at"),)
id: Mapped[str] = _pk()
task_id: Mapped[str] = mapped_column(
ForeignKey("tasks.id", ondelete="CASCADE"), nullable=False, index=True
)
employee_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
status: Mapped[str] = mapped_column(String(24), default="queued", nullable=False)
error: Mapped[str | None] = mapped_column(Text, nullable=True)
started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
created_at: Mapped[datetime] = _created()
class RunStepRow(Base):
__tablename__ = "run_steps"
__table_args__ = (Index("ix_run_step_run_index", "run_id", "index"),)
id: Mapped[str] = _pk()
run_id: Mapped[str] = mapped_column(
ForeignKey("runs.id", ondelete="CASCADE"), nullable=False, index=True
)
index: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
kind: Mapped[str] = mapped_column(String(40), default="note", nullable=False)
title: Mapped[str] = mapped_column(String(255), default="", nullable=False)
payload_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
created_at: Mapped[datetime] = _created()
class ToolCallRow(Base):
__tablename__ = "tool_calls"
__table_args__ = (
Index("ix_tool_call_run_created", "run_id", "created_at"),
# The pending-approval queue is "one organization, still awaiting a
# decision", which is a filter on status plus a join back to the run.
Index("ix_tool_call_status", "status"),
)
id: Mapped[str] = _pk()
run_id: Mapped[str] = mapped_column(
ForeignKey("runs.id", ondelete="CASCADE"), nullable=False, index=True
)
tool_key: Mapped[str] = mapped_column(String(80), nullable=False)
arguments_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
result_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
status: Mapped[str] = mapped_column(String(24), default="pending", nullable=False)
requires_approval: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
approved_by_user_id: Mapped[str | None] = mapped_column(
ForeignKey("users.id"), nullable=True, index=True
)
created_at: Mapped[datetime] = _created()
class ArtifactRow(Base):
__tablename__ = "artifacts"
__table_args__ = (Index("ix_artifact_org_created", "organization_id", "created_at"),)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
project_id: Mapped[str | None] = mapped_column(
ForeignKey("projects.id", ondelete="SET NULL"), nullable=True, index=True
)
run_id: Mapped[str | None] = mapped_column(
ForeignKey("runs.id", ondelete="SET NULL"), nullable=True, index=True
)
name: Mapped[str] = mapped_column(String(255), nullable=False)
kind: Mapped[str] = mapped_column(String(32), default="file", nullable=False)
content_ref: Mapped[str] = mapped_column(Text, default="", nullable=False)
size_bytes: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
created_at: Mapped[datetime] = _created()
# ---------------------------------------------------------------------------
# Billing & Usage
# ---------------------------------------------------------------------------
class PlanRow(Base):
__tablename__ = "plans"
id: Mapped[str] = _pk()
name: Mapped[str] = mapped_column(String(120), unique=True, nullable=False)
monthly_price: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
included_credit: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
features_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = _created()
class SubscriptionRow(Base):
__tablename__ = "subscriptions"
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
plan_id: Mapped[str] = mapped_column(ForeignKey("plans.id"), nullable=False, index=True)
status: Mapped[str] = mapped_column(String(16), default="active", nullable=False)
external_subscription_id: Mapped[str | None] = mapped_column(String(160), nullable=True)
current_period_end: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
created_at: Mapped[datetime] = _created()
class CreditWalletRow(Base):
__tablename__ = "credit_wallets"
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), unique=True, nullable=False, index=True
)
balance: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
currency: Mapped[str] = mapped_column(String(8), default="USD", nullable=False)
low_balance_threshold: Mapped[float] = mapped_column(Float, default=5.0, nullable=False)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utcnow, onupdate=utcnow, nullable=False
)
class UsageEventRow(Base):
__tablename__ = "usage_events"
__table_args__ = (
Index("ix_usage_org_created", "organization_id", "created_at"),
Index("ix_usage_org_unit", "organization_id", "unit"),
)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
project_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
user_id: Mapped[str | None] = mapped_column(String(32), nullable=True)
employee_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
run_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
unit: Mapped[str] = mapped_column(String(40), nullable=False)
quantity: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
unit_price: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
internal_cost_estimate: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
total_price: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
metadata_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
created_at: Mapped[datetime] = _created()
class InvoiceRow(Base):
__tablename__ = "invoices"
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
amount: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
currency: Mapped[str] = mapped_column(String(8), default="USD", nullable=False)
status: Mapped[str] = mapped_column(String(16), default="draft", nullable=False)
external_invoice_id: Mapped[str | None] = mapped_column(String(160), nullable=True)
period_start: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
period_end: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
created_at: Mapped[datetime] = _created()
class PaymentRow(Base):
__tablename__ = "payments"
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
amount: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
status: Mapped[str] = mapped_column(String(16), default="pending", nullable=False)
provider: Mapped[str] = mapped_column(String(32), default="mock", nullable=False)
external_payment_id: Mapped[str | None] = mapped_column(String(160), nullable=True)
invoice_id: Mapped[str | None] = mapped_column(String(32), nullable=True)
created_at: Mapped[datetime] = _created()
# ---------------------------------------------------------------------------
# Knowledge
# ---------------------------------------------------------------------------
class KnowledgeSourceRow(Base):
__tablename__ = "knowledge_sources"
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
project_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
name: Mapped[str] = mapped_column(String(255), nullable=False)
kind: Mapped[str] = mapped_column(String(16), default="text", nullable=False)
uri: Mapped[str] = mapped_column(String(512), default="", nullable=False)
created_at: Mapped[datetime] = _created()
class DocumentRow(Base):
__tablename__ = "documents"
id: Mapped[str] = _pk()
source_id: Mapped[str] = mapped_column(
ForeignKey("knowledge_sources.id", ondelete="CASCADE"), nullable=False, index=True
)
title: Mapped[str] = mapped_column(String(255), default="", nullable=False)
content: Mapped[str] = mapped_column(Text, default="", nullable=False)
created_at: Mapped[datetime] = _created()
class DocumentChunkRow(Base):
__tablename__ = "document_chunks"
__table_args__ = (Index("ix_chunk_document_index", "document_id", "index"),)
id: Mapped[str] = _pk()
document_id: Mapped[str] = mapped_column(
ForeignKey("documents.id", ondelete="CASCADE"), nullable=False, index=True
)
index: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
content: Mapped[str] = mapped_column(Text, default="", nullable=False)
embedding_json: Mapped[list] = mapped_column(JSON, default=list, nullable=False)
class EmbeddingJobRow(Base):
__tablename__ = "embedding_jobs"
id: Mapped[str] = _pk()
source_id: Mapped[str] = mapped_column(
ForeignKey("knowledge_sources.id", ondelete="CASCADE"), nullable=False, index=True
)
status: Mapped[str] = mapped_column(String(16), default="pending", nullable=False)
error: Mapped[str | None] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = _created()
class RetrievalPolicyRow(Base):
__tablename__ = "retrieval_policies"
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
name: Mapped[str] = mapped_column(String(120), nullable=False)
scope_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
created_at: Mapped[datetime] = _created()
# ---------------------------------------------------------------------------
# Audit & Observability
# ---------------------------------------------------------------------------
class AuditEventRow(Base):
__tablename__ = "audit_events"
__table_args__ = (
Index("ix_audit_org_created", "organization_id", "created_at"),
Index("ix_audit_action", "action"),
)
id: Mapped[str] = _pk()
organization_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
actor_user_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
action: Mapped[str] = mapped_column(String(80), nullable=False)
target_type: Mapped[str] = mapped_column(String(40), default="", nullable=False)
target_id: Mapped[str] = mapped_column(String(64), default="", nullable=False)
metadata_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
ip_address: Mapped[str] = mapped_column(String(64), default="", nullable=False)
user_agent: Mapped[str] = mapped_column(String(255), default="", nullable=False)
created_at: Mapped[datetime] = _created()
class TraceRow(Base):
__tablename__ = "traces"
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(String(32), nullable=False, index=True)
run_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
name: Mapped[str] = mapped_column(String(160), default="", nullable=False)
created_at: Mapped[datetime] = _created()
class SpanRow(Base):
__tablename__ = "spans"
id: Mapped[str] = _pk()
trace_id: Mapped[str] = mapped_column(
ForeignKey("traces.id", ondelete="CASCADE"), nullable=False, index=True
)
parent_span_id: Mapped[str | None] = mapped_column(String(32), nullable=True)
name: Mapped[str] = mapped_column(String(160), nullable=False)
kind: Mapped[str] = mapped_column(String(32), default="internal", nullable=False)
attributes_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
started_at: Mapped[datetime] = _created()
ended_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
class ErrorEventRow(Base):
"""Persisted failures — the table the error console reads.
Wide on purpose: a row that lacks the request path or the exception class
forces whoever is debugging to reproduce the bug to learn what it was. The
indexes cover the three access patterns — filter by tenant, filter by
"still open", and group by fingerprint.
"""
__tablename__ = "error_events"
__table_args__ = (
# Grouping view: "how often did this exact bug happen, and when last?"
Index("ix_error_events_fingerprint_created", "fingerprint", "created_at"),
# Feed view: "show me the newest failures for this tenant."
Index("ix_error_events_org_created", "organization_id", "created_at"),
)
id: Mapped[str] = _pk()
organization_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
run_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
kind: Mapped[str] = mapped_column(String(80), nullable=False)
message: Mapped[str] = mapped_column(Text, default="", nullable=False)
stack: Mapped[str] = mapped_column(Text, default="", nullable=False)
metadata_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
# --- classification -----------------------------------------------------
source: Mapped[str] = mapped_column(String(16), default="http", nullable=False)
severity: Mapped[str] = mapped_column(String(16), default="error", nullable=False)
error_type: Mapped[str] = mapped_column(String(200), default="", nullable=False)
#: Groups identical failures. Empty string rather than NULL so the grouping
#: index never has to deal with a NULL key.
fingerprint: Mapped[str] = mapped_column(String(64), default="", nullable=False, index=True)
# --- transport context --------------------------------------------------
status_code: Mapped[int | None] = mapped_column(Integer, nullable=True)
method: Mapped[str] = mapped_column(String(10), default="", nullable=False)
path: Mapped[str] = mapped_column(String(512), default="", nullable=False)
# --- actor context ------------------------------------------------------
user_id: Mapped[str | None] = mapped_column(String(32), nullable=True)
request_id: Mapped[str] = mapped_column(String(64), default="", nullable=False)
ip: Mapped[str] = mapped_column(String(64), default="", nullable=False)
user_agent: Mapped[str] = mapped_column(String(255), default="", nullable=False)
# --- triage -------------------------------------------------------------
resolved_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True, index=True
)
resolved_note: Mapped[str] = mapped_column(Text, default="", nullable=False)
created_at: Mapped[datetime] = _created()
class QualityMetricRow(Base):
__tablename__ = "quality_metrics"
__table_args__ = (Index("ix_metric_org_name", "organization_id", "name"),)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(String(32), nullable=False, index=True)
name: Mapped[str] = mapped_column(String(120), nullable=False)
value: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
dimensions_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
created_at: Mapped[datetime] = _created()