refactor(llms): split AbortError out of InvokeError
This commit is contained in:
@@ -368,7 +368,6 @@ export class PageAgentCore extends EventTarget {
|
|||||||
description: 'You MUST call this tool every step!',
|
description: 'You MUST call this tool every step!',
|
||||||
inputSchema: macroToolSchema as z.ZodType<MacroToolInput>,
|
inputSchema: macroToolSchema as z.ZodType<MacroToolInput>,
|
||||||
execute: async (input: MacroToolInput): Promise<MacroToolResult> => {
|
execute: async (input: MacroToolInput): Promise<MacroToolResult> => {
|
||||||
// abort — throws DOMException whose .name === 'AbortError'
|
|
||||||
this.#abortController.signal.throwIfAborted()
|
this.#abortController.signal.throwIfAborted()
|
||||||
|
|
||||||
console.log(chalk.blue.bold('MacroTool input'), input)
|
console.log(chalk.blue.bold('MacroTool input'), input)
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ export class OpenAIClient implements LLMClient {
|
|||||||
abortSignal?: AbortSignal,
|
abortSignal?: AbortSignal,
|
||||||
options?: InvokeOptions
|
options?: InvokeOptions
|
||||||
): Promise<InvokeResult> {
|
): Promise<InvokeResult> {
|
||||||
// in case user aborted before invoking
|
abortSignal?.throwIfAborted()
|
||||||
if (abortSignal?.aborted) throw new InvokeError(InvokeErrorTypes.ABORTED, 'Aborted')
|
|
||||||
|
|
||||||
// 1. Convert tools to OpenAI format
|
// 1. Convert tools to OpenAI format
|
||||||
const openaiTools = Object.entries(tools).map(([name, t]) => zodToOpenAITool(name, t))
|
const openaiTools = Object.entries(tools).map(([name, t]) => zodToOpenAITool(name, t))
|
||||||
@@ -73,9 +72,7 @@ export class OpenAIClient implements LLMClient {
|
|||||||
signal: abortSignal,
|
signal: abortSignal,
|
||||||
})
|
})
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if ((error as any)?.name === 'AbortError') {
|
if ((error as any)?.name === 'AbortError') throw error
|
||||||
throw new InvokeError(InvokeErrorTypes.ABORTED, 'Aborted', error)
|
|
||||||
}
|
|
||||||
console.error(error)
|
console.error(error)
|
||||||
throw new InvokeError(InvokeErrorTypes.NETWORK_ERROR, 'Network request failed', error)
|
throw new InvokeError(InvokeErrorTypes.NETWORK_ERROR, 'Network request failed', error)
|
||||||
}
|
}
|
||||||
@@ -217,9 +214,7 @@ export class OpenAIClient implements LLMClient {
|
|||||||
try {
|
try {
|
||||||
toolResult = await tool.execute(toolInput)
|
toolResult = await tool.execute(toolInput)
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if ((error as any)?.name === 'AbortError') {
|
if ((error as any)?.name === 'AbortError') throw error
|
||||||
throw new InvokeError(InvokeErrorTypes.ABORTED, 'Aborted', error)
|
|
||||||
}
|
|
||||||
throw new InvokeError(
|
throw new InvokeError(
|
||||||
InvokeErrorTypes.TOOL_EXECUTION_ERROR,
|
InvokeErrorTypes.TOOL_EXECUTION_ERROR,
|
||||||
`Tool execution failed: ${(error as Error)?.message}`,
|
`Tool execution failed: ${(error as Error)?.message}`,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Error types and error handling for LLM invocations
|
* Error types and error handling for LLM invocations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const InvokeErrorTypes = {
|
export const InvokeErrorTypes = {
|
||||||
@@ -14,7 +14,6 @@ export const InvokeErrorTypes = {
|
|||||||
UNKNOWN: 'unknown',
|
UNKNOWN: 'unknown',
|
||||||
|
|
||||||
// Non-retryable
|
// Non-retryable
|
||||||
ABORTED: 'aborted', // User aborted via AbortSignal — instance has name='AbortError'
|
|
||||||
CONFIG_ERROR: 'config_error', // Invalid local configuration or hook
|
CONFIG_ERROR: 'config_error', // Invalid local configuration or hook
|
||||||
AUTH_ERROR: 'auth_error', // Authentication failed
|
AUTH_ERROR: 'auth_error', // Authentication failed
|
||||||
CONTEXT_LENGTH: 'context_length', // Prompt too long
|
CONTEXT_LENGTH: 'context_length', // Prompt too long
|
||||||
@@ -44,9 +43,7 @@ export class InvokeError extends Error {
|
|||||||
|
|
||||||
constructor(type: InvokeErrorType, message: string, rawError?: unknown, rawResponse?: unknown) {
|
constructor(type: InvokeErrorType, message: string, rawError?: unknown, rawResponse?: unknown) {
|
||||||
super(message)
|
super(message)
|
||||||
// ABORTED conforms to the web platform convention so any consumer using
|
this.name = 'InvokeError'
|
||||||
// `err.name === 'AbortError'` (including native DOMException handlers) Just Works.
|
|
||||||
this.name = type === InvokeErrorTypes.ABORTED ? 'AbortError' : 'InvokeError'
|
|
||||||
this.type = type
|
this.type = type
|
||||||
this.retryable = RETRYABLE_TYPES.includes(type)
|
this.retryable = RETRYABLE_TYPES.includes(type)
|
||||||
this.rawError = rawError
|
this.rawError = rawError
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ async function withRetry<T>(
|
|||||||
try {
|
try {
|
||||||
return await fn()
|
return await fn()
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
if ((error as any)?.name === 'AbortError') throw error
|
||||||
if (error instanceof InvokeError && !error.retryable) throw error
|
if (error instanceof InvokeError && !error.retryable) throw error
|
||||||
attempt++
|
attempt++
|
||||||
if (attempt > settings.maxRetries) throw error
|
if (attempt > settings.maxRetries) throw error
|
||||||
|
|||||||
Reference in New Issue
Block a user