mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-14 20:55:15 +02:00
Improve notion page creation approval UI
This commit is contained in:
parent
c47171e218
commit
9023f2656b
3 changed files with 42 additions and 28 deletions
|
|
@ -50,11 +50,15 @@ def create_create_notion_page_tool(
|
|||
|
||||
Returns:
|
||||
Dictionary with:
|
||||
- status: "success" or "error"
|
||||
- page_id: Created page ID
|
||||
- url: URL to the created page
|
||||
- title: Page title
|
||||
- message: Success or error message
|
||||
- status: "success", "rejected", or "error"
|
||||
- page_id: Created page ID (if success)
|
||||
- url: URL to the created page (if success)
|
||||
- title: 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 create the page.")
|
||||
and move on. Do NOT ask for parent page IDs, troubleshoot, or suggest alternatives.
|
||||
|
||||
Examples:
|
||||
- "Create a Notion page titled 'Meeting Notes' with content 'Discussed project timeline'"
|
||||
|
|
@ -80,10 +84,9 @@ def create_create_notion_page_tool(
|
|||
"message": context["error"],
|
||||
}
|
||||
|
||||
logger.info("Requesting approval for creating Notion page")
|
||||
logger.info(f"Requesting approval for creating Notion page: '{title}'")
|
||||
approval = interrupt({
|
||||
"type": "notion_page_creation",
|
||||
"message": f"Approve creating Notion page: '{title}'",
|
||||
"action": {
|
||||
"tool": "create_notion_page",
|
||||
"params": {
|
||||
|
|
@ -105,13 +108,14 @@ def create_create_notion_page_tool(
|
|||
}
|
||||
|
||||
decision = decisions[0]
|
||||
decision_type = decision.get("decision_type")
|
||||
decision_type = decision.get("type") or decision.get("decision_type")
|
||||
logger.info(f"User decision: {decision_type}")
|
||||
|
||||
if decision_type == "reject":
|
||||
logger.info("Notion page creation rejected by user")
|
||||
return {
|
||||
"status": "rejected",
|
||||
"message": "Page creation was rejected",
|
||||
"message": "User declined. The page was not created. Do not ask again or suggest alternatives.",
|
||||
}
|
||||
|
||||
edited_action = decision.get("edited_action", {})
|
||||
|
|
|
|||
|
|
@ -523,19 +523,19 @@ class VercelStreamingService:
|
|||
|
||||
Handles two interrupt sources:
|
||||
1. interrupt_on config (Deep Agent built-in): Already has action_requests/review_configs
|
||||
2. interrupt() primitive (custom tool code): Has type/message/action/context
|
||||
2. interrupt() primitive (custom tool code): Has type/action/context (message is optional)
|
||||
|
||||
Args:
|
||||
interrupt_value: Raw interrupt payload from Deep Agent
|
||||
|
||||
Returns:
|
||||
dict: Normalized payload with action_requests, review_configs, and optional context
|
||||
dict: Normalized payload with action_requests, review_configs, and optional context/message
|
||||
"""
|
||||
if "action_requests" in interrupt_value and "review_configs" in interrupt_value:
|
||||
return interrupt_value
|
||||
|
||||
interrupt_type = interrupt_value.get("type", "unknown")
|
||||
message = interrupt_value.get("message", "Approval required")
|
||||
message = interrupt_value.get("message")
|
||||
action = interrupt_value.get("action", {})
|
||||
context = interrupt_value.get("context", {})
|
||||
|
||||
|
|
@ -553,9 +553,10 @@ class VercelStreamingService:
|
|||
}
|
||||
],
|
||||
"interrupt_type": interrupt_type,
|
||||
"message": message,
|
||||
"context": context,
|
||||
}
|
||||
if message:
|
||||
normalized["message"] = message
|
||||
return normalized
|
||||
|
||||
# =========================================================================
|
||||
|
|
|
|||
|
|
@ -164,19 +164,15 @@ function ApprovalCard({
|
|||
{/* Context section - account and parent page selection */}
|
||||
{!decided && interruptData.context && (
|
||||
<div className="border-b border-border px-4 py-3 bg-muted/30 space-y-3">
|
||||
{interruptData.message && (
|
||||
<p className="text-sm text-foreground">{interruptData.message}</p>
|
||||
)}
|
||||
|
||||
{interruptData.context.error ? (
|
||||
<p className="text-sm text-destructive">{interruptData.context.error}</p>
|
||||
) : (
|
||||
<>
|
||||
{accounts.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs font-medium text-muted-foreground">
|
||||
<div className="text-xs font-medium text-muted-foreground">
|
||||
Notion Account <span className="text-destructive">*</span>
|
||||
</label>
|
||||
</div>
|
||||
<Select
|
||||
value={selectedAccountId}
|
||||
onValueChange={(value) => {
|
||||
|
|
@ -190,7 +186,7 @@ function ApprovalCard({
|
|||
<SelectContent>
|
||||
{accounts.map((account) => (
|
||||
<SelectItem key={account.id} value={String(account.id)}>
|
||||
{account.workspace_name}
|
||||
{account.workspace_name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
|
@ -200,15 +196,15 @@ function ApprovalCard({
|
|||
|
||||
{selectedAccountId && (
|
||||
<div className="space-y-2">
|
||||
<label className="text-xs font-medium text-muted-foreground">
|
||||
<div className="text-xs font-medium text-muted-foreground">
|
||||
Parent Page (optional)
|
||||
</label>
|
||||
</div>
|
||||
<Select value={selectedParentPageId} onValueChange={setSelectedParentPageId}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="None (create at root level)" />
|
||||
<SelectValue placeholder="None" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="__none__">None (create at root level)</SelectItem>
|
||||
<SelectItem value="__none__">None</SelectItem>
|
||||
{availableParentPages.map((page) => (
|
||||
<SelectItem key={page.page_id} value={page.page_id}>
|
||||
📄 {page.title}
|
||||
|
|
@ -218,7 +214,7 @@ function ApprovalCard({
|
|||
</Select>
|
||||
{availableParentPages.length === 0 && selectedAccountId && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
No pages available. Page will be created at root level.
|
||||
No pages available. Page will be created at workspace root.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -249,7 +245,7 @@ function ApprovalCard({
|
|||
)}
|
||||
|
||||
{/* Edit mode - show editable form fields */}
|
||||
{isEditing && (
|
||||
{isEditing && !decided && (
|
||||
<div className="space-y-3 px-4 py-3 bg-card">
|
||||
<div>
|
||||
<label
|
||||
|
|
@ -310,6 +306,7 @@ function ApprovalCard({
|
|||
size="sm"
|
||||
onClick={() => {
|
||||
setDecided("edit");
|
||||
setIsEditing(false);
|
||||
onDecision({
|
||||
type: "edit",
|
||||
edited_action: {
|
||||
|
|
@ -317,11 +314,13 @@ function ApprovalCard({
|
|||
args: {
|
||||
...editedArgs,
|
||||
connector_id: selectedAccountId ? Number(selectedAccountId) : null,
|
||||
parent_page_id: selectedParentPageId === "__none__" ? null : selectedParentPageId,
|
||||
parent_page_id:
|
||||
selectedParentPageId === "__none__" ? null : selectedParentPageId,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
disabled={!selectedAccountId}
|
||||
>
|
||||
<CheckIcon />
|
||||
Approve with Changes
|
||||
|
|
@ -351,7 +350,8 @@ function ApprovalCard({
|
|||
args: {
|
||||
...args,
|
||||
connector_id: selectedAccountId ? Number(selectedAccountId) : null,
|
||||
parent_page_id: selectedParentPageId === "__none__" ? null : selectedParentPageId,
|
||||
parent_page_id:
|
||||
selectedParentPageId === "__none__" ? null : selectedParentPageId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -477,6 +477,15 @@ export const CreateNotionPageToolUI = makeAssistantToolUI<
|
|||
);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof result === "object" &&
|
||||
result !== null &&
|
||||
"status" in result &&
|
||||
(result as { status: string }).status === "rejected"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isErrorResult(result)) {
|
||||
return <ErrorCard result={result} />;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue