feat: add voice co-creation session skeleton

This commit is contained in:
2026-04-19 22:54:48 +08:00
parent 7c85d7a2e7
commit f106f740dd
10 changed files with 1540 additions and 0 deletions

View File

@@ -168,6 +168,95 @@ class GenerationJobEvent(Base):
)
class VoiceSession(Base):
"""Voice co-creation session before it is finalized as a formal story."""
__tablename__ = "voice_sessions"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
user_id: Mapped[str] = mapped_column(
String(255), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True
)
child_profile_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("child_profiles.id", ondelete="SET NULL"), nullable=True, index=True
)
universe_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("story_universes.id", ondelete="SET NULL"), nullable=True, index=True
)
final_story_id: Mapped[int | None] = mapped_column(
Integer, ForeignKey("stories.id", ondelete="SET NULL"), nullable=True, index=True
)
target_mode: Mapped[str] = mapped_column(String(32), nullable=False, default="story")
status: Mapped[str] = mapped_column(String(32), nullable=False, default="draft", index=True)
current_turn_index: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
working_title: Mapped[str | None] = mapped_column(String(255), nullable=True)
story_state: Mapped[dict] = mapped_column(JSON, default=dict)
latest_user_transcript: Mapped[str | None] = mapped_column(Text, nullable=True)
latest_assistant_text: Mapped[str | None] = mapped_column(Text, nullable=True)
last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), index=True
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
class VoiceTurn(Base):
"""One turn of user input and assistant response within a voice session."""
__tablename__ = "voice_turns"
__table_args__ = (
UniqueConstraint("session_id", "turn_index", name="uq_voice_turn_session_turn_index"),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
session_id: Mapped[str] = mapped_column(
String(36), ForeignKey("voice_sessions.id", ondelete="CASCADE"), nullable=False, index=True
)
turn_index: Mapped[int] = mapped_column(Integer, nullable=False)
status: Mapped[str] = mapped_column(String(32), nullable=False, default="received", index=True)
user_audio_path: Mapped[str | None] = mapped_column(String(500), nullable=True)
user_audio_mime_type: Mapped[str | None] = mapped_column(String(100), nullable=True)
user_audio_duration_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
user_transcript: Mapped[str | None] = mapped_column(Text, nullable=True)
transcript_confidence: Mapped[float | None] = mapped_column(Float, nullable=True)
detected_intent: Mapped[str] = mapped_column(String(32), nullable=False, default="unknown")
intent_confidence: Mapped[float | None] = mapped_column(Float, nullable=True)
story_patch: Mapped[dict] = mapped_column(JSON, default=dict)
assistant_text: Mapped[str | None] = mapped_column(Text, nullable=True)
assistant_audio_path: Mapped[str | None] = mapped_column(String(500), nullable=True)
assistant_audio_duration_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), index=True
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
class VoiceSessionEvent(Base):
"""Append-only event emitted by one voice co-creation session."""
__tablename__ = "voice_session_events"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
session_id: Mapped[str] = mapped_column(
String(36), ForeignKey("voice_sessions.id", ondelete="CASCADE"), nullable=False, index=True
)
turn_id: Mapped[str | None] = mapped_column(
String(36), ForeignKey("voice_turns.id", ondelete="SET NULL"), nullable=True, index=True
)
event_type: Mapped[str] = mapped_column(String(64), nullable=False)
status: Mapped[str] = mapped_column(String(32), nullable=False)
message: Mapped[str | None] = mapped_column(Text, nullable=True)
event_metadata: Mapped[dict] = mapped_column(JSON, default=dict)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), index=True
)
class ChildProfile(Base):
"""Child profile entity."""