1
This commit is contained in:
@@ -91,6 +91,8 @@ class ChatGenerationTask(Base, TimestampMixin, SoftDeleteMixin):
|
||||
)
|
||||
generation_count: Mapped[int] = mapped_column(Integer, default=1, server_default="1", nullable=False)
|
||||
generation_index: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||
generation_attempt_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1")
|
||||
resource_generation_started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
|
||||
# 图片主任务同步调用供应商时的分布式执行租约。
|
||||
# 防止重复 Celery 消息或恢复任务同时触发多次组图请求。
|
||||
@@ -118,7 +120,10 @@ class ChatGenerationTask(Base, TimestampMixin, SoftDeleteMixin):
|
||||
video_tokens_used: Mapped[int] = mapped_column(Integer, default=0)
|
||||
image_tokens_used: Mapped[int] = mapped_column(Integer, default=0)
|
||||
|
||||
# retry_count is retained as a compatibility mirror of manual retries.
|
||||
retry_count: Mapped[int] = mapped_column(Integer, default=0)
|
||||
manual_retry_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
|
||||
poll_error_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
|
||||
poll_count: Mapped[int] = mapped_column(Integer, default=0)
|
||||
last_poll_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
# 视频降频轮询调度字段。
|
||||
@@ -126,6 +131,8 @@ class ChatGenerationTask(Base, TimestampMixin, SoftDeleteMixin):
|
||||
poll_started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
next_poll_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
poll_interval_seconds: Mapped[int] = mapped_column(Integer, default=0)
|
||||
poll_claim_token: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
poll_lease_until: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
deadline_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
generated_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
@@ -135,6 +142,7 @@ class ChatGenerationTask(Base, TimestampMixin, SoftDeleteMixin):
|
||||
download_celery_task_id: Mapped[str | None] = mapped_column(String(160), nullable=True, index=True)
|
||||
download_enqueued_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
download_started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
download_claim_token: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
download_lease_until: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
download_next_retry_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
download_attempt_count: Mapped[int] = mapped_column(Integer, default=0)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from sqlalchemy import DateTime, ForeignKey, String, Text, func
|
||||
from sqlalchemy import CheckConstraint, DateTime, ForeignKey, Index, Integer, String, Text, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.models.base import Base
|
||||
@@ -6,20 +6,31 @@ from app.utils.id_gen import generate_id
|
||||
|
||||
|
||||
class ChatGenerationTaskEvent(Base):
|
||||
"""Append-only event log for project-independent chat generation tasks."""
|
||||
"""Append-only event log shared by ChatGenerationTask and GenerationRecord."""
|
||||
|
||||
__tablename__ = "chat_generation_task_events"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"(owner_type = 'chat_generation_task' AND task_id IS NOT NULL AND generation_record_id IS NULL) "
|
||||
"OR (owner_type = 'generation_record' AND task_id IS NULL AND generation_record_id IS NOT NULL)",
|
||||
name="ck_chat_generation_task_events_owner",
|
||||
),
|
||||
Index("idx_chat_generation_task_events_task_created", "owner_type", "task_id", "created_at"),
|
||||
Index("idx_chat_generation_task_events_record_created", "owner_type", "generation_record_id", "created_at"),
|
||||
Index("idx_chat_generation_task_events_attempt_created", "owner_type", "generation_attempt_no", "created_at"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=generate_id)
|
||||
task_id: Mapped[str] = mapped_column(
|
||||
String(32), ForeignKey("chat_generation_tasks.id", ondelete="CASCADE"), index=True
|
||||
)
|
||||
owner_type: Mapped[str] = mapped_column(String(32), nullable=False, default="chat_generation_task", server_default="chat_generation_task", index=True)
|
||||
task_id: Mapped[str | None] = mapped_column(String(32), ForeignKey("chat_generation_tasks.id", ondelete="CASCADE"), nullable=True, index=True)
|
||||
generation_record_id: Mapped[str | None] = mapped_column(String(32), ForeignKey("generation_records.id", ondelete="CASCADE"), nullable=True, index=True)
|
||||
generation_attempt_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1", index=True)
|
||||
generation_mode: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
|
||||
event_type: Mapped[str] = mapped_column(String(64), index=True)
|
||||
from_status: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
||||
to_status: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
||||
from_stage: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
||||
to_stage: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
||||
message: Mapped[str | None] = mapped_column(String(512), nullable=True)
|
||||
from_stage: Mapped[str | None] = mapped_column(String(48), nullable=True)
|
||||
to_stage: Mapped[str | None] = mapped_column(String(48), nullable=True)
|
||||
message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
detail_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
created_at = mapped_column(DateTime(timezone=True), server_default=func.now(), index=True)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, func
|
||||
from sqlalchemy import CheckConstraint, DateTime, ForeignKey, Index, Integer, String, Text, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.models.base import Base
|
||||
@@ -6,14 +6,25 @@ from app.utils.id_gen import generate_id
|
||||
|
||||
|
||||
class ChatProviderCallLog(Base):
|
||||
"""Provider call audit log for chat_generation_tasks."""
|
||||
"""Provider call audit log shared by both generation owner models."""
|
||||
|
||||
__tablename__ = "chat_provider_call_logs"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"(owner_type = 'chat_generation_task' AND task_id IS NOT NULL AND generation_record_id IS NULL) "
|
||||
"OR (owner_type = 'generation_record' AND task_id IS NULL AND generation_record_id IS NOT NULL)",
|
||||
name="ck_chat_provider_call_logs_owner",
|
||||
),
|
||||
Index("idx_chat_provider_call_logs_task_created", "owner_type", "task_id", "created_at"),
|
||||
Index("idx_chat_provider_call_logs_record_created", "owner_type", "generation_record_id", "created_at"),
|
||||
Index("idx_chat_provider_call_logs_attempt_created", "owner_type", "generation_attempt_no", "created_at"),
|
||||
)
|
||||
|
||||
id: Mapped[str] = mapped_column(String(32), primary_key=True, default=generate_id)
|
||||
task_id: Mapped[str] = mapped_column(
|
||||
String(32), ForeignKey("chat_generation_tasks.id", ondelete="CASCADE"), index=True
|
||||
)
|
||||
owner_type: Mapped[str] = mapped_column(String(32), nullable=False, default="chat_generation_task", server_default="chat_generation_task", index=True)
|
||||
task_id: Mapped[str | None] = mapped_column(String(32), ForeignKey("chat_generation_tasks.id", ondelete="CASCADE"), nullable=True, index=True)
|
||||
generation_record_id: Mapped[str | None] = mapped_column(String(32), ForeignKey("generation_records.id", ondelete="CASCADE"), nullable=True, index=True)
|
||||
generation_attempt_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1", index=True)
|
||||
generation_mode: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
|
||||
provider: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
api_type: Mapped[str] = mapped_column(String(64), index=True)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from datetime import date, datetime
|
||||
|
||||
from sqlalchemy import BigInteger, Date, DateTime, ForeignKey, Index, String, Text
|
||||
from sqlalchemy import BigInteger, Date, DateTime, ForeignKey, Index, String, Text, text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.models.base import Base, TimestampMixin, SoftDeleteMixin
|
||||
@@ -44,4 +44,12 @@ class GeneratedResource(Base, TimestampMixin, SoftDeleteMixin):
|
||||
|
||||
Index("ix_generated_resources_user_month", GeneratedResource.user_id, GeneratedResource.resource_month)
|
||||
Index("ix_generated_resources_source", GeneratedResource.source_model, GeneratedResource.source_id)
|
||||
Index(
|
||||
"uq_generated_resources_active_source_type",
|
||||
GeneratedResource.source_model,
|
||||
GeneratedResource.source_id,
|
||||
GeneratedResource.resource_type,
|
||||
unique=True,
|
||||
postgresql_where=text("deleted_at IS NULL"),
|
||||
)
|
||||
Index("ix_generated_resources_active_user", GeneratedResource.user_id, GeneratedResource.deleted_at)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text, Float, Index
|
||||
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text, Float, Index, text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.models.base import Base, TimestampMixin, SoftDeleteMixin
|
||||
@@ -52,7 +52,49 @@ class GenerationRecord(Base, TimestampMixin, SoftDeleteMixin):
|
||||
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
idempotency_key: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
|
||||
# Unified Celery generation pipeline state.
|
||||
generation_attempt_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1, server_default="1")
|
||||
resource_generation_started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
deadline_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
|
||||
engine_id: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True)
|
||||
engine_snapshot_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
provider_response_json: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
remote_result_url: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
||||
provider_create_claim_token: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
provider_create_lease_until: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
provider_create_started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
# retry_count is retained for API/backward compatibility. New business
|
||||
# logic uses manual_retry_count and poll_error_count separately.
|
||||
retry_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
|
||||
manual_retry_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
|
||||
poll_error_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
|
||||
poll_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
|
||||
last_poll_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
poll_started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
next_poll_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
poll_interval_seconds: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
|
||||
poll_claim_token: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
poll_lease_until: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
|
||||
download_celery_task_id: Mapped[str | None] = mapped_column(String(160), nullable=True, index=True)
|
||||
download_enqueued_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
download_started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
download_claim_token: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
download_lease_until: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
download_next_retry_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
||||
download_attempt_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
|
||||
download_last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
download_storage_date_dir: Mapped[str | None] = mapped_column(String(16), nullable=True)
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_genrec_user_status_created', 'user_id', 'status', 'created_at'),
|
||||
Index('idx_genrec_project_status', 'project_id', 'status'),
|
||||
Index(
|
||||
'idx_genrec_next_poll_at',
|
||||
'next_poll_at',
|
||||
postgresql_where=text("deleted_at IS NULL AND status = 'generating' AND gen_type = 'video' AND next_poll_at IS NOT NULL"),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from sqlalchemy import Boolean, CheckConstraint, Integer, String, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.models.base import Base, TimestampMixin
|
||||
from app.models.base import Base, TimestampMixin, SoftDeleteMixin
|
||||
|
||||
|
||||
class ImageEngine(Base, TimestampMixin):
|
||||
class ImageEngine(Base, TimestampMixin, SoftDeleteMixin):
|
||||
__tablename__ = "image_engines"
|
||||
__table_args__ = (
|
||||
CheckConstraint("max_generation_count BETWEEN 1 AND 5", name="ck_image_engines_max_generation_count"),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from sqlalchemy import Boolean, Float, Integer, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.models.base import Base, TimestampMixin
|
||||
from app.models.base import Base, TimestampMixin, SoftDeleteMixin
|
||||
|
||||
|
||||
class ModelConfig(Base, TimestampMixin):
|
||||
class ModelConfig(Base, TimestampMixin, SoftDeleteMixin):
|
||||
__tablename__ = "model_configs"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(32), primary_key=True)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from sqlalchemy import Boolean, CheckConstraint, Integer, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.models.base import Base, TimestampMixin
|
||||
from app.models.base import Base, TimestampMixin, SoftDeleteMixin
|
||||
|
||||
|
||||
class VideoEngine(Base, TimestampMixin):
|
||||
class VideoEngine(Base, TimestampMixin, SoftDeleteMixin):
|
||||
__tablename__ = "video_engines"
|
||||
__table_args__ = (
|
||||
CheckConstraint("max_generation_count BETWEEN 1 AND 5", name="ck_video_engines_max_generation_count"),
|
||||
|
||||
Reference in New Issue
Block a user