diff --git a/surfsense_backend/app/agents/new_chat/tools/notion/update_page.py b/surfsense_backend/app/agents/new_chat/tools/notion/update_page.py index 09507f465..2fbc0a5f3 100644 --- a/surfsense_backend/app/agents/new_chat/tools/notion/update_page.py +++ b/surfsense_backend/app/agents/new_chat/tools/notion/update_page.py @@ -33,19 +33,16 @@ def create_update_notion_page_tool( @tool async def update_notion_page( page_title: str, - new_title: str | None = None, - new_content: str | None = None, + content: str, ) -> dict[str, Any]: - """Update an existing Notion page's title and/or content. + """Update an existing Notion page by appending new content. - Use this tool when the user asks you to modify, edit, or update - a Notion page. At least one of new_title or new_content must be provided. + Use this tool when the user asks you to add content to, modify, or update + a Notion page. The new content will be appended to the existing page content. Args: - page_title: The current title of the Notion page to update (required). - new_title: New title for the page (optional). - new_content: New markdown content for the page body (optional). - If provided, replaces all existing content. + page_title: The title of the Notion page to update. + content: The markdown content to append to the page body (supports headings, lists, paragraphs). Returns: Dictionary with: @@ -54,17 +51,16 @@ def create_update_notion_page_tool( - url: URL to the updated page (if success) - title: Current page title (if success) - message: Result message - + IMPORTANT: If status is "rejected", the user explicitly declined the action. - Respond with a brief acknowledgment (e.g., "Understood, I didn't update the page.") + Respond with a brief acknowledgment (e.g., "Understood, I didn't update the page.") and move on. Do NOT ask for alternatives or troubleshoot. Examples: - - "Update the 'Meeting Notes' page with new title 'Updated Meeting Notes'" - - "Change the content of 'Project Plan' page to 'New content here'" - - "Update 'Weekly Report' with new title 'Final Report' and content '# Summary...'" + - "Add 'New meeting notes from today' to the 'Meeting Notes' page" + - "Append the following to 'Project Plan': '# Status Update\n\nCompleted phase 1'" """ - logger.info(f"update_notion_page called: page_title='{page_title}', new_title={new_title}, has_content={new_content is not None}") + logger.info(f"update_notion_page called: page_title='{page_title}', content_length={len(content) if content else 0}") if db_session is None or search_space_id is None or user_id is None: logger.error("Notion tool not properly configured - missing required parameters") @@ -73,10 +69,11 @@ def create_update_notion_page_tool( "message": "Notion tool not properly configured. Please contact support.", } - if not new_title and not new_content: + if not content or not content.strip(): + logger.error(f"Empty content provided for page '{page_title}'") return { "status": "error", - "message": "At least one of 'new_title' or 'new_content' must be provided to update the page.", + "message": "Content is required to update the page. Please provide the actual content you want to add.", } try: @@ -103,8 +100,7 @@ def create_update_notion_page_tool( "tool": "update_notion_page", "params": { "page_id": page_id, - "title": new_title, - "content": new_content, + "content": content, "connector_id": connector_id_from_context, }, }, @@ -135,19 +131,10 @@ def create_update_notion_page_tool( final_params = edited_action.get("args", {}) if edited_action else {} final_page_id = final_params.get("page_id", page_id) - final_title = final_params.get("title", new_title) - final_content = final_params.get("content", new_content) + final_content = final_params.get("content", content) final_connector_id = final_params.get("connector_id", connector_id_from_context) - # Validate title if it's being updated - if final_title is not None and not final_title.strip(): - logger.error("Title is empty or contains only whitespace") - return { - "status": "error", - "message": "Page title cannot be empty. Please provide a valid title.", - } - - logger.info(f"Updating Notion page with final params: page_id={final_page_id}, title={final_title}, has_content={final_content is not None}") + logger.info(f"Updating Notion page with final params: page_id={final_page_id}, has_content={final_content is not None}") from sqlalchemy.future import select @@ -189,7 +176,6 @@ def create_update_notion_page_tool( result = await notion_connector.update_page( page_id=final_page_id, - title=final_title, content=final_content, ) logger.info(f"update_page result: {result.get('status')} - {result.get('message', '')}") diff --git a/surfsense_backend/app/agents/new_chat/tools/registry.py b/surfsense_backend/app/agents/new_chat/tools/registry.py index 5384cdce0..bcbd5220c 100644 --- a/surfsense_backend/app/agents/new_chat/tools/registry.py +++ b/surfsense_backend/app/agents/new_chat/tools/registry.py @@ -219,7 +219,7 @@ BUILTIN_TOOLS: list[ToolDefinition] = [ ), ToolDefinition( name="update_notion_page", - description="Update an existing Notion page's title or content", + description="Append new content to an existing Notion page", factory=lambda deps: create_update_notion_page_tool( db_session=deps["db_session"], search_space_id=deps["search_space_id"], diff --git a/surfsense_backend/app/connectors/notion_history.py b/surfsense_backend/app/connectors/notion_history.py index 83fcbd037..b6fda8b8b 100644 --- a/surfsense_backend/app/connectors/notion_history.py +++ b/surfsense_backend/app/connectors/notion_history.py @@ -987,15 +987,16 @@ class NotionHistoryConnector: } async def update_page( - self, page_id: str, title: str | None = None, content: str | None = None + self, page_id: str, content: str | None = None ) -> dict[str, Any]: """ - Update an existing Notion page. + Update an existing Notion page by appending new content. + + Note: Content is appended to the page, not replaced. Args: page_id: Page ID to update - title: New page title (optional) - content: New page content in markdown (optional) + content: New markdown content to append to the page (optional) Returns: Dictionary with update result @@ -1006,31 +1007,13 @@ class NotionHistoryConnector: try: notion = await self._get_client() - # Update title if provided - if title: - await self._api_call_with_retry( - notion.pages.update, - page_id=page_id, - properties={ - "title": { - "title": [{"type": "text", "text": {"content": title}}] - } - }, - ) - - # Update content if provided + # Append content if provided if content: - # First, get existing blocks - existing_blocks = await self._api_call_with_retry( - notion.blocks.children.list, - block_id=page_id - ) - - # Convert new content to blocks + # Convert new content to blocks try: children = self._markdown_to_blocks(content) if not children: - logger.warning("No blocks generated from content, skipping update") + logger.warning("No blocks generated from content, skipping append") return { "status": "error", "message": "Content conversion failed: no valid blocks generated", @@ -1042,25 +1025,7 @@ class NotionHistoryConnector: "message": f"Failed to parse content: {e!s}", } - # Store block count for logging - block_count = len(existing_blocks.get("results", [])) - - # Delete existing blocks - try: - for block in existing_blocks.get("results", []): - await self._api_call_with_retry( - notion.blocks.delete, - block_id=block["id"] - ) - logger.info(f"Deleted {block_count} existing blocks from page {page_id}") - except Exception as e: - logger.error(f"Failed to delete existing blocks: {e}") - return { - "status": "error", - "message": f"Failed to clear existing content: {e!s}", - } - - # Add new content (CRITICAL: if this fails, content is lost) Need improvement to handle this better. + # Append new content blocks try: for i in range(0, len(children), 100): batch = children[i : i + 100] @@ -1069,44 +1034,15 @@ class NotionHistoryConnector: block_id=page_id, children=batch ) - logger.info(f"Successfully added {len(children)} new blocks to page {page_id}") + logger.info(f"Successfully appended {len(children)} new blocks to page {page_id}") except Exception as e: - # CRITICAL ERROR: Content was deleted but new content failed to add - logger.error( - f"CRITICAL: Failed to add new content after deleting {block_count} blocks. " - f"Page {page_id} content is lost! Error: {e}" - ) - - # Attempt to add an error placeholder block so page isn't completely empty - try: - await self._api_call_with_retry( - notion.blocks.children.append, - block_id=page_id, - children=[{ - "object": "block", - "type": "paragraph", - "paragraph": { - "rich_text": [{ - "type": "text", - "text": { - "content": "[ERROR] Content update failed. Original content was lost. " - "Please check your SurfSense logs for details." - } - }] - }, - }] - ) - logger.info(f"Added error placeholder to page {page_id}") - except Exception as placeholder_error: - logger.error(f"Failed to add error placeholder: {placeholder_error}") - + logger.error(f"Failed to append content blocks: {e}") return { "status": "error", - "message": f"CRITICAL: Failed to update page content. Original content ({block_count} blocks) " - f"was deleted but new content could not be added: {e!s}", + "message": f"Failed to append content: {e!s}", } - # Get updated page + # Get updated page info response = await self._api_call_with_retry( notion.pages.retrieve, page_id=page_id @@ -1119,7 +1055,7 @@ class NotionHistoryConnector: "page_id": page_id, "url": page_url, "title": page_title, - "message": f"Updated Notion page '{page_title}'", + "message": f"Updated Notion page '{page_title}' (content appended)", } except APIResponseError as e: diff --git a/surfsense_backend/app/services/notion/tool_metadata_service.py b/surfsense_backend/app/services/notion/tool_metadata_service.py index 521bfece0..c3caed358 100644 --- a/surfsense_backend/app/services/notion/tool_metadata_service.py +++ b/surfsense_backend/app/services/notion/tool_metadata_service.py @@ -140,7 +140,6 @@ class NotionToolMetadataService: "account": account.to_dict(), "page_id": page_id, "current_title": document.title, - "current_content": document.content, "document_id": document.id, "indexed_at": document.document_metadata.get("indexed_at"), } diff --git a/surfsense_web/components/tool-ui/update-notion-page.tsx b/surfsense_web/components/tool-ui/update-notion-page.tsx index 7fde7b75e..405b1177d 100644 --- a/surfsense_web/components/tool-ui/update-notion-page.tsx +++ b/surfsense_web/components/tool-ui/update-notion-page.tsx @@ -38,7 +38,6 @@ interface InterruptResult { }; page_id?: string; current_title?: string; - current_content?: string; document_id?: number; indexed_at?: string; error?: string; @@ -102,7 +101,6 @@ function ApprovalCard({ const account = interruptData.context?.account; const currentTitle = interruptData.context?.current_title; - const currentContent = interruptData.context?.current_content; // Title is not editable, so it's always valid const isTitleValid = true; @@ -220,7 +218,7 @@ function ApprovalCard({ id="notion-content" value={String(editedArgs.content ?? "")} onChange={(e) => setEditedArgs({ ...editedArgs, content: e.target.value || null })} - placeholder={currentContent || "Enter new content"} + placeholder="Enter content to append to the page" rows={isFullScreen ? undefined : 12} className={`resize-none ${isFullScreen ? "flex-1 min-h-0" : ""}`} /> @@ -406,7 +404,7 @@ function SuccessCard({ result }: { result: SuccessResult }) { } export const UpdateNotionPageToolUI = makeAssistantToolUI< - { page_id: string; title?: string | null; content?: string | null }, + { page_id: string; content: string }, UpdateNotionPageResult >({ toolName: "update_notion_page",