نسخهٔ وب مستقل — پلتفرم نیروی کار هوش مصنوعی
مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت. تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
This commit is contained in:
@@ -0,0 +1,895 @@
|
||||
"""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)
|
||||
type: Mapped[str] = mapped_column(String(32), default="mock", 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()
|
||||
|
||||
|
||||
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)
|
||||
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()
|
||||
Reference in New Issue
Block a user