fix(look-at): add timeout to sync model retry to prevent process hang
Closes #2087
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
import type { createOpencodeClient } from "@opencode-ai/sdk"
|
||||
import { log } from "./logger"
|
||||
import {
|
||||
createPromptTimeoutContext,
|
||||
PROMPT_TIMEOUT_MS,
|
||||
type PromptRetryOptions,
|
||||
} from "./prompt-timeout-context"
|
||||
|
||||
type Client = ReturnType<typeof createOpencodeClient>
|
||||
|
||||
@@ -77,30 +82,36 @@ interface PromptBody {
|
||||
interface PromptArgs {
|
||||
path: { id: string }
|
||||
body: PromptBody
|
||||
signal?: AbortSignal
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export async function promptWithModelSuggestionRetry(
|
||||
client: Client,
|
||||
args: PromptArgs,
|
||||
options: PromptRetryOptions = {},
|
||||
): Promise<void> {
|
||||
const timeoutMs = options.timeoutMs ?? PROMPT_TIMEOUT_MS
|
||||
const timeoutContext = createPromptTimeoutContext(args, timeoutMs)
|
||||
// NOTE: Model suggestion retry removed — promptAsync returns 204 immediately,
|
||||
// model errors happen asynchronously server-side and cannot be caught here
|
||||
const promptPromise = client.session.promptAsync(
|
||||
args as Parameters<typeof client.session.promptAsync>[0],
|
||||
)
|
||||
|
||||
let timeoutID: ReturnType<typeof setTimeout> | null = null
|
||||
const timeoutPromise = new Promise<never>((_, reject) => {
|
||||
timeoutID = setTimeout(() => {
|
||||
reject(new Error("promptAsync timed out after 120000ms"))
|
||||
}, 120000)
|
||||
})
|
||||
const promptPromise = client.session.promptAsync({
|
||||
...args,
|
||||
signal: timeoutContext.signal,
|
||||
} as Parameters<typeof client.session.promptAsync>[0])
|
||||
|
||||
try {
|
||||
await Promise.race([promptPromise, timeoutPromise])
|
||||
await promptPromise
|
||||
if (timeoutContext.wasTimedOut()) {
|
||||
throw new Error(`promptAsync timed out after ${timeoutMs}ms`)
|
||||
}
|
||||
} catch (error) {
|
||||
if (timeoutContext.wasTimedOut()) {
|
||||
throw new Error(`promptAsync timed out after ${timeoutMs}ms`)
|
||||
}
|
||||
throw error
|
||||
} finally {
|
||||
if (timeoutID !== null) clearTimeout(timeoutID)
|
||||
timeoutContext.cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,9 +127,28 @@ export async function promptWithModelSuggestionRetry(
|
||||
export async function promptSyncWithModelSuggestionRetry(
|
||||
client: Client,
|
||||
args: PromptArgs,
|
||||
options: PromptRetryOptions = {},
|
||||
): Promise<void> {
|
||||
const timeoutMs = options.timeoutMs ?? PROMPT_TIMEOUT_MS
|
||||
|
||||
try {
|
||||
await client.session.prompt(args as Parameters<typeof client.session.prompt>[0])
|
||||
const timeoutContext = createPromptTimeoutContext(args, timeoutMs)
|
||||
try {
|
||||
await client.session.prompt({
|
||||
...args,
|
||||
signal: timeoutContext.signal,
|
||||
} as Parameters<typeof client.session.prompt>[0])
|
||||
if (timeoutContext.wasTimedOut()) {
|
||||
throw new Error(`prompt timed out after ${timeoutMs}ms`)
|
||||
}
|
||||
} catch (error) {
|
||||
if (timeoutContext.wasTimedOut()) {
|
||||
throw new Error(`prompt timed out after ${timeoutMs}ms`)
|
||||
}
|
||||
throw error
|
||||
} finally {
|
||||
timeoutContext.cleanup()
|
||||
}
|
||||
} catch (error) {
|
||||
const suggestion = parseModelSuggestion(error)
|
||||
if (!suggestion || !args.body.model) {
|
||||
@@ -130,7 +160,7 @@ export async function promptSyncWithModelSuggestionRetry(
|
||||
suggested: suggestion.suggestion,
|
||||
})
|
||||
|
||||
await client.session.prompt({
|
||||
const retryArgs: PromptArgs = {
|
||||
...args,
|
||||
body: {
|
||||
...args.body,
|
||||
@@ -139,6 +169,24 @@ export async function promptSyncWithModelSuggestionRetry(
|
||||
modelID: suggestion.suggestion,
|
||||
},
|
||||
},
|
||||
} as Parameters<typeof client.session.prompt>[0])
|
||||
}
|
||||
|
||||
const timeoutContext = createPromptTimeoutContext(retryArgs, timeoutMs)
|
||||
try {
|
||||
await client.session.prompt({
|
||||
...retryArgs,
|
||||
signal: timeoutContext.signal,
|
||||
} as Parameters<typeof client.session.prompt>[0])
|
||||
if (timeoutContext.wasTimedOut()) {
|
||||
throw new Error(`prompt timed out after ${timeoutMs}ms`)
|
||||
}
|
||||
} catch (retryError) {
|
||||
if (timeoutContext.wasTimedOut()) {
|
||||
throw new Error(`prompt timed out after ${timeoutMs}ms`)
|
||||
}
|
||||
throw retryError
|
||||
} finally {
|
||||
timeoutContext.cleanup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user