fix(ts): InvokeErrorType separate confusing value/type space

This commit is contained in:
Simon
2026-04-28 15:57:51 +08:00
parent 757fe07b4d
commit 90270fb76c
4 changed files with 31 additions and 31 deletions

View File

@@ -1,4 +1,4 @@
import { InvokeError, InvokeErrorType } from '@page-agent/llms' import { InvokeError, InvokeErrorTypes } from '@page-agent/llms'
import chalk from 'chalk' import chalk from 'chalk'
import * as z from 'zod/v4' import * as z from 'zod/v4'
@@ -137,7 +137,7 @@ function validateAction(action: any, tools: Map<string, PageAgentTool>): any {
if (!tool) { if (!tool) {
const available = Array.from(tools.keys()).join(', ') const available = Array.from(tools.keys()).join(', ')
throw new InvokeError( throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS, InvokeErrorTypes.INVALID_TOOL_ARGS,
`Unknown action "${toolName}". Available: ${available}` `Unknown action "${toolName}". Available: ${available}`
) )
} }
@@ -159,7 +159,7 @@ function validateAction(action: any, tools: Map<string, PageAgentTool>): any {
const result = schema.safeParse(value) const result = schema.safeParse(value)
if (!result.success) { if (!result.success) {
throw new InvokeError( throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS, InvokeErrorTypes.INVALID_TOOL_ARGS,
`Invalid input for action "${toolName}": ${z.prettifyError(result.error)}` `Invalid input for action "${toolName}": ${z.prettifyError(result.error)}`
) )
} }

View File

