"""Hub-owned SQLAlchemy rows and constraints. The model intentionally does not contain gateway tables or foreign keys to the ContextForge database. Cross-system identity is represented only by opaque external IDs and is reconciled by a later adapter. """ from __future__ import annotations from datetime import datetime from typing import Any from uuid import UUID, uuid4 import sqlalchemy as sa from sqlalchemy.dialects import postgresql from sqlalchemy.orm import Mapped, mapped_column from .db import Base, utc_now JSON_TYPE = sa.JSON().with_variant(postgresql.JSONB(), "postgresql") UUID_TYPE = sa.Uuid(as_uuid=True) class TimestampMixin: created_at: Mapped[datetime] = mapped_column( sa.DateTime(timezone=True), default=utc_now, nullable=False ) updated_at: Mapped[datetime] = mapped_column( sa.DateTime(timezone=True), default=utc_now, onupdate=utc_now, nullable=False ) revision: Mapped[int] = mapped_column(sa.Integer, default=1, nullable=False) class WorkspaceRow(TimestampMixin, Base): __tablename__ = "workspaces" __table_args__ = (sa.UniqueConstraint("slug", name="uq_workspaces_slug"),) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) slug: Mapped[str] = mapped_column(sa.String(63), nullable=False) name: Mapped[str] = mapped_column(sa.String(160), nullable=False) status: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="active") class ProjectRow(TimestampMixin, Base): __tablename__ = "projects" __table_args__ = ( sa.UniqueConstraint("workspace_id", "slug", name="uq_projects_workspace_slug"), sa.UniqueConstraint("workspace_id", "id", name="uq_projects_workspace_id"), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(sa.ForeignKey("workspaces.id"), nullable=False) slug: Mapped[str] = mapped_column(sa.String(63), nullable=False) name: Mapped[str] = mapped_column(sa.String(160), nullable=False) description: Mapped[str | None] = mapped_column(sa.String(1000)) status: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="active") repository_provider: Mapped[str | None] = mapped_column(sa.String(32)) repository_owner: Mapped[str | None] = mapped_column(sa.String(120)) repository_name: Mapped[str | None] = mapped_column(sa.String(120)) classification: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="internal") owner_metadata: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) class EnvironmentRow(TimestampMixin, Base): __tablename__ = "environments" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id", "project_id"], ["projects.workspace_id", "projects.id"], name="fk_environments_project_scope", ), sa.UniqueConstraint("workspace_id", "project_id", "id", name="uq_environments_scope_id"), sa.UniqueConstraint("project_id", "name", name="uq_environments_project_name"), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) name: Mapped[str] = mapped_column(sa.String(32), nullable=False) classification: Mapped[str] = mapped_column(sa.String(32), nullable=False) base_url: Mapped[str | None] = mapped_column(sa.String(2048)) status: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="active") class ConnectorRow(TimestampMixin, Base): __tablename__ = "connectors" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id", "project_id"], ["projects.workspace_id", "projects.id"], name="fk_connectors_project_scope", ), sa.ForeignKeyConstraint( ["workspace_id", "project_id", "environment_id"], ["environments.workspace_id", "environments.project_id", "environments.id"], name="fk_connectors_environment_scope", ), sa.UniqueConstraint( "workspace_id", "project_id", "environment_scope", "slug", name="uq_connectors_scope_slug", ), sa.CheckConstraint( "health_state in (" "'unconfigured','checking','healthy','degraded','unavailable','misconfigured','disabled')", name="ck_connectors_health_state", ), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) environment_id: Mapped[UUID | None] = mapped_column() environment_scope: Mapped[str] = mapped_column( sa.String(36), nullable=False, default="__project__" ) type: Mapped[str] = mapped_column(sa.String(64), nullable=False) slug: Mapped[str] = mapped_column(sa.String(63), nullable=False) endpoint: Mapped[str | None] = mapped_column(sa.String(2048)) credential_reference: Mapped[str | None] = mapped_column(sa.String(512)) configuration: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) enabled: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, default=False) health_state: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="unconfigured") last_check_at: Mapped[datetime | None] = mapped_column(sa.DateTime(timezone=True)) latency_ms: Mapped[int | None] = mapped_column(sa.Integer) stable_error_code: Mapped[str | None] = mapped_column(sa.String(128)) sanitized_error: Mapped[str | None] = mapped_column(sa.String(1000)) class RiskClassRow(Base): __tablename__ = "risk_classes" code: Mapped[str] = mapped_column(sa.String(64), primary_key=True) description: Mapped[str] = mapped_column(sa.String(500), nullable=False) allows_writes: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, default=False) max_output_bytes: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=65536) class CapabilityRow(TimestampMixin, Base): __tablename__ = "capabilities" __table_args__ = ( sa.ForeignKeyConstraint( ["risk_class"], ["risk_classes.code"], name="fk_capabilities_risk_class" ), sa.UniqueConstraint("canonical_name", "version", name="uq_capabilities_name_version"), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) canonical_name: Mapped[str] = mapped_column(sa.String(120), nullable=False) version: Mapped[str] = mapped_column(sa.String(32), nullable=False) description: Mapped[str] = mapped_column(sa.String(1000), nullable=False) risk_class: Mapped[str] = mapped_column(sa.String(64), nullable=False) enabled: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, default=True) class ToolDefinitionRow(TimestampMixin, Base): __tablename__ = "tool_definitions" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id", "project_id"], ["projects.workspace_id", "projects.id"], name="fk_tools_project_scope", ), sa.ForeignKeyConstraint(["connector_id"], ["connectors.id"], name="fk_tools_connector"), sa.UniqueConstraint("canonical_name", "version", name="uq_tools_name_version"), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) connector_id: Mapped[UUID] = mapped_column(nullable=False) canonical_name: Mapped[str] = mapped_column(sa.String(120), nullable=False) version: Mapped[str] = mapped_column(sa.String(32), nullable=False) input_schema: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) output_schema: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) timeout_ms: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=10000) output_limit_bytes: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=65536) rate_limit_per_minute: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=60) redaction_profile: Mapped[str] = mapped_column(sa.String(64), nullable=False, default="default") lifecycle_status: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="active") class VirtualServerRow(TimestampMixin, Base): __tablename__ = "virtual_servers" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id", "project_id"], ["projects.workspace_id", "projects.id"], name="fk_virtual_servers_project_scope", ), sa.ForeignKeyConstraint( ["workspace_id", "project_id", "environment_id"], ["environments.workspace_id", "environments.project_id", "environments.id"], name="fk_virtual_servers_environment_scope", ), sa.UniqueConstraint("workspace_id", "project_id", "id", name="uq_virtual_servers_scope_id"), sa.UniqueConstraint("slug", name="uq_virtual_servers_slug"), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) environment_id: Mapped[UUID] = mapped_column(nullable=False) slug: Mapped[str] = mapped_column(sa.String(63), nullable=False) gateway_external_id: Mapped[str | None] = mapped_column(sa.String(256)) desired_hash: Mapped[str] = mapped_column(sa.String(128), nullable=False) enabled: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, default=False) protocol: Mapped[str] = mapped_column(sa.String(64), nullable=False, default="streamable-http") health_state: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="unconfigured") record_metadata: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) class VirtualServerCapabilityRow(Base): __tablename__ = "virtual_server_capabilities" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id", "project_id", "virtual_server_id"], ["virtual_servers.workspace_id", "virtual_servers.project_id", "virtual_servers.id"], name="fk_vs_capabilities_server_scope", ), sa.ForeignKeyConstraint( ["capability_id"], ["capabilities.id"], name="fk_vs_capabilities_capability" ), sa.UniqueConstraint( "virtual_server_id", "capability_id", name="uq_vs_capabilities_binding" ), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) virtual_server_id: Mapped[UUID] = mapped_column(nullable=False) capability_id: Mapped[UUID] = mapped_column(nullable=False) desired_state_source: Mapped[str] = mapped_column( sa.String(64), nullable=False, default="catalog" ) created_at: Mapped[datetime] = mapped_column( sa.DateTime(timezone=True), default=utc_now, nullable=False ) updated_at: Mapped[datetime] = mapped_column( sa.DateTime(timezone=True), default=utc_now, nullable=False ) class ClientRow(TimestampMixin, Base): __tablename__ = "clients" __table_args__ = ( sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], name="fk_clients_workspace"), sa.UniqueConstraint("workspace_id", "id", name="uq_clients_workspace_id"), sa.UniqueConstraint("workspace_id", "issuer", "subject", name="uq_clients_principal"), sa.CheckConstraint( "status in ('pending','active','disabled','revoked')", name="ck_clients_status" ), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) subject: Mapped[str] = mapped_column(sa.String(256), nullable=False) issuer: Mapped[str] = mapped_column(sa.String(512), nullable=False) client_type: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="mcp") status: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="pending") revoked_at: Mapped[datetime | None] = mapped_column(sa.DateTime(timezone=True)) last_seen_at: Mapped[datetime | None] = mapped_column(sa.DateTime(timezone=True)) record_metadata: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) class ClientBindingRow(TimestampMixin, Base): __tablename__ = "client_bindings" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id", "project_id"], ["projects.workspace_id", "projects.id"], name="fk_client_bindings_project_scope", ), sa.ForeignKeyConstraint( ["workspace_id", "client_id"], ["clients.workspace_id", "clients.id"], name="fk_client_bindings_client_scope", ), sa.ForeignKeyConstraint( ["workspace_id", "project_id", "virtual_server_id"], ["virtual_servers.workspace_id", "virtual_servers.project_id", "virtual_servers.id"], name="fk_client_bindings_server_scope", ), sa.UniqueConstraint( "client_id", "virtual_server_id", "capability_pack", name="uq_client_bindings" ), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) client_id: Mapped[UUID] = mapped_column(nullable=False) virtual_server_id: Mapped[UUID] = mapped_column(nullable=False) capability_pack: Mapped[str | None] = mapped_column(sa.String(128)) constraints: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) enabled: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, default=False) class PolicyRow(TimestampMixin, Base): __tablename__ = "policies" __table_args__ = ( sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], name="fk_policies_workspace"), sa.ForeignKeyConstraint( ["workspace_id", "project_id"], ["projects.workspace_id", "projects.id"], name="fk_policies_project_scope", ), sa.UniqueConstraint("workspace_id", "slug", name="uq_policies_workspace_slug"), sa.CheckConstraint( "decision in ('allow','allow_if_conditions','deny','deny_and_alert','enforce')", name="ck_policies_decision", ), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID | None] = mapped_column() slug: Mapped[str] = mapped_column(sa.String(63), nullable=False) priority: Mapped[int] = mapped_column(sa.Integer, nullable=False) description: Mapped[str] = mapped_column(sa.String(1000), nullable=False) match_json: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) conditions_json: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) decision: Mapped[str] = mapped_column(sa.String(32), nullable=False) terminal: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, default=False) source: Mapped[str] = mapped_column(sa.String(64), nullable=False, default="catalog") enabled: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, default=True) class PolicyDecisionRow(Base): __tablename__ = "policy_decisions" id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(sa.ForeignKey("workspaces.id"), nullable=False) project_id: Mapped[UUID | None] = mapped_column() policy_id: Mapped[UUID | None] = mapped_column(sa.ForeignKey("policies.id")) decision: Mapped[str] = mapped_column(sa.String(32), nullable=False) matched_rule_ids: Mapped[list[str]] = mapped_column(JSON_TYPE, default=list, nullable=False) decision_trace: Mapped[list[dict[str, Any]]] = mapped_column( JSON_TYPE, default=list, nullable=False ) reason_code: Mapped[str | None] = mapped_column(sa.String(128)) trace_id: Mapped[str] = mapped_column(sa.String(128), nullable=False) retention_until: Mapped[datetime | None] = mapped_column(sa.DateTime(timezone=True)) created_at: Mapped[datetime] = mapped_column( sa.DateTime(timezone=True), default=utc_now, nullable=False ) class ReconcileRunRow(TimestampMixin, Base): __tablename__ = "reconcile_runs" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id", "project_id"], ["projects.workspace_id", "projects.id"], name="fk_reconcile_runs_project_scope", ), sa.CheckConstraint( "status in (" "'created','planning','planned','applying','verifying','succeeded','invalid','failed')", name="ck_reconcile_runs_status", ), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) desired_hash: Mapped[str] = mapped_column(sa.String(128), nullable=False) current_hash: Mapped[str | None] = mapped_column(sa.String(128)) plan_hash: Mapped[str | None] = mapped_column(sa.String(128)) status: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="created") planned_changes: Mapped[list[dict[str, Any]]] = mapped_column( JSON_TYPE, default=list, nullable=False ) applied_changes: Mapped[list[dict[str, Any]]] = mapped_column( JSON_TYPE, default=list, nullable=False ) verification: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) started_at: Mapped[datetime | None] = mapped_column(sa.DateTime(timezone=True)) completed_at: Mapped[datetime | None] = mapped_column(sa.DateTime(timezone=True)) class OperationRow(TimestampMixin, Base): __tablename__ = "operations" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id", "project_id"], ["projects.workspace_id", "projects.id"], name="fk_operations_project_scope", ), sa.ForeignKeyConstraint( ["workspace_id", "project_id", "environment_id"], ["environments.workspace_id", "environments.project_id", "environments.id"], name="fk_operations_environment_scope", ), sa.UniqueConstraint("workspace_id", "project_id", "id", name="uq_operations_project_id"), sa.CheckConstraint( "state in (" "'requested','planned','approval_required','approved','executing','verifying','succeeded','rejected','failed','rollback_pending','rolled_back','rollback_failed')", name="ck_operations_state", ), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) environment_id: Mapped[UUID] = mapped_column(nullable=False) operation_type: Mapped[str] = mapped_column(sa.String(64), nullable=False) target_ref: Mapped[str] = mapped_column(sa.String(512), nullable=False) plan_hash: Mapped[str] = mapped_column(sa.String(128), nullable=False) state: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="requested") requested_by: Mapped[str] = mapped_column(sa.String(256), nullable=False) expires_at: Mapped[datetime | None] = mapped_column(sa.DateTime(timezone=True)) verification: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) class ApprovalRow(TimestampMixin, Base): __tablename__ = "approvals" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id", "project_id", "operation_id"], ["operations.workspace_id", "operations.project_id", "operations.id"], name="fk_approvals_operation_scope", ), sa.CheckConstraint( "state in ('pending','approved','rejected','expired')", name="ck_approvals_state" ), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) operation_id: Mapped[UUID] = mapped_column(nullable=False) plan_hash: Mapped[str] = mapped_column(sa.String(128), nullable=False) state: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="pending") actor: Mapped[str] = mapped_column(sa.String(256), nullable=False) reason: Mapped[str | None] = mapped_column(sa.String(1000)) expires_at: Mapped[datetime] = mapped_column(sa.DateTime(timezone=True), nullable=False) approved_at: Mapped[datetime | None] = mapped_column(sa.DateTime(timezone=True)) class AuditEventRow(Base): __tablename__ = "audit_events" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id"], ["workspaces.id"], name="fk_audit_events_workspace" ), sa.Index("ix_audit_events_occurred_at_id", "occurred_at", "id"), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) occurred_at: Mapped[datetime] = mapped_column( sa.DateTime(timezone=True), default=utc_now, nullable=False ) trace_id: Mapped[str] = mapped_column(sa.String(128), nullable=False) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID | None] = mapped_column() environment_id: Mapped[UUID | None] = mapped_column() event_type: Mapped[str] = mapped_column(sa.String(128), nullable=False) principal_issuer: Mapped[str | None] = mapped_column(sa.String(512)) principal_subject: Mapped[str | None] = mapped_column(sa.String(256)) client_id: Mapped[UUID | None] = mapped_column() virtual_server_id: Mapped[UUID | None] = mapped_column() tool_name: Mapped[str | None] = mapped_column(sa.String(256)) policy_decision: Mapped[str | None] = mapped_column(sa.String(32)) matched_rule_ids: Mapped[list[str]] = mapped_column(JSON_TYPE, default=list, nullable=False) policy_trace: Mapped[list[dict[str, Any]]] = mapped_column( JSON_TYPE, default=list, nullable=False ) arguments_hash: Mapped[str | None] = mapped_column(sa.String(128)) sanitized_arguments: Mapped[dict[str, Any]] = mapped_column( JSON_TYPE, default=dict, nullable=False ) duration_ms: Mapped[int | None] = mapped_column(sa.Integer) outcome: Mapped[str] = mapped_column(sa.String(32), nullable=False) error_code: Mapped[str | None] = mapped_column(sa.String(128)) retention_until: Mapped[datetime | None] = mapped_column(sa.DateTime(timezone=True)) operation_id: Mapped[UUID | None] = mapped_column() class KnowledgeSourceRow(TimestampMixin, Base): __tablename__ = "knowledge_sources" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id", "project_id"], ["projects.workspace_id", "projects.id"], name="fk_knowledge_sources_project_scope", ), sa.UniqueConstraint("project_id", "slug", name="uq_knowledge_sources_project_slug"), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) slug: Mapped[str] = mapped_column(sa.String(63), nullable=False) source_type: Mapped[str] = mapped_column(sa.String(64), nullable=False) uri: Mapped[str] = mapped_column(sa.String(2048), nullable=False) visibility: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="project") state: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="registered") configuration: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) last_indexed_at: Mapped[datetime | None] = mapped_column(sa.DateTime(timezone=True)) class KnowledgeDocumentRow(TimestampMixin, Base): __tablename__ = "knowledge_documents" __table_args__ = ( sa.ForeignKeyConstraint( ["workspace_id", "project_id"], ["projects.workspace_id", "projects.id"], name="fk_knowledge_documents_project_scope", ), sa.ForeignKeyConstraint( ["source_id"], ["knowledge_sources.id"], name="fk_knowledge_documents_source" ), sa.UniqueConstraint("source_id", "external_ref", name="uq_knowledge_documents_source_ref"), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) source_id: Mapped[UUID] = mapped_column(nullable=False) external_ref: Mapped[str] = mapped_column(sa.String(1024), nullable=False) title: Mapped[str] = mapped_column(sa.String(500), nullable=False) content_hash: Mapped[str] = mapped_column(sa.String(128), nullable=False) document_version: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=1) state: Mapped[str] = mapped_column(sa.String(32), nullable=False, default="registered") content_bytes: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=0) record_metadata: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False) class KnowledgeChunkRow(TimestampMixin, Base): __tablename__ = "knowledge_chunks" __table_args__ = ( sa.ForeignKeyConstraint( ["document_id"], ["knowledge_documents.id"], name="fk_knowledge_chunks_document" ), sa.UniqueConstraint("document_id", "chunk_index", name="uq_knowledge_chunks_index"), ) id: Mapped[UUID] = mapped_column(UUID_TYPE, primary_key=True, default=uuid4) workspace_id: Mapped[UUID] = mapped_column(nullable=False) project_id: Mapped[UUID] = mapped_column(nullable=False) document_id: Mapped[UUID] = mapped_column(nullable=False) chunk_index: Mapped[int] = mapped_column(sa.Integer, nullable=False) text: Mapped[str] = mapped_column(sa.Text, nullable=False) content_hash: Mapped[str] = mapped_column(sa.String(128), nullable=False) token_count: Mapped[int] = mapped_column(sa.Integer, nullable=False, default=0) record_metadata: Mapped[dict[str, Any]] = mapped_column(JSON_TYPE, default=dict, nullable=False)