diff --git a/packages/core/src/PageAgentCore.ts b/packages/core/src/PageAgentCore.ts index 2716043..97afc1a 100644 --- a/packages/core/src/PageAgentCore.ts +++ b/packages/core/src/PageAgentCore.ts @@ -2,7 +2,7 @@ * Copyright (C) 2025 Alibaba Group Holding Limited * 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 chalk from 'chalk' import zod from 'zod' @@ -107,7 +107,7 @@ export class PageAgentCore extends EventTarget { this.#emitHistoryChange() }) 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) this.emitActivity({ type: 'error', message }) // Also push to history for panel rendering @@ -115,6 +115,7 @@ export class PageAgentCore extends EventTarget { type: 'error', errorType: 'error', message, + rawResponse: (error as InvokeError).rawResponse, }) this.#emitHistoryChange() }) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index b63c008..91c94a0 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -76,6 +76,7 @@ export interface ErrorEvent { message: string attempt?: number maxAttempts?: number + rawResponse?: unknown } /** diff --git a/packages/llms/src/OpenAIClient.ts b/packages/llms/src/OpenAIClient.ts index 72dfd56..f9273a7 100644 --- a/packages/llms/src/OpenAIClient.ts +++ b/packages/llms/src/OpenAIClient.ts @@ -107,14 +107,23 @@ export class OpenAIClient implements LLMClient { case 'length': throw new InvokeError( InvokeErrorType.CONTEXT_LENGTH, - 'Response truncated: max tokens reached' + 'Response truncated: max tokens reached', + undefined, + data ) 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: throw new InvokeError( 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( InvokeErrorType.NO_TOOL_CALL, 'No tool call found in response', - normalizedData + undefined, + data ) } @@ -137,7 +147,8 @@ export class OpenAIClient implements LLMClient { throw new InvokeError( InvokeErrorType.UNKNOWN, `Tool "${toolCallName}" not found in tools`, - normalizedData + undefined, + data ) } @@ -147,7 +158,8 @@ export class OpenAIClient implements LLMClient { throw new InvokeError( InvokeErrorType.INVALID_TOOL_ARGS, 'No tool call arguments found', - normalizedData + undefined, + data ) } @@ -158,7 +170,8 @@ export class OpenAIClient implements LLMClient { throw new InvokeError( InvokeErrorType.INVALID_TOOL_ARGS, 'Failed to parse tool arguments as JSON', - error + error, + data ) } @@ -169,7 +182,8 @@ export class OpenAIClient implements LLMClient { throw new InvokeError( InvokeErrorType.INVALID_TOOL_ARGS, 'Tool arguments validation failed', - validation.error + validation.error, + data ) } const toolInput = validation.data @@ -182,7 +196,8 @@ export class OpenAIClient implements LLMClient { throw new InvokeError( InvokeErrorType.TOOL_EXECUTION_ERROR, `Tool execution failed: ${(e as Error).message}`, - e + e, + data ) } diff --git a/packages/llms/src/errors.ts b/packages/llms/src/errors.ts index 5aa05e1..4970d4c 100644 --- a/packages/llms/src/errors.ts +++ b/packages/llms/src/errors.ts @@ -25,14 +25,18 @@ export class InvokeError extends Error { type: InvokeErrorType retryable: boolean statusCode?: number + /* raw error (provided if this error is caused by another error) */ 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) this.name = 'InvokeError' this.type = type this.retryable = this.isRetryable(type) this.rawError = rawError + this.rawResponse = rawResponse } private isRetryable(type: InvokeErrorType): boolean { diff --git a/packages/llms/src/index.ts b/packages/llms/src/index.ts index fb9f9ed..12a861a 100644 --- a/packages/llms/src/index.ts +++ b/packages/llms/src/index.ts @@ -3,7 +3,7 @@ import { DEFAULT_TEMPERATURE, LLM_MAX_RETRIES } from './constants' import { InvokeError } from './errors' 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 { // Runtime validation as defensive programming (types already guarantee these)