@@ -3,7 +3,7 @@
*/ */
import * as z from 'zod/v4' import * as z from 'zod/v4'
import { InvokeError, InvokeErrorType } from './errors' import { InvokeError, InvokeErrorTypes } from './errors'
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types' import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
import { modelPatch, zodToOpenAITool } from './utils' import { modelPatch, zodToOpenAITool } from './utils'
@@ -50,7 +50,7 @@ export class OpenAIClient implements LLMClient {
transformedBody = this.config.transformRequestBody(requestBody) transformedBody = this.config.transformRequestBody(requestBody)
} catch (error) { } catch (error) {
throw new InvokeError( throw new InvokeError(
InvokeErrorType.CONFIG_ERROR, InvokeErrorTypes.CONFIG_ERROR,
`transformRequestBody failed: ${(error as Error).message}`, `transformRequestBody failed: ${(error as Error).message}`,
error error
) )
@@ -73,7 +73,7 @@ export class OpenAIClient implements LLMClient {
const isAbortError = (error as any)?.name === 'AbortError' const isAbortError = (error as any)?.name === 'AbortError'
const errorMessage = isAbortError ? 'Network request aborted' : 'Network request failed' const errorMessage = isAbortError ? 'Network request aborted' : 'Network request failed'
if (!isAbortError) console.error(error) if (!isAbortError) console.error(error)
throw new InvokeError(InvokeErrorType.NETWORK_ERROR, errorMessage, error) throw new InvokeError(InvokeErrorTypes.NETWORK_ERROR, errorMessage, error)
} }
// 3. Handle HTTP errors // 3. Handle HTTP errors
@@ -84,27 +84,27 @@ export class OpenAIClient implements LLMClient {
if (response.status === 401 || response.status === 403) { if (response.status === 401 || response.status === 403) {
throw new InvokeError( throw new InvokeError(
InvokeErrorType.AUTH_ERROR, InvokeErrorTypes.AUTH_ERROR,
`Authentication failed: ${errorMessage}`, `Authentication failed: ${errorMessage}`,
errorData errorData
) )
} }
if (response.status === 429) { if (response.status === 429) {
throw new InvokeError( throw new InvokeError(
InvokeErrorType.RATE_LIMIT, InvokeErrorTypes.RATE_LIMIT,
`Rate limit exceeded: ${errorMessage}`, `Rate limit exceeded: ${errorMessage}`,
errorData errorData
) )
} }
if (response.status >= 500) { if (response.status >= 500) {
throw new InvokeError( throw new InvokeError(
InvokeErrorType.SERVER_ERROR, InvokeErrorTypes.SERVER_ERROR,
`Server error: ${errorMessage}`, `Server error: ${errorMessage}`,
errorData errorData
) )
} }
throw new InvokeError( throw new InvokeError(
InvokeErrorType.UNKNOWN, InvokeErrorTypes.UNKNOWN,
`HTTP ${response.status}: ${errorMessage}`, `HTTP ${response.status}: ${errorMessage}`,
errorData errorData
) )
@@ -115,7 +115,7 @@ export class OpenAIClient implements LLMClient {
const choice = data.choices?.[0] const choice = data.choices?.[0]
if (!choice) { if (!choice) {
throw new InvokeError(InvokeErrorType.UNKNOWN, 'No choices in response', data) throw new InvokeError(InvokeErrorTypes.UNKNOWN, 'No choices in response', data)
} }
// Check finish_reason // Check finish_reason
@@ -126,21 +126,21 @@ export class OpenAIClient implements LLMClient {
break break
case 'length': case 'length':
throw new InvokeError( throw new InvokeError(
InvokeErrorType.CONTEXT_LENGTH, InvokeErrorTypes.CONTEXT_LENGTH,
'Response truncated: max tokens reached', 'Response truncated: max tokens reached',
undefined, undefined,
data data
) )
case 'content_filter': case 'content_filter':
throw new InvokeError( throw new InvokeError(
InvokeErrorType.CONTENT_FILTER, InvokeErrorTypes.CONTENT_FILTER,
'Content filtered by safety system', 'Content filtered by safety system',
undefined, undefined,
data data
) )
default: default:
throw new InvokeError( throw new InvokeError(
InvokeErrorType.UNKNOWN, InvokeErrorTypes.UNKNOWN,
`Unexpected finish_reason: ${choice.finish_reason}`, `Unexpected finish_reason: ${choice.finish_reason}`,
undefined, undefined,
data data
@@ -155,7 +155,7 @@ export class OpenAIClient implements LLMClient {
const toolCallName = normalizedChoice?.message?.tool_calls?.[0]?.function?.name const toolCallName = normalizedChoice?.message?.tool_calls?.[0]?.function?.name
if (!toolCallName) { if (!toolCallName) {
throw new InvokeError( throw new InvokeError(
InvokeErrorType.NO_TOOL_CALL, InvokeErrorTypes.NO_TOOL_CALL,
'No tool call found in response', 'No tool call found in response',
undefined, undefined,
data data
@@ -165,7 +165,7 @@ export class OpenAIClient implements LLMClient {
const tool = tools[toolCallName] const tool = tools[toolCallName]
if (!tool) { if (!tool) {
throw new InvokeError( throw new InvokeError(
InvokeErrorType.UNKNOWN, InvokeErrorTypes.UNKNOWN,
`Tool "${toolCallName}" not found in tools`, `Tool "${toolCallName}" not found in tools`,
undefined, undefined,
data data
@@ -176,7 +176,7 @@ export class OpenAIClient implements LLMClient {
const argString = normalizedChoice.message?.tool_calls?.[0]?.function?.arguments const argString = normalizedChoice.message?.tool_calls?.[0]?.function?.arguments
if (!argString) { if (!argString) {
throw new InvokeError( throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS, InvokeErrorTypes.INVALID_TOOL_ARGS,
'No tool call arguments found', 'No tool call arguments found',
undefined, undefined,
data data
@@ -188,7 +188,7 @@ export class OpenAIClient implements LLMClient {
parsedArgs = JSON.parse(argString) parsedArgs = JSON.parse(argString)
} catch (error) { } catch (error) {
throw new InvokeError( throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS, InvokeErrorTypes.INVALID_TOOL_ARGS,
'Failed to parse tool arguments as JSON', 'Failed to parse tool arguments as JSON',
error, error,
data data
@@ -200,7 +200,7 @@ export class OpenAIClient implements LLMClient {
if (!validation.success) { if (!validation.success) {
console.error(z.prettifyError(validation.error)) console.error(z.prettifyError(validation.error))
throw new InvokeError( throw new InvokeError(
InvokeErrorType.INVALID_TOOL_ARGS, InvokeErrorTypes.INVALID_TOOL_ARGS,
'Tool arguments validation failed', 'Tool arguments validation failed',
validation.error, validation.error,
data data
@@ -214,7 +214,7 @@ export class OpenAIClient implements LLMClient {
toolResult = await tool.execute(toolInput) toolResult = await tool.execute(toolInput)
} catch (e) { } catch (e) {
throw new InvokeError( throw new InvokeError(
InvokeErrorType.TOOL_EXECUTION_ERROR, InvokeErrorTypes.TOOL_EXECUTION_ERROR,
`Tool execution failed: ${(e as Error).message}`, `Tool execution failed: ${(e as Error).message}`,
e, e,
data data

View File

@@ -2,7 +2,7 @@
* Error types and error handling for LLM invocations * Error types and error handling for LLM invocations
*/ */
export const InvokeErrorType = { export const InvokeErrorTypes = {
// Retryable // Retryable
NETWORK_ERROR: 'network_error', // Network error, retry NETWORK_ERROR: 'network_error', // Network error, retry
RATE_LIMIT: 'rate_limit', // Rate limit, retry RATE_LIMIT: 'rate_limit', // Rate limit, retry
@@ -20,7 +20,7 @@ export const InvokeErrorType = {
CONTENT_FILTER: 'content_filter', // Content filtered CONTENT_FILTER: 'content_filter', // Content filtered
} as const } as const
export type InvokeErrorType = (typeof InvokeErrorType)[keyof typeof InvokeErrorType] type InvokeErrorType = (typeof InvokeErrorTypes)[keyof typeof InvokeErrorTypes]
export class InvokeError extends Error { export class InvokeError extends Error {
type: InvokeErrorType type: InvokeErrorType
@@ -45,13 +45,13 @@ export class InvokeError extends Error {
if (isAbortError) return false if (isAbortError) return false
const retryableTypes: InvokeErrorType[] = [ const retryableTypes: InvokeErrorType[] = [
InvokeErrorType.NETWORK_ERROR, InvokeErrorTypes.NETWORK_ERROR,
InvokeErrorType.RATE_LIMIT, InvokeErrorTypes.RATE_LIMIT,
InvokeErrorType.SERVER_ERROR, InvokeErrorTypes.SERVER_ERROR,
InvokeErrorType.NO_TOOL_CALL, InvokeErrorTypes.NO_TOOL_CALL,
InvokeErrorType.INVALID_TOOL_ARGS, InvokeErrorTypes.INVALID_TOOL_ARGS,
InvokeErrorType.TOOL_EXECUTION_ERROR, InvokeErrorTypes.TOOL_EXECUTION_ERROR,
InvokeErrorType.UNKNOWN, InvokeErrorTypes.UNKNOWN,
] ]
return retryableTypes.includes(type) return retryableTypes.includes(type)
} }

View File

@@ -1,9 +1,9 @@
import { OpenAIClient } from './OpenAIClient' import { OpenAIClient } from './OpenAIClient'
import { DEFAULT_TEMPERATURE, LLM_MAX_RETRIES } from './constants' import { DEFAULT_TEMPERATURE, LLM_MAX_RETRIES } from './constants'
import { InvokeError, InvokeErrorType } from './errors' import { InvokeError, InvokeErrorTypes } from './errors'
import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types' import type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } from './types'
export { InvokeError, InvokeErrorType } export { InvokeError, InvokeErrorTypes }
export type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool } export type { InvokeOptions, InvokeResult, LLMClient, LLMConfig, Message, Tool }
export function parseLLMConfig(config: LLMConfig): Required<LLMConfig> { export function parseLLMConfig(config: LLMConfig): Required<LLMConfig> {