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
|
||||
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:**
|
||||
- **Conversational Cycle:** Question → Response → Follow-up → User Input
|
||||
- **Task Execution Cycle:** Analyze → Plan → Execute → Update → Complete
|
||||
|
|
|
@ -369,6 +369,67 @@ All deliverables are attached for your review.</parameter>
|
|||
except Exception as 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__":
|
||||
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 { AskToolView } from '../ask-tool/AskToolView';
|
||||
import { CompleteToolView } from '../CompleteToolView';
|
||||
import { WaitToolView } from '../wait-tool/WaitToolView';
|
||||
import { ExecuteDataProviderCallToolView } from '../data-provider-tool/ExecuteDataProviderCallToolView';
|
||||
import { DataProviderEndpointsToolView } from '../data-provider-tool/DataProviderEndpointsToolView';
|
||||
import { DeployToolView } from '../DeployToolView';
|
||||
|
@ -107,6 +108,7 @@ const defaultRegistry: ToolViewRegistryType = {
|
|||
|
||||
'ask': AskToolView,
|
||||
'complete': CompleteToolView,
|
||||
'wait': WaitToolView,
|
||||
|
||||
'deploy': DeployToolView,
|
||||
|
||||
|
|
|
@ -331,6 +331,7 @@ const TOOL_DISPLAY_NAMES = new Map([
|
|||
|
||||
['deploy', 'Deploying'],
|
||||
['ask', 'Ask'],
|
||||
['wait', 'Wait'],
|
||||
['create-tasks', 'Creating Tasks'],
|
||||
['update-tasks', 'Updating Tasks'],
|
||||
['complete', 'Completing Task'],
|
||||
|
|
Loading…
Reference in New Issue