refactor: add snapshot endpoints, remove deprecated clone/share endpoints

This commit is contained in:
CREDO23 2026-01-29 20:43:48 +02:00
parent e7242be763
commit 005ceaa2e8

View file

@ -37,7 +37,6 @@ from app.db import (
get_async_session, get_async_session,
) )
from app.schemas.new_chat import ( from app.schemas.new_chat import (
CompleteCloneResponse,
NewChatMessageAppend, NewChatMessageAppend,
NewChatMessageRead, NewChatMessageRead,
NewChatRequest, NewChatRequest,
@ -46,14 +45,13 @@ from app.schemas.new_chat import (
NewChatThreadUpdate, NewChatThreadUpdate,
NewChatThreadVisibilityUpdate, NewChatThreadVisibilityUpdate,
NewChatThreadWithMessages, NewChatThreadWithMessages,
PublicShareToggleRequest,
PublicShareToggleResponse,
RegenerateRequest, RegenerateRequest,
SnapshotCreateResponse,
SnapshotListResponse,
ThreadHistoryLoadResponse, ThreadHistoryLoadResponse,
ThreadListItem, ThreadListItem,
ThreadListResponse, ThreadListResponse,
) )
from app.services.public_chat_service import toggle_public_share
from app.tasks.chat.stream_new_chat import stream_new_chat from app.tasks.chat.stream_new_chat import stream_new_chat
from app.users import current_active_user from app.users import current_active_user
from app.utils.rbac import check_permission from app.utils.rbac import check_permission
@ -670,66 +668,6 @@ async def delete_thread(
) from None ) from None
@router.post(
"/threads/{thread_id}/complete-clone", response_model=CompleteCloneResponse
)
async def complete_clone(
thread_id: int,
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
):
"""
Complete the cloning process for a thread.
Copies messages and podcasts from the source thread.
Sets clone_pending=False and needs_history_bootstrap=True when done.
Requires authentication and ownership of the thread.
"""
from app.services.public_chat_service import complete_clone_content
try:
result = await session.execute(
select(NewChatThread).filter(NewChatThread.id == thread_id)
)
thread = result.scalars().first()
if not thread:
raise HTTPException(status_code=404, detail="Thread not found")
if thread.created_by_id != user.id:
raise HTTPException(status_code=403, detail="Not authorized")
if not thread.clone_pending:
raise HTTPException(status_code=400, detail="Clone already completed")
if not thread.cloned_from_thread_id:
raise HTTPException(
status_code=400, detail="No source thread to clone from"
)
message_count = await complete_clone_content(
session=session,
target_thread=thread,
source_thread_id=thread.cloned_from_thread_id,
target_search_space_id=thread.search_space_id,
)
return CompleteCloneResponse(
status="success",
message_count=message_count,
)
except HTTPException:
raise
except Exception as e:
await session.rollback()
raise HTTPException(
status_code=500,
detail=f"An unexpected error occurred while completing clone: {e!s}",
) from None
@router.patch("/threads/{thread_id}/visibility", response_model=NewChatThreadRead) @router.patch("/threads/{thread_id}/visibility", response_model=NewChatThreadRead)
async def update_thread_visibility( async def update_thread_visibility(
thread_id: int, thread_id: int,
@ -795,32 +733,83 @@ async def update_thread_visibility(
) from None ) from None
@router.patch( # =============================================================================
"/threads/{thread_id}/public-share", response_model=PublicShareToggleResponse # Snapshot Endpoints
) # =============================================================================
async def update_thread_public_share(
@router.post("/threads/{thread_id}/snapshots", response_model=SnapshotCreateResponse)
async def create_thread_snapshot(
thread_id: int, thread_id: int,
request: Request, request: Request,
toggle_request: PublicShareToggleRequest,
session: AsyncSession = Depends(get_async_session), session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user), user: User = Depends(current_active_user),
): ):
""" """
Enable or disable public sharing for a thread. Create a public snapshot of the thread.
Only the creator of the thread can manage public sharing. Returns existing snapshot URL if content unchanged (deduplication).
When enabled, returns a public URL that anyone can use to view the chat. Only the thread owner can create snapshots.
""" """
from app.services.public_chat_service import create_snapshot
base_url = str(request.base_url).rstrip("/") base_url = str(request.base_url).rstrip("/")
return await toggle_public_share( return await create_snapshot(
session=session, session=session,
thread_id=thread_id, thread_id=thread_id,
enabled=toggle_request.enabled,
user=user, user=user,
base_url=base_url, base_url=base_url,
) )
@router.get("/threads/{thread_id}/snapshots", response_model=SnapshotListResponse)
async def list_thread_snapshots(
thread_id: int,
request: Request,
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
):
"""
List all public snapshots for this thread.
Only the thread owner can view snapshots.
"""
from app.services.public_chat_service import list_snapshots_for_thread
base_url = str(request.base_url).rstrip("/")
return SnapshotListResponse(
snapshots=await list_snapshots_for_thread(
session=session,
thread_id=thread_id,
user=user,
base_url=base_url,
)
)
@router.delete("/threads/{thread_id}/snapshots/{snapshot_id}")
async def delete_thread_snapshot(
thread_id: int,
snapshot_id: int,
session: AsyncSession = Depends(get_async_session),
user: User = Depends(current_active_user),
):
"""
Delete a specific snapshot.
Only the thread owner can delete snapshots.
"""
from app.services.public_chat_service import delete_snapshot
await delete_snapshot(
session=session,
thread_id=thread_id,
snapshot_id=snapshot_id,
user=user,
)
return {"message": "Snapshot deleted successfully"}
# ============================================================================= # =============================================================================
# Message Endpoints # Message Endpoints
# ============================================================================= # =============================================================================
@ -1326,9 +1315,21 @@ async def regenerate_response(
# This ensures we don't lose data on streaming failures # This ensures we don't lose data on streaming failures
if streaming_completed and messages_to_delete: if streaming_completed and messages_to_delete:
try: try:
# Get message IDs before deletion for snapshot cleanup
deleted_message_ids = [msg.id for msg in messages_to_delete]
for msg in messages_to_delete: for msg in messages_to_delete:
await session.delete(msg) await session.delete(msg)
await session.commit() await session.commit()
# Delete any public snapshots that contain the modified messages
from app.services.public_chat_service import (
delete_affected_snapshots,
)
await delete_affected_snapshots(
session, thread_id, deleted_message_ids
)
except Exception as cleanup_error: except Exception as cleanup_error:
# Log but don't fail - the new messages are already streamed # Log but don't fail - the new messages are already streamed
print( print(