-
Notifications
You must be signed in to change notification settings - Fork 8.1k
feat(tools): add WaitTool for pausing on long-running jobs #6690
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
122debd
feat(tools): add WaitTool for pausing on long-running jobs
joaomdmoura 438a89c
fix(tools): enforce non-negative wait on positional calls, fix doc sn…
joaomdmoura 6205516
docs: point the wait tool card at the edge path
joaomdmoura f343936
fix(tools): never cache waits and keep the advertised cap accurate
joaomdmoura ff94734
fix(tools): reject NaN waits and pluralize single-second results
joaomdmoura File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| --- | ||
| title: Wait Tool | ||
| description: The `WaitTool` lets an agent pause before checking a long-running job again. | ||
| icon: hourglass-half | ||
| mode: "wide" | ||
| --- | ||
|
|
||
| ## Overview | ||
|
|
||
| The `WaitTool` pauses execution for a given number of seconds. It exists because agents that | ||
| kick off long-running work — a sandbox build, a deployment, a batch import, an async API job — | ||
| otherwise have no way to let time pass. Without it, an agent either polls in a tight loop or | ||
| gives up before the work finishes. | ||
|
|
||
| The tool takes no API key and has no dependencies beyond the standard library. | ||
|
|
||
| ## When to Use It | ||
|
|
||
| The tool's description tells the model to reach for it when out-of-band work needs real time | ||
| to progress: | ||
|
|
||
| - A sandbox build, test run, or script that is still executing | ||
| - A deployment or provisioning step that is still rolling out | ||
| - A batch import, export, or training job | ||
| - An async API that returned a job id to poll later | ||
| - A rate limit or backoff that has to cool down before retrying | ||
|
|
||
| The pattern the model is steered toward is: start the job, wait, check status, wait again if it | ||
| is still running. The description also tells it *not* to wait to pace a conversation or when the | ||
| information it needs is already available — waiting only lets clock time pass, it does not | ||
| advance or check the job. | ||
|
|
||
| ## Installation | ||
|
|
||
| The tool ships with `crewai-tools`: | ||
|
|
||
| ```shell | ||
| uv add crewai-tools | ||
| ``` | ||
|
|
||
| ## Example | ||
|
|
||
| ```python Code | ||
| from crewai import Agent, Crew, Task | ||
| from crewai.tools import tool | ||
| from crewai_tools import WaitTool | ||
|
|
||
| wait_tool = WaitTool() | ||
|
|
||
|
|
||
| @tool("Check build status") | ||
| def check_build_status_tool(build_id: str) -> str: | ||
| """Return the current status of a build: queued, running, passed, or failed.""" | ||
| # Replace this with a call to your own build system. | ||
| return my_ci_client.get_build(build_id).status | ||
|
|
||
|
|
||
| build_agent = Agent( | ||
| role="Build Monitor", | ||
| goal="Start the build and report its final status", | ||
| backstory="An engineer who knows that builds take time.", | ||
| tools=[wait_tool, check_build_status_tool], | ||
| verbose=True, | ||
| ) | ||
|
|
||
| monitor_task = Task( | ||
| description=( | ||
| "Start the build, then wait and re-check its status until it finishes." | ||
| ), | ||
| expected_output="The final build status.", | ||
| agent=build_agent, | ||
| ) | ||
|
|
||
| crew = Crew(agents=[build_agent], tasks=[monitor_task]) | ||
| result = crew.kickoff() | ||
| ``` | ||
|
|
||
| ## Arguments | ||
|
|
||
| | Argument | Type | Required | Description | | ||
| | :-------- | :------ | :------- | :----------------------------------------------------------------------------- | | ||
| | `seconds` | `float` | ✅ | How many seconds to wait. Must be zero or greater. | | ||
| | `reason` | `str` | ❌ | Optional note on what is being waited for. Echoed back in the tool's result. | | ||
|
|
||
| ## Initialization Parameters | ||
|
|
||
| | Parameter | Type | Default | Description | | ||
| | :------------ | :------ | :------ | :----------------------------------------------------------------------------------- | | ||
| | `max_seconds` | `float` | `300` | Upper bound for a single wait. Longer requests are capped to this value, not rejected. | | ||
|
|
||
| ## Capping Long Waits | ||
|
|
||
| A single call waits at most `max_seconds`. If an agent asks for more, the tool waits the | ||
| maximum and says so in its result, so the agent can call it again rather than fail: | ||
|
|
||
| ```python Code | ||
| wait_tool = WaitTool() | ||
| wait_tool.run(seconds=3600) | ||
| # 'Waited 300 seconds. Requested 3600 seconds, capped at 300 seconds per call - | ||
| # call this tool again if more waiting is needed.' | ||
| ``` | ||
|
|
||
| Raise the cap when a workflow genuinely needs longer single pauses: | ||
|
|
||
| ```python Code | ||
| wait_tool = WaitTool(max_seconds=1800) | ||
| ``` | ||
|
|
||
| ## Async Support | ||
|
|
||
| The tool implements both sync and async execution, so it does not block the event loop when | ||
| awaited: | ||
|
|
||
| ```python Code | ||
| import asyncio | ||
|
|
||
|
|
||
| async def main(): | ||
| result = await wait_tool.arun(seconds=30, reason="waiting for the sandbox build") | ||
| print(result) | ||
|
|
||
|
|
||
| asyncio.run(main()) | ||
| ``` | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
lib/crewai-tools/src/crewai_tools/tools/wait_tool/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| # WaitTool | ||
|
|
||
| The **WaitTool** pauses execution for a given number of seconds. Use it when an agent needs to | ||
| let time pass before re-checking a long-running job — a sandbox build, a deployment, a batch | ||
| import, an async API call. | ||
|
|
||
| No API key, no dependencies beyond the standard library. | ||
|
|
||
| ## Arguments | ||
|
|
||
| | Argument | Type | Required | Description | | ||
| | --------- | ------- | -------- | ---------------------------------------------------------------------------- | | ||
| | `seconds` | `float` | ✅ | How many seconds to wait. Must be zero or greater. | | ||
| | `reason` | `str` | ❌ | Optional note on what is being waited for. Echoed back in the tool's result. | | ||
|
|
||
| ## Initialization Parameters | ||
|
|
||
| | Parameter | Type | Default | Description | | ||
| | ------------- | ------- | ------- | -------------------------------------------------------------------------------------- | | ||
| | `max_seconds` | `float` | `300` | Upper bound for a single wait. Longer requests are capped to this value, not rejected. | | ||
|
|
||
| ## Usage Example | ||
|
|
||
| ```python | ||
| from crewai import Agent | ||
| from crewai.tools import tool | ||
| from crewai_tools import WaitTool | ||
|
|
||
| wait_tool = WaitTool() | ||
|
|
||
|
|
||
| @tool("Check build status") | ||
| def check_build_status_tool(build_id: str) -> str: | ||
| """Return the current status of a build: queued, running, passed, or failed.""" | ||
| # Replace this with a call to your own build system. | ||
| return my_ci_client.get_build(build_id).status | ||
|
|
||
|
|
||
| agent = Agent( | ||
| role="Build Monitor", | ||
| goal="Start the build and report its final status", | ||
| backstory="An engineer who knows that builds take time.", | ||
| tools=[wait_tool, check_build_status_tool], | ||
| ) | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| ``` | ||
|
|
||
| A single call waits at most `max_seconds`. Longer requests are capped and the result says so, | ||
| so the agent can simply call the tool again: | ||
|
|
||
| ```python | ||
| wait_tool.run(seconds=3600) | ||
| # 'Waited 300 seconds. Requested 3600 seconds, capped at 300 seconds per call - | ||
| # call this tool again if more waiting is needed.' | ||
|
|
||
| WaitTool(max_seconds=1800).run(seconds=900) | ||
| # 'Waited 900 seconds.' | ||
| ``` | ||
|
|
||
| Async execution is supported and does not block the event loop: | ||
|
|
||
| ```python | ||
| import asyncio | ||
|
|
||
|
|
||
| async def main(): | ||
| print(await wait_tool.arun(seconds=30, reason="waiting for the sandbox build")) | ||
|
|
||
|
|
||
| asyncio.run(main()) | ||
| ``` | ||
4 changes: 4 additions & 0 deletions
4
lib/crewai-tools/src/crewai_tools/tools/wait_tool/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| from crewai_tools.tools.wait_tool.wait_tool import WaitTool, WaitToolSchema | ||
|
|
||
|
|
||
| __all__ = ["WaitTool", "WaitToolSchema"] |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.