From 9390f195cc57479627a552e79401c1528186b16a Mon Sep 17 00:00:00 2001 From: "DESKTOP-RTLN3BA\\$punk" Date: Mon, 16 Feb 2026 02:14:26 -0800 Subject: [PATCH] refactor: fix decision handling and error message extraction in Notion tools - Improved decision extraction logic in create, delete, and update Notion page tools to ensure proper handling of approval decisions. - Added a static method to NotionHistoryConnector for consistent error message extraction from API responses, enhancing readability and maintainability. --- .../new_chat/tools/notion/create_page.py | 15 +++++++++--- .../new_chat/tools/notion/delete_page.py | 15 +++++++++--- .../new_chat/tools/notion/update_page.py | 15 +++++++++--- .../app/connectors/notion_history.py | 23 +++++++++++-------- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/surfsense_backend/app/agents/new_chat/tools/notion/create_page.py b/surfsense_backend/app/agents/new_chat/tools/notion/create_page.py index 112c5a34d..3d4b84ee0 100644 --- a/surfsense_backend/app/agents/new_chat/tools/notion/create_page.py +++ b/surfsense_backend/app/agents/new_chat/tools/notion/create_page.py @@ -107,7 +107,9 @@ def create_create_notion_page_tool( } ) - decisions = approval.get("decisions", []) + decisions_raw = approval.get("decisions", []) if isinstance(approval, dict) else [] + decisions = decisions_raw if isinstance(decisions_raw, list) else [decisions_raw] + decisions = [d for d in decisions if isinstance(d, dict)] if not decisions: logger.warning("No approval decision received") return { @@ -126,8 +128,15 @@ def create_create_notion_page_tool( "message": "User declined. The page was not created. Do not ask again or suggest alternatives.", } - edited_action = decision.get("edited_action", {}) - final_params = edited_action.get("args", {}) if edited_action else {} + edited_action = decision.get("edited_action") + final_params: dict[str, Any] = {} + if isinstance(edited_action, dict): + edited_args = edited_action.get("args") + if isinstance(edited_args, dict): + final_params = edited_args + elif isinstance(decision.get("args"), dict): + # Some interrupt payloads place args directly on the decision. + final_params = decision["args"] final_title = final_params.get("title", title) final_content = final_params.get("content", content) diff --git a/surfsense_backend/app/agents/new_chat/tools/notion/delete_page.py b/surfsense_backend/app/agents/new_chat/tools/notion/delete_page.py index b63284e3d..c07de407e 100644 --- a/surfsense_backend/app/agents/new_chat/tools/notion/delete_page.py +++ b/surfsense_backend/app/agents/new_chat/tools/notion/delete_page.py @@ -119,7 +119,9 @@ def create_delete_notion_page_tool( } ) - decisions = approval.get("decisions", []) + decisions_raw = approval.get("decisions", []) if isinstance(approval, dict) else [] + decisions = decisions_raw if isinstance(decisions_raw, list) else [decisions_raw] + decisions = [d for d in decisions if isinstance(d, dict)] if not decisions: logger.warning("No approval decision received") return { @@ -139,8 +141,15 @@ def create_delete_notion_page_tool( } # Extract edited action arguments (if user modified the checkbox) - edited_action = decision.get("edited_action", {}) - final_params = edited_action.get("args", {}) if edited_action else {} + edited_action = decision.get("edited_action") + final_params: dict[str, Any] = {} + if isinstance(edited_action, dict): + edited_args = edited_action.get("args") + if isinstance(edited_args, dict): + final_params = edited_args + elif isinstance(decision.get("args"), dict): + # Some interrupt payloads place args directly on the decision. + final_params = decision["args"] final_page_id = final_params.get("page_id", page_id) final_connector_id = final_params.get( 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 371352ee0..17a9aa6db 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 @@ -128,7 +128,9 @@ def create_update_notion_page_tool( } ) - decisions = approval.get("decisions", []) + decisions_raw = approval.get("decisions", []) if isinstance(approval, dict) else [] + decisions = decisions_raw if isinstance(decisions_raw, list) else [decisions_raw] + decisions = [d for d in decisions if isinstance(d, dict)] if not decisions: logger.warning("No approval decision received") return { @@ -147,8 +149,15 @@ def create_update_notion_page_tool( "message": "User declined. The page was not updated. Do not ask again or suggest alternatives.", } - edited_action = decision.get("edited_action", {}) - final_params = edited_action.get("args", {}) if edited_action else {} + edited_action = decision.get("edited_action") + final_params: dict[str, Any] = {} + if isinstance(edited_action, dict): + edited_args = edited_action.get("args") + if isinstance(edited_args, dict): + final_params = edited_args + elif isinstance(decision.get("args"), dict): + # Some interrupt payloads place args directly on the decision. + final_params = decision["args"] final_page_id = final_params.get("page_id", page_id) final_content = final_params.get("content", content) diff --git a/surfsense_backend/app/connectors/notion_history.py b/surfsense_backend/app/connectors/notion_history.py index 56a9b6c34..311f97ddd 100644 --- a/surfsense_backend/app/connectors/notion_history.py +++ b/surfsense_backend/app/connectors/notion_history.py @@ -442,6 +442,16 @@ class NotionHistoryConnector: if page_title not in self._pages_with_skipped_content: self._pages_with_skipped_content.append(page_title) + @staticmethod + def _api_error_message(error: APIResponseError) -> str: + """Extract a stable, human-readable message from Notion API errors.""" + body = getattr(error, "body", None) + if isinstance(body, dict): + return str(body.get("message", str(error))) + if body: + return str(body) + return str(error) + async def __aenter__(self): """Async context manager entry.""" return self @@ -998,7 +1008,7 @@ class NotionHistoryConnector: except APIResponseError as e: logger.error(f"Notion API error creating page: {e}") - error_msg = e.body.get("message", str(e)) if hasattr(e, "body") else str(e) + error_msg = self._api_error_message(e) return { "status": "error", "message": f"Failed to create Notion page: {error_msg}", @@ -1087,7 +1097,7 @@ class NotionHistoryConnector: except APIResponseError as e: logger.error(f"Notion API error updating page: {e}") - error_msg = e.body.get("message", str(e)) if hasattr(e, "body") else str(e) + error_msg = self._api_error_message(e) return { "status": "error", "message": f"Failed to update Notion page: {error_msg}", @@ -1136,14 +1146,7 @@ class NotionHistoryConnector: except APIResponseError as e: logger.error(f"Notion API error deleting page: {e}") - # Handle both dict and string body formats - if hasattr(e, "body"): - if isinstance(e.body, dict): - error_msg = e.body.get("message", str(e)) - else: - error_msg = str(e.body) if e.body else str(e) - else: - error_msg = str(e) + error_msg = self._api_error_message(e) return { "status": "error", "message": f"Failed to delete Notion page: {error_msg}",