- projects: app profile columns + one app per org; project_external_git, deploy_targets, org_model_grants tables (migration a0f0e1d2c3b4) - ports + fakes: RepoWorkspace, DeliveryHooks, DeployTarget, Memory, ModelGrants, WorkforceBootstrap; AppProfile + AppProfileRepository - per-workstream wiring (container_org/engine/delivery), config blocks, empty routers, delivery worker, MCP/delivery service shells, web stubs - admin nav derives from ADMIN_ITEMS; plan docs updated for ext4 worktrees Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1029 lines
46 KiB
Python
1029 lines
46 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,
|
|
text,
|
|
)
|
|
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"
|
|
__table_args__ = (
|
|
# Exactly one platform owner may exist — ever. A partial unique index
|
|
# makes that a *database* invariant, not just an application habit: a
|
|
# second INSERT with platform_role='owner' fails even if some future
|
|
# code path forgets to check. Both dialects support partial indexes.
|
|
Index(
|
|
"uq_single_platform_owner",
|
|
"platform_role",
|
|
unique=True,
|
|
sqlite_where=text("platform_role = 'owner'"),
|
|
postgresql_where=text("platform_role = 'owner'"),
|
|
),
|
|
Index("ix_user_platform_role", "platform_role"),
|
|
)
|
|
|
|
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)
|
|
#: Platform-wide role: ``owner`` | ``manager`` | ``user`` (see
|
|
#: ``PlatformRole``). Exactly one row may hold ``owner``.
|
|
platform_role: Mapped[str] = mapped_column(String(16), default="user", 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"),
|
|
# v2 rule: one organization has exactly one application.
|
|
UniqueConstraint("organization_id", name="uq_project_one_per_org"),
|
|
# 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)
|
|
# --- v2 application profile (filled by the organization wizard) ---
|
|
app_type: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
|
language: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
|
framework: Mapped[str | None] = mapped_column(String(48), nullable=True)
|
|
runtime_version: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
|
package_manager: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
|
setup_command: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
build_command: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
test_command: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
start_command: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
app_port: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
healthcheck_path: Mapped[str] = mapped_column(String(255), default="/", nullable=False)
|
|
git_source: Mapped[str] = mapped_column(String(16), default="platform", nullable=False)
|
|
external_repo_url: Mapped[str | None] = mapped_column(String(512), nullable=True)
|
|
wizard_completed_at: Mapped[datetime | None] = mapped_column(
|
|
DateTime(timezone=True), nullable=True
|
|
)
|
|
created_at: Mapped[datetime] = _created()
|
|
|
|
|
|
class ProjectExternalGitRow(Base):
|
|
"""Credentials for a user-supplied git remote. Secrets are encrypted at rest."""
|
|
|
|
__tablename__ = "project_external_git"
|
|
|
|
project_id: Mapped[str] = mapped_column(
|
|
ForeignKey("projects.id", ondelete="CASCADE"), primary_key=True
|
|
)
|
|
auth_type: Mapped[str] = mapped_column(String(16), default="none", nullable=False)
|
|
username: Mapped[str | None] = mapped_column(String(160), nullable=True)
|
|
secret_encrypted: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
known_hosts: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), default=utcnow, onupdate=utcnow, nullable=False
|
|
)
|
|
|
|
|
|
class DeployTargetRow(Base):
|
|
"""A production deploy target (ssh / sftp / ftp / ftps). Secrets are encrypted."""
|
|
|
|
__tablename__ = "deploy_targets"
|
|
__table_args__ = (Index("ix_deploy_target_project", "project_id"),)
|
|
|
|
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
|
|
)
|
|
kind: Mapped[str] = mapped_column(String(8), nullable=False)
|
|
host: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
port: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
username: Mapped[str] = mapped_column(String(160), nullable=False)
|
|
auth_type: Mapped[str] = mapped_column(String(16), default="password", nullable=False)
|
|
secret_encrypted: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
remote_path: Mapped[str] = mapped_column(String(512), nullable=False)
|
|
post_deploy_command: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
host_fingerprint: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
last_tested_at: Mapped[datetime | None] = mapped_column(
|
|
DateTime(timezone=True), nullable=True
|
|
)
|
|
last_test_ok: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
|
|
last_test_message: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
|
created_at: Mapped[datetime] = _created()
|
|
|
|
|
|
class OrgModelGrantRow(Base):
|
|
"""A model the platform owner lets one organization's employees use."""
|
|
|
|
__tablename__ = "org_model_grants"
|
|
__table_args__ = (
|
|
UniqueConstraint("organization_id", "ai_model_id", name="uq_org_model_grant"),
|
|
)
|
|
|
|
id: Mapped[str] = _pk()
|
|
organization_id: Mapped[str] = mapped_column(
|
|
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
|
|
)
|
|
ai_model_id: Mapped[str] = mapped_column(
|
|
ForeignKey("ai_models.id", ondelete="CASCADE"), nullable=False
|
|
)
|
|
daily_credit_cap: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
created_by_user_id: Mapped[str | None] = mapped_column(
|
|
ForeignKey("users.id"), nullable=True
|
|
)
|
|
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()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# v2 tables owned by one workstream each (kept in separate modules so the three
|
|
# parallel agents never edit the same file). Imported last so every table is
|
|
# registered on ``Base.metadata`` before ``create_all`` / alembic autogenerate.
|
|
# ---------------------------------------------------------------------------
|
|
from app.infrastructure.db import models_delivery as _models_delivery # noqa: E402,F401
|
|
from app.infrastructure.db import models_engine as _models_engine # noqa: E402,F401
|
|
from app.infrastructure.db import models_org as _models_org # noqa: E402,F401
|