feat: unify InvokeError; add rawResponse to historical ErrorEvent
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
* Copyright (C) 2025 Alibaba Group Holding Limited
|
* Copyright (C) 2025 Alibaba Group Holding Limited
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*/
|
*/
|
||||||
import { LLM, type Tool } from '@page-agent/llms'
|
import { InvokeError, LLM, type Tool } from '@page-agent/llms'
|
||||||
import type { PageController } from '@page-agent/page-controller'
|
import type { PageController } from '@page-agent/page-controller'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import zod from 'zod'
|
import zod from 'zod'
|
||||||
@@ -107,7 +107,7 @@ export class PageAgentCore extends EventTarget {
|
|||||||
this.#emitHistoryChange()
|
this.#emitHistoryChange()
|
||||||
})
|
})
|
||||||
this.#llm.addEventListener('error', (e) => {
|
this.#llm.addEventListener('error', (e) => {
|
||||||
const { error } = (e as CustomEvent).detail
|
const error = (e as CustomEvent).detail.error as Error | InvokeError
|
||||||
const message = String(error)
|
const message = String(error)
|
||||||
this.emitActivity({ type: 'error', message })
|
this.emitActivity({ type: 'error', message })
|
||||||
// Also push to history for panel rendering
|
// Also push to history for panel rendering
|
||||||
@@ -115,6 +115,7 @@ export class PageAgentCore extends EventTarget {
|
|||||||
type: 'error',
|
type: 'error',
|
||||||
errorType: 'error',
|
errorType: 'error',
|
||||||
message,
|
message,
|
||||||
|
rawResponse: (error as InvokeError).rawResponse,
|
||||||
})
|
})
|
||||||
this.#emitHistoryChange()
|
this.#emitHistoryChange()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ export interface ErrorEvent {
|
|||||||
message: string
|
message: string
|
||||||
attempt?: number
|
attempt?: number
|
||||||
maxAttempts?: number
|
maxAttempts?: number
|
||||||
|
rawResponse?: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -107,14 +107,23 @@ export class OpenAIClient implements LLMClient {
|
|||||||
case 'length':
|
case 'length':
|
||||||
throw new InvokeError(
|
throw new InvokeError(
|
||||||
InvokeErrorType.CONTEXT_LENGTH,
|
InvokeErrorType.CONTEXT_LENGTH,
|
||||||
'Response truncated: max tokens reached'
|
'Response truncated: max tokens reached',
|
||||||
|
undefined,
|
||||||
|
data
|
||||||
)
|
)
|
||||||
case 'content_filter':
|
case 'content_filter':
|
||||||
throw new InvokeError(InvokeErrorType.CONTENT_FILTER, 'Content filtered by safety system')
|
throw new InvokeError(
|
||||||
|
InvokeErrorType.CONTENT_FILTER,
|
||||||
|
'Content filtered by safety system',
|
||||||
|
undefined,
|
||||||
|
data
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
throw new InvokeError(
|
throw new InvokeError(
|
||||||
InvokeErrorType.UNKNOWN,
|
InvokeErrorType.UNKNOWN,
|
||||||
`Unexpected finish_reason: ${choice.finish_reason}`
|
`Unexpected finish_reason: ${choice.finish_reason}`,
|
||||||
|
undefined,
|
||||||
|
data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +137,8 @@ export class OpenAIClient implements LLMClient {
|
|||||||
throw new InvokeError(
|
throw new InvokeError(
|
||||||
InvokeErrorType.NO_TOOL_CALL,
|
InvokeErrorType.NO_TOOL_CALL,
|
||||||
'No tool call found in response',
|
'No tool call found in response',
|
||||||
normalizedData
|
undefined,
|
||||||
|
data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +147,8 @@ export class OpenAIClient implements LLMClient {
|
|||||||
throw new InvokeError(
|
throw new InvokeError(
|
||||||
InvokeErrorType.UNKNOWN,
|
InvokeErrorType.UNKNOWN,
|
||||||
`Tool "${toolCallName}" not found in tools`,
|
`Tool "${toolCallName}" not found in tools`,
|
||||||
normalizedData
|
undefined,
|
||||||
|
data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +158,8 @@ export class OpenAIClient implements LLMClient {
|
|||||||
throw new InvokeError(
|
throw new InvokeError(
|
||||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||||
'No tool call arguments found',
|
'No tool call arguments found',
|
||||||
normalizedData
|
undefined,
|
||||||
|
data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +170,8 @@ export class OpenAIClient implements LLMClient {
|
|||||||
throw new InvokeError(
|
throw new InvokeError(
|
||||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||||
'Failed to parse tool arguments as JSON',
|
'Failed to parse tool arguments as JSON',
|
||||||
error
|
error,
|
||||||
|
data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +182,8 @@ export class OpenAIClient implements LLMClient {
|
|||||||
throw new InvokeError(
|
throw new InvokeError(
|
||||||
InvokeErrorType.INVALID_TOOL_ARGS,
|
InvokeErrorType.INVALID_TOOL_ARGS,
|
||||||
'Tool arguments validation failed',
|
'Tool arguments validation failed',
|
||||||
validation.error
|
validation.error,
|
||||||
|
data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const toolInput = validation.data
|
const toolInput = validation.data
|
||||||
@@ -182,7 +196,8 @@ export class OpenAIClient implements LLMClient {
|
|||||||
throw new InvokeError(
|
throw new InvokeError(
|
||||||
InvokeErrorType.TOOL_EXECUTION_ERROR,
|
InvokeErrorType.TOOL_EXECUTION_ERROR,
|
||||||
`Tool execution failed: ${(e as Error).message}`,
|
`Tool execution failed: ${(e as Error).message}`,
|
||||||
e
|
e,
|
||||||
|
data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,14 +25,18 @@ export class InvokeError extends Error {
|
|||||||
type: InvokeErrorType
|
type: InvokeErrorType
|
||||||
retryable: boolean
|
retryable: boolean
|
||||||
statusCode?: number
|
statusCode?: number
|
||||||
|
/* raw error (provided if this error is caused by another error) */
|
||||||
rawError?: unknown
|
rawError?: unknown
|
||||||
|
/* raw response from the API (provided if this error is caused by an API calling) */
|
||||||
|
rawResponse?: unknown
|
||||||
|
|
||||||
constructor(type: InvokeErrorType, message: string, rawError?: unknown) {
|
constructor(type: InvokeErrorType, message: string, rawError?: unknown, rawResponse?: unknown) {
|
||||||
super(message)
|
super(message)
|
||||||
this.name = 'InvokeError'
|
this.name = 'InvokeError'
|
||||||
this.type = type
|
this.type = type
|
||||||
this.retryable = this.isRetryable(type)
|
this.retryable = this.isRetryable(type)
|
||||||
this.rawError = rawError
|
this.rawError = rawError
|
||||||
|
this.rawResponse = rawResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
private isRetryable(type: InvokeErrorType): boolean {
|
private isRetryable(type: InvokeErrorType): boolean {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { DEFAULT_TEMPERATURE, LLM_MAX_RETRIES } from './constants'
|
|||||||
import { InvokeError } from './errors'
|
import { InvokeError } from './errors'
|
||||||
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
|
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
|
||||||
|
|
||||||
export type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool }
|
export type { InvokeError, InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool }
|
||||||
|
|
||||||
export function parseLLMConfig(config: LLMConfig): Required<LLMConfig> {
|
export function parseLLMConfig(config: LLMConfig): Required<LLMConfig> {
|
||||||
// Runtime validation as defensive programming (types already guarantee these)
|
// Runtime validation as defensive programming (types already guarantee these)
|
||||||
|
|||||||
Reference in New Issue
Block a user