mirror of https://github.com/kortix-ai/suna.git
Merge pull request #1671 from KrishavRajSingh/feat/wait_tool
Feat/wait tool
This commit is contained in:
commit
a1582112ad
|
@ -828,6 +828,22 @@ Your approach is adaptive and context-aware:
|
||||||
6. **Be Human:** Use natural, conversational language throughout all interactions
|
6. **Be Human:** Use natural, conversational language throughout all interactions
|
||||||
7. **Show Personality:** Be warm, helpful, and genuinely interested in helping the user succeed
|
7. **Show Personality:** Be warm, helpful, and genuinely interested in helping the user succeed
|
||||||
|
|
||||||
|
**PACED EXECUTION & WAIT TOOL USAGE:**
|
||||||
|
8. **Deliberate Pacing:** Use the 'wait' tool frequently during long processes to maintain a steady, thoughtful pace rather than rushing through tasks
|
||||||
|
9. **Strategic Waiting:** Add brief pauses to:
|
||||||
|
- Allow file operations to complete properly
|
||||||
|
- Prevent overwhelming the system with rapid-fire operations
|
||||||
|
- Ensure quality execution over speed
|
||||||
|
- Add breathing room between complex operations
|
||||||
|
- Let long-running commands finish naturally instead of abandoning them
|
||||||
|
10. **Wait Tool Usage:**
|
||||||
|
- Use 1-3 seconds for brief pauses between operations
|
||||||
|
- Use 5-10 seconds for processing waits
|
||||||
|
- Use 10-30 seconds for long-running commands (npm install, build processes, etc.)
|
||||||
|
- Proactively use wait tool during long processes to prevent rushing
|
||||||
|
11. **Quality Over Speed:** Prioritize thorough, accurate execution over rapid completion
|
||||||
|
12. **Patience with Long Processes:** When a command is running (like create-react-app, npm install, etc.), wait for it to complete rather than switching to alternative approaches
|
||||||
|
|
||||||
**EXECUTION CYCLES:**
|
**EXECUTION CYCLES:**
|
||||||
- **Conversational Cycle:** Question → Response → Follow-up → User Input
|
- **Conversational Cycle:** Question → Response → Follow-up → User Input
|
||||||
- **Task Execution Cycle:** Analyze → Plan → Execute → Update → Complete
|
- **Task Execution Cycle:** Analyze → Plan → Execute → Update → Complete
|
||||||
|
|
|
@ -369,6 +369,67 @@ All deliverables are attached for your review.</parameter>
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return self.fail_response(f"Error entering complete state: {str(e)}")
|
return self.fail_response(f"Error entering complete state: {str(e)}")
|
||||||
|
|
||||||
|
@openapi_schema({
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "wait",
|
||||||
|
"description": "Pause execution for a specified number of seconds. Use this tool to add deliberate pauses in long-running processes to prevent rushing and maintain a steady, thoughtful pace. This helps prevent errors and ensures quality execution.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"seconds": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Number of seconds to wait (1-300 seconds). Use 1-3 seconds for brief pauses, 5-10 seconds for processing waits, 60+ seconds for longer operations.",
|
||||||
|
"minimum": 1,
|
||||||
|
"maximum": 300
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["seconds"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
@usage_example('''
|
||||||
|
<function_calls>
|
||||||
|
<invoke name="wait">
|
||||||
|
<parameter name="seconds">3</parameter>
|
||||||
|
</invoke>
|
||||||
|
</function_calls>
|
||||||
|
|
||||||
|
<function_calls>
|
||||||
|
<invoke name="wait">
|
||||||
|
<parameter name="seconds">5</parameter>
|
||||||
|
</invoke>
|
||||||
|
</function_calls>
|
||||||
|
''')
|
||||||
|
async def wait(self, seconds: int) -> ToolResult:
|
||||||
|
"""Pause execution for a specified number of seconds.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
seconds: Number of seconds to wait (1-300)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ToolResult indicating the wait was completed
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Validate duration
|
||||||
|
if seconds < 1 or seconds > 300:
|
||||||
|
return self.fail_response("Duration must be between 1 and 300 seconds")
|
||||||
|
|
||||||
|
# Import asyncio for the sleep
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
# Log the wait
|
||||||
|
logger.info(f"Agent waiting {seconds} seconds")
|
||||||
|
|
||||||
|
# Perform the wait
|
||||||
|
await asyncio.sleep(seconds)
|
||||||
|
|
||||||
|
# Return success
|
||||||
|
return self.success_response(f"Waited {seconds} seconds")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return self.fail_response(f"Error during wait: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Clock, CheckCircle, AlertTriangle, Loader2, Timer } from 'lucide-react';
|
||||||
|
import { ToolViewProps } from '../types';
|
||||||
|
import { formatTimestamp, extractToolData, getToolTitle } from '../utils';
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
interface WaitToolViewProps extends ToolViewProps {
|
||||||
|
// No additional props needed
|
||||||
|
}
|
||||||
|
|
||||||
|
const extractWaitData = (toolContent?: any, isSuccess: boolean = true) => {
|
||||||
|
let seconds = 0;
|
||||||
|
let actualIsSuccess = isSuccess;
|
||||||
|
|
||||||
|
if (toolContent) {
|
||||||
|
try {
|
||||||
|
const toolData = extractToolData(toolContent);
|
||||||
|
const toolResult = toolData.toolResult;
|
||||||
|
const arguments_ = toolResult?.arguments || {};
|
||||||
|
|
||||||
|
seconds = arguments_.seconds || 0;
|
||||||
|
actualIsSuccess = toolResult ? toolResult.isSuccess : isSuccess;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error parsing wait tool content:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
seconds,
|
||||||
|
isSuccess: actualIsSuccess
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function WaitToolView({
|
||||||
|
name = 'wait',
|
||||||
|
assistantContent,
|
||||||
|
toolContent,
|
||||||
|
assistantTimestamp,
|
||||||
|
toolTimestamp,
|
||||||
|
isSuccess = true,
|
||||||
|
isStreaming = false,
|
||||||
|
}: WaitToolViewProps) {
|
||||||
|
const { seconds, isSuccess: actualIsSuccess } = extractWaitData(toolContent, isSuccess);
|
||||||
|
|
||||||
|
const formatDuration = (seconds: number) => {
|
||||||
|
if (seconds < 60) {
|
||||||
|
return `${seconds}s`;
|
||||||
|
} else {
|
||||||
|
const minutes = Math.floor(seconds / 60);
|
||||||
|
const remainingSeconds = seconds % 60;
|
||||||
|
return remainingSeconds > 0 ? `${minutes}m ${remainingSeconds}s` : `${minutes}m`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toolTitle = getToolTitle(name) || 'Wait';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="gap-0 flex border shadow-none border-t border-b-0 border-x-0 p-0 rounded-none flex-col h-full overflow-hidden bg-card">
|
||||||
|
<CardHeader className="h-14 bg-zinc-50/80 dark:bg-zinc-900/80 backdrop-blur-sm border-b p-2 px-4 space-y-2">
|
||||||
|
<div className="flex flex-row items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="relative p-2 rounded-xl bg-gradient-to-br from-orange-500/20 to-orange-600/10 border border-orange-500/20">
|
||||||
|
<Clock className="w-5 h-5 text-orange-500 dark:text-orange-400" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<CardTitle className="text-base font-medium text-zinc-900 dark:text-zinc-100">
|
||||||
|
{toolTitle}
|
||||||
|
</CardTitle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!isStreaming && (
|
||||||
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
className={
|
||||||
|
actualIsSuccess
|
||||||
|
? "bg-gradient-to-b from-emerald-200 to-emerald-100 text-emerald-700 dark:from-emerald-800/50 dark:to-emerald-900/60 dark:text-emerald-300"
|
||||||
|
: "bg-gradient-to-b from-rose-200 to-rose-100 text-rose-700 dark:from-rose-800/50 dark:to-rose-900/60 dark:text-rose-300"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{actualIsSuccess ? (
|
||||||
|
<CheckCircle className="h-3.5 w-3.5 mr-1" />
|
||||||
|
) : (
|
||||||
|
<AlertTriangle className="h-3.5 w-3.5 mr-1" />
|
||||||
|
)}
|
||||||
|
{actualIsSuccess ? 'Completed' : 'Failed'}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isStreaming && (
|
||||||
|
<Badge className="bg-gradient-to-b from-orange-200 to-orange-100 text-orange-700 dark:from-orange-800/50 dark:to-orange-900/60 dark:text-orange-300">
|
||||||
|
<Loader2 className="h-3.5 w-3.5 animate-spin mr-1" />
|
||||||
|
Waiting
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent className="p-0 flex-1 overflow-hidden relative">
|
||||||
|
<div className="h-full flex items-center justify-center p-8">
|
||||||
|
<div className="flex flex-col items-center text-center max-w-md">
|
||||||
|
<Timer className="h-24 w-24 text-muted-foreground mb-6" />
|
||||||
|
|
||||||
|
<div className="text-5xl font-medium text-foreground mb-3">
|
||||||
|
{formatDuration(seconds)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-sm text-muted-foreground mb-4">
|
||||||
|
{isStreaming
|
||||||
|
? 'The system is currently pausing execution for the specified duration.'
|
||||||
|
: `The system paused execution for ${formatDuration(seconds)} as requested.`
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{seconds > 0 && (
|
||||||
|
<div className="text-xs text-muted-foreground bg-muted/50 px-3 py-2 rounded-full">
|
||||||
|
{isStreaming ? 'Please wait...' : 'Wait completed successfully'}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<div className="px-4 py-2 h-10 bg-gradient-to-r from-zinc-50/90 to-zinc-100/90 dark:from-zinc-900/90 dark:to-zinc-800/90 backdrop-blur-sm border-t border-zinc-200 dark:border-zinc-800 flex justify-between items-center gap-4">
|
||||||
|
<div className="h-full flex items-center gap-2 text-sm text-zinc-500 dark:text-zinc-400">
|
||||||
|
<Badge className="h-6 py-0.5" variant="outline">
|
||||||
|
<Clock className="h-3 w-3 mr-1" />
|
||||||
|
Timing Control
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||||
|
{toolTimestamp ? formatTimestamp(toolTimestamp) : assistantTimestamp ? formatTimestamp(assistantTimestamp) : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import { SeeImageToolView } from '../see-image-tool/SeeImageToolView';
|
||||||
import { TerminateCommandToolView } from '../command-tool/TerminateCommandToolView';
|
import { TerminateCommandToolView } from '../command-tool/TerminateCommandToolView';
|
||||||
import { AskToolView } from '../ask-tool/AskToolView';
|
import { AskToolView } from '../ask-tool/AskToolView';
|
||||||
import { CompleteToolView } from '../CompleteToolView';
|
import { CompleteToolView } from '../CompleteToolView';
|
||||||
|
import { WaitToolView } from '../wait-tool/WaitToolView';
|
||||||
import { ExecuteDataProviderCallToolView } from '../data-provider-tool/ExecuteDataProviderCallToolView';
|
import { ExecuteDataProviderCallToolView } from '../data-provider-tool/ExecuteDataProviderCallToolView';
|
||||||
import { DataProviderEndpointsToolView } from '../data-provider-tool/DataProviderEndpointsToolView';
|
import { DataProviderEndpointsToolView } from '../data-provider-tool/DataProviderEndpointsToolView';
|
||||||
import { DeployToolView } from '../DeployToolView';
|
import { DeployToolView } from '../DeployToolView';
|
||||||
|
@ -107,6 +108,7 @@ const defaultRegistry: ToolViewRegistryType = {
|
||||||
|
|
||||||
'ask': AskToolView,
|
'ask': AskToolView,
|
||||||
'complete': CompleteToolView,
|
'complete': CompleteToolView,
|
||||||
|
'wait': WaitToolView,
|
||||||
|
|
||||||
'deploy': DeployToolView,
|
'deploy': DeployToolView,
|
||||||
|
|
||||||
|
|
|
@ -331,6 +331,7 @@ const TOOL_DISPLAY_NAMES = new Map([
|
||||||
|
|
||||||
['deploy', 'Deploying'],
|
['deploy', 'Deploying'],
|
||||||
['ask', 'Ask'],
|
['ask', 'Ask'],
|
||||||
|
['wait', 'Wait'],
|
||||||
['create-tasks', 'Creating Tasks'],
|
['create-tasks', 'Creating Tasks'],
|
||||||
['update-tasks', 'Updating Tasks'],
|
['update-tasks', 'Updating Tasks'],
|
||||||
['complete', 'Completing Task'],
|
['complete', 'Completing Task'],
|
||||||
|
|
Loading…
Reference in New Issue