diff --git a/surfsense_backend/alembic/versions/85_add_public_chat_snapshots_table.py b/surfsense_backend/alembic/versions/85_add_public_chat_snapshots_table.py index 60c31f039..510b40959 100644 --- a/surfsense_backend/alembic/versions/85_add_public_chat_snapshots_table.py +++ b/surfsense_backend/alembic/versions/85_add_public_chat_snapshots_table.py @@ -103,7 +103,9 @@ def upgrade() -> None: # 5. Drop deprecated columns from new_chat_threads op.execute("ALTER TABLE new_chat_threads DROP COLUMN IF EXISTS clone_pending") - 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") # 6. Add cloned_from_snapshot_id to new_chat_threads @@ -129,7 +131,9 @@ def downgrade() -> None: # 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") + 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( diff --git a/surfsense_backend/app/db.py b/surfsense_backend/app/db.py index 97d15d90f..56e39c2e7 100644 --- a/surfsense_backend/app/db.py +++ b/surfsense_backend/app/db.py @@ -546,7 +546,9 @@ class PublicChatSnapshot(BaseModel, TimestampMixin): # Constraints __table_args__ = ( # Prevent duplicate snapshots of the same content for the same thread - UniqueConstraint("thread_id", "content_hash", name="uq_snapshot_thread_content_hash"), + UniqueConstraint( + "thread_id", "content_hash", name="uq_snapshot_thread_content_hash" + ), ) diff --git a/surfsense_backend/app/routes/new_chat_routes.py b/surfsense_backend/app/routes/new_chat_routes.py index 5e1bc238f..e419be7f7 100644 --- a/surfsense_backend/app/routes/new_chat_routes.py +++ b/surfsense_backend/app/routes/new_chat_routes.py @@ -1273,7 +1273,7 @@ async def regenerate_response( .limit(2) ) messages_to_delete = list(last_messages_result.scalars().all()) - + message_ids_to_delete = [msg.id for msg in messages_to_delete] # Get search space for LLM config diff --git a/surfsense_backend/app/routes/new_llm_config_routes.py b/surfsense_backend/app/routes/new_llm_config_routes.py index b86c37199..ed7d62d31 100644 --- a/surfsense_backend/app/routes/new_llm_config_routes.py +++ b/surfsense_backend/app/routes/new_llm_config_routes.py @@ -61,21 +61,23 @@ async def get_global_new_llm_configs( # Only include Auto mode if there are actual global configs to route to # Auto mode requires at least one global config with valid API key if global_configs and len(global_configs) > 0: - safe_configs.append({ - "id": 0, - "name": "Auto (Load Balanced)", - "description": "Automatically routes requests across available LLM providers for optimal performance and rate limit handling. Recommended for most users.", - "provider": "AUTO", - "custom_provider": None, - "model_name": "auto", - "api_base": None, - "litellm_params": {}, - "system_instructions": "", - "use_default_system_instructions": True, - "citations_enabled": True, - "is_global": True, - "is_auto_mode": True, - }) + safe_configs.append( + { + "id": 0, + "name": "Auto (Load Balanced)", + "description": "Automatically routes requests across available LLM providers for optimal performance and rate limit handling. Recommended for most users.", + "provider": "AUTO", + "custom_provider": None, + "model_name": "auto", + "api_base": None, + "litellm_params": {}, + "system_instructions": "", + "use_default_system_instructions": True, + "citations_enabled": True, + "is_global": True, + "is_auto_mode": True, + } + ) # Add individual global configs for cfg in global_configs: diff --git a/surfsense_backend/app/routes/notifications_routes.py b/surfsense_backend/app/routes/notifications_routes.py index 83af87e2d..e8e89e6c4 100644 --- a/surfsense_backend/app/routes/notifications_routes.py +++ b/surfsense_backend/app/routes/notifications_routes.py @@ -22,7 +22,9 @@ router = APIRouter(prefix="/notifications", tags=["notifications"]) SYNC_WINDOW_DAYS = 14 # Valid notification types - must match frontend InboxItemTypeEnum -NotificationType = Literal["connector_indexing", "document_processing", "new_mention", "page_limit_exceeded"] +NotificationType = Literal[ + "connector_indexing", "document_processing", "new_mention", "page_limit_exceeded" +] class NotificationResponse(BaseModel): diff --git a/surfsense_backend/app/services/notification_service.py b/surfsense_backend/app/services/notification_service.py index 8ca4c2e6f..1788d05e1 100644 --- a/surfsense_backend/app/services/notification_service.py +++ b/surfsense_backend/app/services/notification_service.py @@ -867,9 +867,7 @@ class PageLimitNotificationHandler(BaseNotificationHandler): def __init__(self): super().__init__("page_limit_exceeded") - def _generate_operation_id( - self, document_name: str, search_space_id: int - ) -> str: + def _generate_operation_id(self, document_name: str, search_space_id: int) -> str: """ Generate a unique operation ID for a page limit exceeded notification. @@ -915,9 +913,11 @@ class PageLimitNotificationHandler(BaseNotificationHandler): Notification: The created notification """ operation_id = self._generate_operation_id(document_name, search_space_id) - + # Truncate document name for title if too long - display_name = document_name[:40] + "..." if len(document_name) > 40 else document_name + display_name = ( + document_name[:40] + "..." if len(document_name) > 40 else document_name + ) title = f"Page limit exceeded: {display_name}" message = f"This document has ~{pages_to_add} page(s) but you've used {pages_used}/{pages_limit} pages. Upgrade to process more documents." diff --git a/surfsense_backend/app/services/public_chat_service.py b/surfsense_backend/app/services/public_chat_service.py index 5e8580642..21c87ad29 100644 --- a/surfsense_backend/app/services/public_chat_service.py +++ b/surfsense_backend/app/services/public_chat_service.py @@ -8,6 +8,7 @@ Key concepts: - Single-phase clone reads directly from snapshot_data """ +import contextlib import hashlib import json import re @@ -213,7 +214,6 @@ async def create_snapshot( # Update status to "ready" so frontend renders PodcastPlayer part["result"] = {**result_data, "status": "ready"} - messages_data.append( { "id": msg.id, @@ -314,9 +314,7 @@ async def get_snapshot_by_token( ) -> PublicChatSnapshot | None: """Get a snapshot by its share token.""" result = await session.execute( - select(PublicChatSnapshot).filter( - PublicChatSnapshot.share_token == share_token - ) + select(PublicChatSnapshot).filter(PublicChatSnapshot.share_token == share_token) ) return result.scalars().first() @@ -426,7 +424,7 @@ async def delete_snapshot( async def delete_affected_snapshots( - session: AsyncSession, # noqa: ARG001 - kept for API compatibility + session: AsyncSession, thread_id: int, message_ids: list[int], ) -> int: @@ -538,10 +536,8 @@ async def clone_from_snapshot( author_ids_from_snapshot: set[UUID] = set() for msg_data in messages_data: if author_str := msg_data.get("author_id"): - try: + with contextlib.suppress(ValueError, TypeError): author_ids_from_snapshot.add(UUID(author_str)) - except (ValueError, TypeError): - pass existing_authors: set[UUID] = set() if author_ids_from_snapshot: diff --git a/surfsense_backend/app/tasks/celery_tasks/document_tasks.py b/surfsense_backend/app/tasks/celery_tasks/document_tasks.py index c811140f5..f21ff5a30 100644 --- a/surfsense_backend/app/tasks/celery_tasks/document_tasks.py +++ b/surfsense_backend/app/tasks/celery_tasks/document_tasks.py @@ -418,7 +418,11 @@ async def _process_file_upload( page_limit_error: PageLimitExceededError | None = None if isinstance(e, PageLimitExceededError): page_limit_error = e - elif isinstance(e, HTTPException) and e.__cause__ and isinstance(e.__cause__, PageLimitExceededError): + elif ( + isinstance(e, HTTPException) + and e.__cause__ + and isinstance(e.__cause__, PageLimitExceededError) + ): # HTTPException wraps the original PageLimitExceededError page_limit_error = e.__cause__ elif isinstance(e, HTTPException) and "page limit" in str(e.detail).lower(): @@ -432,14 +436,12 @@ async def _process_file_upload( try: # First, mark the processing notification as failed await session.refresh(notification) - await ( - NotificationService.document_processing.notify_processing_completed( - session=session, - notification=notification, - error_message="Page limit exceeded", - ) + await NotificationService.document_processing.notify_processing_completed( + session=session, + notification=notification, + error_message="Page limit exceeded", ) - + # Then create a separate page_limit_exceeded notification for better UX await NotificationService.page_limit.notify_page_limit_exceeded( session=session, @@ -460,12 +462,10 @@ async def _process_file_upload( error_message = str(e.detail) try: await session.refresh(notification) - await ( - NotificationService.document_processing.notify_processing_completed( - session=session, - notification=notification, - error_message=error_message, - ) + await NotificationService.document_processing.notify_processing_completed( + session=session, + notification=notification, + error_message=error_message, ) except Exception as notif_error: logger.error( @@ -477,12 +477,10 @@ async def _process_file_upload( try: # Refresh notification to ensure it's not stale after any rollback await session.refresh(notification) - await ( - NotificationService.document_processing.notify_processing_completed( - session=session, - notification=notification, - error_message=error_message, - ) + await NotificationService.document_processing.notify_processing_completed( + session=session, + notification=notification, + error_message=error_message, ) except Exception as notif_error: logger.error( diff --git a/surfsense_web/components/assistant-ui/connector-popup.tsx b/surfsense_web/components/assistant-ui/connector-popup.tsx index e838dd02e..9b201e96b 100644 --- a/surfsense_web/components/assistant-ui/connector-popup.tsx +++ b/surfsense_web/components/assistant-ui/connector-popup.tsx @@ -389,7 +389,9 @@ export const ConnectorIndicator: FC = () => { onConnectOAuth={hasDocumentSummaryLLM ? handleConnectOAuth : () => {}} onConnectNonOAuth={hasDocumentSummaryLLM ? handleConnectNonOAuth : () => {}} onCreateWebcrawler={hasDocumentSummaryLLM ? handleCreateWebcrawler : () => {}} - onCreateYouTubeCrawler={hasDocumentSummaryLLM ? handleCreateYouTubeCrawler : () => {}} + onCreateYouTubeCrawler={ + hasDocumentSummaryLLM ? handleCreateYouTubeCrawler : () => {} + } onManage={handleStartEdit} onViewAccountsList={handleViewAccountsList} /> diff --git a/surfsense_web/lib/apis/connectors-api.service.ts b/surfsense_web/lib/apis/connectors-api.service.ts index 45898b762..4a6d67d80 100644 --- a/surfsense_web/lib/apis/connectors-api.service.ts +++ b/surfsense_web/lib/apis/connectors-api.service.ts @@ -3,9 +3,9 @@ import { createConnectorRequest, createConnectorResponse, type DeleteConnectorRequest, + type DiscordChannel, deleteConnectorRequest, deleteConnectorResponse, - type DiscordChannel, type GetConnectorRequest, type GetConnectorsRequest, getConnectorRequest,