feat(01-02): create Project, Discussion, Round, Message, Consensus models
- Add Project model: id, name, created/updated_at, models (JSON), settings (JSON) - Add Discussion model: id, project_id (FK), question, type, status, created_at - Add Round model: id, discussion_id (FK), round_number, type - Add Message model: id, round_id (FK), model, content, timestamp, is_direct - Add Consensus model: id, discussion_id (FK unique), agreements, disagreements, generated_at/by - Configure bidirectional relationships with cascade delete-orphan - All FKs reference correct tables, all type hints present
This commit is contained in:
parent
61da27c7d5
commit
a0de94141b
1 changed files with 153 additions and 2 deletions
|
|
@ -8,9 +8,16 @@ All IDs use UUID stored as String(36) for SQLite compatibility.
|
|||
Enums are stored as strings for database portability.
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
from sqlalchemy import JSON, DateTime, ForeignKey, String, Text
|
||||
from sqlalchemy import Enum as SAEnum
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
|
|
@ -38,3 +45,147 @@ class RoundType(str, Enum):
|
|||
|
||||
PARALLEL = "parallel"
|
||||
SEQUENTIAL = "sequential"
|
||||
|
||||
|
||||
def _uuid() -> str:
|
||||
"""Generate a new UUID string."""
|
||||
return str(uuid4())
|
||||
|
||||
|
||||
class Project(Base):
|
||||
"""A project container for related discussions.
|
||||
|
||||
Attributes:
|
||||
id: Unique identifier (UUID).
|
||||
name: Human-readable project name.
|
||||
created_at: When the project was created.
|
||||
updated_at: When the project was last modified.
|
||||
models: List of AI model identifiers (e.g., ["claude", "gpt", "gemini"]).
|
||||
settings: Configuration dict (default_rounds, consensus_threshold, system_prompt_override).
|
||||
discussions: Related discussions in this project.
|
||||
"""
|
||||
|
||||
__tablename__ = "project"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
|
||||
name: Mapped[str] = mapped_column(String(255))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
|
||||
)
|
||||
models: Mapped[Any] = mapped_column(JSON, default=list)
|
||||
settings: Mapped[Any] = mapped_column(JSON, default=dict)
|
||||
|
||||
discussions: Mapped[list[Discussion]] = relationship(
|
||||
back_populates="project", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
|
||||
class Discussion(Base):
|
||||
"""A discussion within a project.
|
||||
|
||||
Attributes:
|
||||
id: Unique identifier (UUID).
|
||||
project_id: FK to parent project.
|
||||
question: The question or topic being discussed.
|
||||
type: Whether this is an "open" or "discuss" mode discussion.
|
||||
status: Current status (active or completed).
|
||||
created_at: When the discussion started.
|
||||
project: Parent project relationship.
|
||||
rounds: Discussion rounds.
|
||||
consensus: Generated consensus (if any).
|
||||
"""
|
||||
|
||||
__tablename__ = "discussion"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
|
||||
project_id: Mapped[str] = mapped_column(ForeignKey("project.id"))
|
||||
question: Mapped[str] = mapped_column(Text)
|
||||
type: Mapped[DiscussionType] = mapped_column(SAEnum(DiscussionType))
|
||||
status: Mapped[DiscussionStatus] = mapped_column(
|
||||
SAEnum(DiscussionStatus), default=DiscussionStatus.ACTIVE
|
||||
)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
||||
|
||||
project: Mapped[Project] = relationship(back_populates="discussions")
|
||||
rounds: Mapped[list[Round]] = relationship(
|
||||
back_populates="discussion", cascade="all, delete-orphan"
|
||||
)
|
||||
consensus: Mapped[Consensus | None] = relationship(
|
||||
back_populates="discussion", uselist=False, cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
|
||||
class Round(Base):
|
||||
"""A round within a discussion.
|
||||
|
||||
Attributes:
|
||||
id: Unique identifier (UUID).
|
||||
discussion_id: FK to parent discussion.
|
||||
round_number: Sequential round number within the discussion.
|
||||
type: Whether this round is parallel or sequential.
|
||||
discussion: Parent discussion relationship.
|
||||
messages: Messages from AI models in this round.
|
||||
"""
|
||||
|
||||
__tablename__ = "round"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
|
||||
discussion_id: Mapped[str] = mapped_column(ForeignKey("discussion.id"))
|
||||
round_number: Mapped[int]
|
||||
type: Mapped[RoundType] = mapped_column(SAEnum(RoundType))
|
||||
|
||||
discussion: Mapped[Discussion] = relationship(back_populates="rounds")
|
||||
messages: Mapped[list[Message]] = relationship(
|
||||
back_populates="round", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
|
||||
class Message(Base):
|
||||
"""A message from an AI model within a round.
|
||||
|
||||
Attributes:
|
||||
id: Unique identifier (UUID).
|
||||
round_id: FK to parent round.
|
||||
model: AI model identifier (e.g., "claude", "gpt", "gemini").
|
||||
content: The message content.
|
||||
timestamp: When the message was created.
|
||||
is_direct: True if this was a direct @mention to this model.
|
||||
round: Parent round relationship.
|
||||
"""
|
||||
|
||||
__tablename__ = "message"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
|
||||
round_id: Mapped[str] = mapped_column(ForeignKey("round.id"))
|
||||
model: Mapped[str] = mapped_column(String(50))
|
||||
content: Mapped[str] = mapped_column(Text)
|
||||
timestamp: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
||||
is_direct: Mapped[bool] = mapped_column(default=False)
|
||||
|
||||
round: Mapped[Round] = relationship(back_populates="messages")
|
||||
|
||||
|
||||
class Consensus(Base):
|
||||
"""Generated consensus summary for a discussion.
|
||||
|
||||
Attributes:
|
||||
id: Unique identifier (UUID).
|
||||
discussion_id: FK to parent discussion (unique - one consensus per discussion).
|
||||
agreements: List of bullet point strings for agreed items.
|
||||
disagreements: List of {topic, positions: {model: position}} dicts.
|
||||
generated_at: When the consensus was generated.
|
||||
generated_by: Which model generated this summary.
|
||||
discussion: Parent discussion relationship.
|
||||
"""
|
||||
|
||||
__tablename__ = "consensus"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
|
||||
discussion_id: Mapped[str] = mapped_column(ForeignKey("discussion.id"), unique=True)
|
||||
agreements: Mapped[Any] = mapped_column(JSON, default=list)
|
||||
disagreements: Mapped[Any] = mapped_column(JSON, default=list)
|
||||
generated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
||||
generated_by: Mapped[str] = mapped_column(String(50))
|
||||
|
||||
discussion: Mapped[Discussion] = relationship(back_populates="consensus")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue