mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-30 21:59:46 +02:00
feat: track cloned_from_snapshot_id for cloned chats
This commit is contained in:
parent
2ec7050603
commit
bc0fb3cb68
3 changed files with 36 additions and 28 deletions
|
|
@ -11,6 +11,7 @@ Changes:
|
||||||
- public_share_enabled (replaced by snapshot existence)
|
- public_share_enabled (replaced by snapshot existence)
|
||||||
- clone_pending (single-phase clone)
|
- clone_pending (single-phase clone)
|
||||||
3. Drop related indexes
|
3. Drop related indexes
|
||||||
|
4. Add cloned_from_snapshot_id to new_chat_threads (tracks source snapshot for clones)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
@ -105,11 +106,32 @@ def upgrade() -> None:
|
||||||
op.execute("ALTER TABLE new_chat_threads DROP COLUMN IF EXISTS public_share_enabled")
|
op.execute("ALTER TABLE new_chat_threads DROP COLUMN IF EXISTS public_share_enabled")
|
||||||
op.execute("ALTER TABLE new_chat_threads DROP COLUMN IF EXISTS public_share_token")
|
op.execute("ALTER TABLE new_chat_threads DROP COLUMN IF EXISTS public_share_token")
|
||||||
|
|
||||||
|
# 6. Add cloned_from_snapshot_id to new_chat_threads
|
||||||
|
op.execute(
|
||||||
|
"""
|
||||||
|
ALTER TABLE new_chat_threads
|
||||||
|
ADD COLUMN IF NOT EXISTS cloned_from_snapshot_id INTEGER
|
||||||
|
REFERENCES public_chat_snapshots(id) ON DELETE SET NULL;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
op.execute(
|
||||||
|
"""
|
||||||
|
CREATE INDEX IF NOT EXISTS ix_new_chat_threads_cloned_from_snapshot_id
|
||||||
|
ON new_chat_threads(cloned_from_snapshot_id)
|
||||||
|
WHERE cloned_from_snapshot_id IS NOT NULL;
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
"""Restore deprecated columns and drop public_chat_snapshots table."""
|
"""Restore deprecated columns and drop public_chat_snapshots table."""
|
||||||
|
|
||||||
# 1. Restore deprecated columns on new_chat_threads
|
# 1. Drop cloned_from_snapshot_id column and index
|
||||||
|
op.execute("DROP INDEX IF EXISTS ix_new_chat_threads_cloned_from_snapshot_id")
|
||||||
|
op.execute("ALTER TABLE new_chat_threads DROP COLUMN IF EXISTS cloned_from_snapshot_id")
|
||||||
|
|
||||||
|
# 2. Restore deprecated columns on new_chat_threads
|
||||||
op.execute(
|
op.execute(
|
||||||
"""
|
"""
|
||||||
ALTER TABLE new_chat_threads
|
ALTER TABLE new_chat_threads
|
||||||
|
|
|
||||||
|
|
@ -418,6 +418,12 @@ class NewChatThread(BaseModel, TimestampMixin):
|
||||||
nullable=True,
|
nullable=True,
|
||||||
index=True,
|
index=True,
|
||||||
)
|
)
|
||||||
|
cloned_from_snapshot_id = Column(
|
||||||
|
Integer,
|
||||||
|
ForeignKey("public_chat_snapshots.id", ondelete="SET NULL"),
|
||||||
|
nullable=True,
|
||||||
|
index=True,
|
||||||
|
)
|
||||||
cloned_at = Column(
|
cloned_at = Column(
|
||||||
TIMESTAMP(timezone=True),
|
TIMESTAMP(timezone=True),
|
||||||
nullable=True,
|
nullable=True,
|
||||||
|
|
@ -443,6 +449,7 @@ class NewChatThread(BaseModel, TimestampMixin):
|
||||||
"PublicChatSnapshot",
|
"PublicChatSnapshot",
|
||||||
back_populates="thread",
|
back_populates="thread",
|
||||||
cascade="all, delete-orphan",
|
cascade="all, delete-orphan",
|
||||||
|
foreign_keys="[PublicChatSnapshot.thread_id]",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -491,12 +498,6 @@ class PublicChatSnapshot(BaseModel, TimestampMixin):
|
||||||
Each snapshot is a frozen copy of the chat at a specific point in time.
|
Each snapshot is a frozen copy of the chat at a specific point in time.
|
||||||
The snapshot_data JSONB contains all messages and metadata needed to
|
The snapshot_data JSONB contains all messages and metadata needed to
|
||||||
render the public chat without querying the original thread.
|
render the public chat without querying the original thread.
|
||||||
|
|
||||||
Key features:
|
|
||||||
- Immutable: Content never changes after creation
|
|
||||||
- Deduplication: content_hash prevents duplicate snapshots of same state
|
|
||||||
- Cascade delete: Deleted when parent thread is deleted
|
|
||||||
- Message tracking: message_ids array enables cascade delete on message edit
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__tablename__ = "public_chat_snapshots"
|
__tablename__ = "public_chat_snapshots"
|
||||||
|
|
@ -517,36 +518,16 @@ class PublicChatSnapshot(BaseModel, TimestampMixin):
|
||||||
index=True,
|
index=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# SHA-256 hash of message content for deduplication
|
|
||||||
# Same content = same hash = return existing snapshot instead of creating new
|
|
||||||
content_hash = Column(
|
content_hash = Column(
|
||||||
String(64),
|
String(64),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
index=True,
|
index=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Immutable snapshot data - self-contained for rendering
|
|
||||||
# Structure:
|
|
||||||
# {
|
|
||||||
# "version": 1,
|
|
||||||
# "title": "Chat title",
|
|
||||||
# "snapshot_at": "2026-01-29T12:00:00Z",
|
|
||||||
# "author": { "display_name": "...", "avatar_url": "..." },
|
|
||||||
# "messages": [
|
|
||||||
# { "id": 123, "role": "user|assistant", "content": [...], "author": {...}, "created_at": "..." }
|
|
||||||
# ],
|
|
||||||
# "podcasts": [
|
|
||||||
# { "original_id": 456, "title": "...", "transcript": "...", "file_path": "..." }
|
|
||||||
# ]
|
|
||||||
# }
|
|
||||||
snapshot_data = Column(JSONB, nullable=False)
|
snapshot_data = Column(JSONB, nullable=False)
|
||||||
|
|
||||||
# Array of message IDs included in this snapshot
|
|
||||||
# Used for cascade deletion when messages are edited/deleted
|
|
||||||
# GIN index enables fast array overlap queries
|
|
||||||
message_ids = Column(ARRAY(Integer), nullable=False)
|
message_ids = Column(ARRAY(Integer), nullable=False)
|
||||||
|
|
||||||
# Who created this snapshot
|
|
||||||
created_by_user_id = Column(
|
created_by_user_id = Column(
|
||||||
UUID(as_uuid=True),
|
UUID(as_uuid=True),
|
||||||
ForeignKey("user.id", ondelete="SET NULL"),
|
ForeignKey("user.id", ondelete="SET NULL"),
|
||||||
|
|
@ -555,7 +536,11 @@ class PublicChatSnapshot(BaseModel, TimestampMixin):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
thread = relationship("NewChatThread", back_populates="snapshots")
|
thread = relationship(
|
||||||
|
"NewChatThread",
|
||||||
|
back_populates="snapshots",
|
||||||
|
foreign_keys="[PublicChatSnapshot.thread_id]",
|
||||||
|
)
|
||||||
created_by = relationship("User")
|
created_by = relationship("User")
|
||||||
|
|
||||||
# Constraints
|
# Constraints
|
||||||
|
|
|
||||||
|
|
@ -523,6 +523,7 @@ async def clone_from_snapshot(
|
||||||
search_space_id=target_search_space_id,
|
search_space_id=target_search_space_id,
|
||||||
created_by_id=user.id,
|
created_by_id=user.id,
|
||||||
cloned_from_thread_id=snapshot.thread_id,
|
cloned_from_thread_id=snapshot.thread_id,
|
||||||
|
cloned_from_snapshot_id=snapshot.id,
|
||||||
cloned_at=datetime.now(UTC),
|
cloned_at=datetime.now(UTC),
|
||||||
needs_history_bootstrap=True,
|
needs_history_bootstrap=True,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue