Spaces:
Sleeping
Sleeping
File size: 6,611 Bytes
3493993 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | from __future__ import annotations
from datetime import datetime, timezone
from uuid import uuid4
from sqlalchemy import (
DateTime,
ForeignKey,
Index,
Integer,
JSON,
String,
Text,
UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column
from app.security.models import Base
def utcnow() -> datetime:
return datetime.now(timezone.utc)
def new_id() -> str:
return str(uuid4())
class GenerationRequest(Base):
"""Immutable validated intent, scoped to one authoritative workspace."""
__tablename__ = "generation_requests"
__table_args__ = (
UniqueConstraint(
"workspace_id", "idempotency_key", name="uq_generation_request_workspace_idempotency"
),
Index("ix_generation_requests_workspace_created", "workspace_id", "created_at"),
Index("ix_generation_requests_workspace_status", "workspace_id", "status"),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id)
workspace_id: Mapped[str] = mapped_column(
String(36), ForeignKey("workspaces.id", ondelete="RESTRICT"), nullable=False
)
created_by_user_id: Mapped[str] = mapped_column(
String(36), ForeignKey("users.id", ondelete="RESTRICT"), nullable=False
)
provider: Mapped[str] = mapped_column(String(64), nullable=False)
model_id: Mapped[str] = mapped_column(String(255), nullable=False)
modality: Mapped[str] = mapped_column(String(32), nullable=False)
input_asset_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("media_assets.id", ondelete="RESTRICT")
)
project_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("projects.id", ondelete="RESTRICT")
)
product_surface: Mapped[str] = mapped_column(String(32), nullable=False, default="generation")
# This JSON contains only adapter-validated, non-secret input. Its
# request fingerprint is authoritative for idempotency conflict checks.
spec_json: Mapped[dict[str, object]] = mapped_column("spec", JSON, nullable=False, default=dict)
request_fingerprint: Mapped[str] = mapped_column(String(64), nullable=False)
idempotency_key: Mapped[str] = mapped_column(String(255), nullable=False)
status: Mapped[str] = mapped_column(String(32), nullable=False, default="queued")
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, default=utcnow
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, default=utcnow, onupdate=utcnow
)
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
class GenerationJob(Base):
"""One durable execution for one request; retries keep this identity."""
__tablename__ = "generation_jobs"
__table_args__ = (
UniqueConstraint("generation_request_id", name="uq_generation_job_request"),
# An external worker job is an opaque provider identity, not a
# workspace-scoped client value. Binding it once prevents a worker
# status/output from being attached to another tenant's logical job.
UniqueConstraint("provider", "external_job_id", name="uq_generation_job_provider_external"),
Index("ix_generation_jobs_workspace_status", "workspace_id", "status"),
Index("ix_generation_jobs_next_attempt", "status", "next_attempt_at"),
Index("ix_generation_jobs_external", "provider", "external_job_id"),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id)
generation_request_id: Mapped[str] = mapped_column(
String(36),
ForeignKey("generation_requests.id", ondelete="CASCADE"),
nullable=False,
)
workspace_id: Mapped[str] = mapped_column(
String(36), ForeignKey("workspaces.id", ondelete="RESTRICT"), nullable=False
)
provider: Mapped[str] = mapped_column(String(64), nullable=False)
status: Mapped[str] = mapped_column(String(32), nullable=False, default="queued")
attempt_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
max_attempts: Mapped[int] = mapped_column(Integer, nullable=False, default=3)
next_attempt_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
external_job_id: Mapped[str | None] = mapped_column(String(255))
# Safe metadata only. Bearer-like upload URLs or credentials belong in a
# future secret store, never in this row or externally serialised views.
provider_metadata_json: Mapped[dict[str, object]] = mapped_column(
"provider_metadata", JSON, nullable=False, default=dict
)
output_asset_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("media_assets.id", ondelete="RESTRICT")
)
error_code: Mapped[str | None] = mapped_column(String(100))
error_message: Mapped[str | None] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, default=utcnow
)
started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, default=utcnow, onupdate=utcnow
)
class GenerationJobAttempt(Base):
"""Auditable retry history without creating new logical jobs."""
__tablename__ = "generation_job_attempts"
__table_args__ = (
UniqueConstraint(
"generation_job_id", "attempt_number", name="uq_generation_job_attempt_number"
),
Index("ix_generation_job_attempts_job", "generation_job_id", "attempt_number"),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_id)
generation_job_id: Mapped[str] = mapped_column(
String(36), ForeignKey("generation_jobs.id", ondelete="CASCADE"), nullable=False
)
attempt_number: Mapped[int] = mapped_column(Integer, nullable=False)
status: Mapped[str] = mapped_column(String(32), nullable=False)
brand_kit_version_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
external_job_id: Mapped[str | None] = mapped_column(String(128))
error_message: Mapped[str | None] = mapped_column(Text)
provider_request_id: Mapped[str | None] = mapped_column(String(255))
started_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, default=utcnow
)
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|