-
Notifications
You must be signed in to change notification settings - Fork 6
Fix agent turns hanging indefinitely: add step timeout and fetch_url timeout #32
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
base: main
Are you sure you want to change the base?
Changes from all commits
d95b54c
0ab41a4
ae742a6
c25d142
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,19 @@ import { z } from 'zod'; | |
| import { exa } from '../lib/exa'; | ||
| import { input, output } from '../types/tools/index'; | ||
|
|
||
| const FETCH_TIMEOUT_MS = 20_000; | ||
|
|
||
| function withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> { | ||
| let timeoutId: ReturnType<typeof setTimeout>; | ||
| const timeout = new Promise<never>((_, reject) => { | ||
| timeoutId = setTimeout( | ||
| () => reject(new Error(`${label} timed out after ${ms}ms.`)), | ||
| ms | ||
| ); | ||
| }); | ||
| return Promise.race([promise, timeout]).finally(() => clearTimeout(timeoutId)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift 🔎 Supported by static analysis🤖 get_repo_knowledge executed:
Length of output: 599 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- diff ---'
git diff -- src/mastra/tools/fetch-url.ts
printf '%s\n' '--- file ---'
cat -n src/mastra/tools/fetch-url.ts
printf '%s\n' '--- exa references ---'
rg -n -C 3 'getContents|livecrawlTimeout|exa-js|fetchImpl|fetch-url' --glob '!node_modules' .Repository: techwithanirudh/gorkie Length of output: 9800 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- Exa binding ---'
cat -n src/mastra/lib/exa.ts
printf '%s\n' '--- exa-js v2.16.3 index exports and request path ---'
tmpdir="$(mktemp -d)"
curl -fsSL https://raw.githubusercontent.com/exa-labs/exa-js/v2.16.3/src/index.ts -o "$tmpdir/index.ts"
cat -n "$tmpdir/index.ts" | sed -n '1,260p'
printf '%s\n' '--- referenced source files ---'
rg -n 'getContents|livecrawlTimeout|fetchImpl|Abort|signal|request' "$tmpdir/index.ts"Repository: techwithanirudh/gorkie Length of output: 15316 🏁 Script executed: #!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
curl -fsSL https://raw.githubusercontent.com/exa-labs/exa-js/v2.16.3/src/index.ts -o "$tmpdir/index.ts"
printf '%s\n' '--- constructor and option normalization ---'
sed -n '700,845p' "$tmpdir/index.ts"
printf '%s\n' '--- request implementation ---'
sed -n '870,945p' "$tmpdir/index.ts"
printf '%s\n' '--- getContents implementation ---'
sed -n '1278,1322p' "$tmpdir/index.ts"
printf '%s\n' '--- package constructor/type surface ---'
rg -n -C 5 'constructor\(|class Exa|fetch|signal|AbortController' "$tmpdir/index.ts"Repository: techwithanirudh/gorkie Length of output: 12097 Abort the underlying Exa fetch when the tool timeout fires.
🧰 Tools🪛 GitHub Actions: CI / 2_Lint.txt[error] 8-22: Ultracite formatter check failed. Reformat the function declaration and Promise.finally call to match the expected formatting. 🪛 GitHub Actions: CI / Lint[error] 8-16: Ultracite formatter check failed. The file is not formatted according to the expected style. Run 'bun run check' with the formatter's write/fix option to apply the required formatting. 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| export const fetchUrlTool = createTool({ | ||
| id: 'fetch_url', | ||
| description: `Fetch a readable excerpt from a specific, known public URL (an article, doc page, README, or link someone shared). Not for search; use search_web to find URLs first. | ||
|
|
@@ -29,11 +42,19 @@ This extracts readable article content, so it fails on anything that isn't a pla | |
| }, | ||
| }, | ||
| execute: async ({ url }) => { | ||
| // A slow or hanging page here has no bound of its own, and used to be | ||
| // able to block the whole step (and by extension the turn) with no | ||
| // error and no trace, since the agent-level step timeout only fires | ||
| // between steps, not inside one still-running tool call. | ||
| const [result] = ( | ||
| await exa.getContents([url], { | ||
| text: { maxCharacters: 8000 }, | ||
| livecrawl: 'preferred', | ||
| }) | ||
| await withTimeout( | ||
| exa.getContents([url], { | ||
| text: { maxCharacters: 8000 }, | ||
| livecrawl: 'preferred', | ||
| }), | ||
| FETCH_TIMEOUT_MS, | ||
| `Fetching ${url}` | ||
| ) | ||
| ).results; | ||
| if (!result) { | ||
| throw new Error(`Could not fetch content from ${url}.`); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🤖 get_repo_knowledge executed:
get_repo_knowledge techwithanirudh/gorkie /tmp/coderabbit-repo-knowledge/techwithanirudh-gorkie-55f3ff29/conventionsLength of output: 1545
🏁 Script executed:
Repository: techwithanirudh/gorkie
Length of output: 38314
🏁 Script executed:
Repository: techwithanirudh/gorkie
Length of output: 4565
🌐 Web query:
Mastra modelSettings.timeout.stepMs agent tool timeout documentation@mastra/core1.60.0💡 Result:
In Mastra, modelSettings.timeout provides time-based execution budgets for agent runs, helping prevent long-running calls from stalling the agent loop [1][2][3]. The timeout settings are defined within the modelSettings object under the timeout property, which accepts an object of type ModelTimeoutSettings [1][2]. Key timeout properties: totalMs: Specifies the maximum wall-clock duration (in milliseconds) for an entire agent run, including all loop iterations, tool calls, and retries [1][3]. If this limit is exceeded, the run is aborted with a MastraTimeoutError; fallback models are not attempted because this is treated as a hard deadline for the total operation [1][2][3]. stepMs: Specifies the maximum wall-clock duration (in milliseconds) for a single model call [1][3]. This covers both establishing the stream and consuming it [1]. If exceeded, the call fails with a MastraTimeoutError; while the specific model call is not retried, the agent will advance to the next configured model if fallback models are defined [1][2][3]. Implementation Example: const result = await agent.generate('Prompt content', { modelSettings: { timeout: { totalMs: 60000, // 60 seconds total stepMs: 10000, // 10 seconds per model call }, }, }); Tool Execution Timeouts: In addition to model-level timeouts, individual tool execution can be constrained using the timeoutMs option within ToolOptions [4]. Setting this value ensures that a stalled tool execution does not block the agent loop indefinitely [4]. If a tool exceeds its assigned timeoutMs, it throws a MastraError with the ID TOOL_EXECUTION_TIMEOUT [4]. These features are part of the Mastra core framework as of version 1.60.0 and subsequent releases [2][5].
Citations:
🤖 get_repo_knowledge executed:
get_repo_knowledge techwithanirudh/gorkie /tmp/coderabbit-repo-knowledge/techwithanirudh-gorkie-55f3ff29/conventionsLength of output: 1545
Correct the timeout comments in both agents.
modelSettings.timeout.stepMslimits a provider model call. It does not time out tool execution.fetchUrlToolalready applies its own 20-second timeout. Rewrite both comments so they describe only the provider-step guarantee and do not attributefetch_urlprotection tostepMs.📍 Affects 2 files
src/mastra/agents/explore.ts#L59-L61(this comment)src/mastra/agents/research.ts#L53-L55🤖 Prompt for AI Agents