mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-24 10:40:07 +08:00
Fix/aisdk error (#10563)
* Add syntax highlighting to AI SDK error cause display - Parse and format error cause as JSON with syntax highlighting - Use CodeStyleProvider context for consistent code styling - Maintain plain text fallback for non-JSON content * fix patch * chore: yarn lock * feat: provider-specific-error * chore * chore * fix: handle JSON parsing errors in AiSdkErrorBase component * fix: improve error message formatting in AiSdkToChunkAdapter * fix: remove unused MarkdownContainer and update AiSdkErrorBase to use styled div
This commit is contained in:
parent
4028b26c1d
commit
f27a481c3c
@ -6,6 +6,8 @@
|
||||
import { loggerService } from '@logger'
|
||||
import { AISDKWebSearchResult, MCPTool, WebSearchResults, WebSearchSource } from '@renderer/types'
|
||||
import { Chunk, ChunkType } from '@renderer/types/chunk'
|
||||
import { ProviderSpecificError } from '@renderer/types/provider-specific-error'
|
||||
import { formatErrorMessage } from '@renderer/utils/error'
|
||||
import { convertLinks, flushLinkConverterBuffer } from '@renderer/utils/linkConverter'
|
||||
import type { ClaudeCodeRawValue } from '@shared/agents/claudecode/types'
|
||||
import type { TextStreamPart, ToolSet } from 'ai'
|
||||
@ -355,7 +357,11 @@ export class AiSdkToChunkAdapter {
|
||||
case 'error':
|
||||
this.onChunk({
|
||||
type: ChunkType.ERROR,
|
||||
error: chunk.error as Record<string, any>
|
||||
error: new ProviderSpecificError({
|
||||
message: formatErrorMessage(chunk.error),
|
||||
provider: 'unknown',
|
||||
cause: chunk.error
|
||||
})
|
||||
})
|
||||
break
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Button } from '@heroui/button'
|
||||
import CodeViewer from '@renderer/components/CodeViewer'
|
||||
import { useCodeStyle } from '@renderer/context/CodeStyleProvider'
|
||||
import { useTimer } from '@renderer/hooks/useTimer'
|
||||
import { getHttpMessageLabel, getProviderLabel } from '@renderer/i18n/label'
|
||||
import { getProviderById } from '@renderer/services/ProviderService'
|
||||
@ -35,7 +36,7 @@ import {
|
||||
import type { ErrorMessageBlock, Message } from '@renderer/types/newMessage'
|
||||
import { formatAiSdkError, formatError, safeToString } from '@renderer/utils/error'
|
||||
import { Alert as AntdAlert, Modal } from 'antd'
|
||||
import React, { useState } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
@ -305,14 +306,36 @@ const BuiltinError = ({ error }: { error: SerializedError }) => {
|
||||
// 作为 base,渲染公共字段,应当在 ErrorDetailList 中渲染
|
||||
const AiSdkErrorBase = ({ error }: { error: SerializedAiSdkError }) => {
|
||||
const { t } = useTranslation()
|
||||
const { highlightCode } = useCodeStyle()
|
||||
const [highlightedString, setHighlightedString] = useState('')
|
||||
const cause = error.cause
|
||||
|
||||
useEffect(() => {
|
||||
const highlight = async () => {
|
||||
try {
|
||||
const result = await highlightCode(JSON.stringify(JSON.parse(cause || '{}'), null, 2), 'json')
|
||||
setHighlightedString(result)
|
||||
} catch {
|
||||
setHighlightedString(cause || '')
|
||||
}
|
||||
}
|
||||
const timer = setTimeout(highlight, 0)
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [highlightCode, cause])
|
||||
|
||||
return (
|
||||
<>
|
||||
<BuiltinError error={error} />
|
||||
{cause && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.cause')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.cause}</ErrorDetailValue>
|
||||
<ErrorDetailValue>
|
||||
<div
|
||||
className="markdown [&_pre]:!bg-transparent [&_pre_span]:whitespace-pre-wrap"
|
||||
dangerouslySetInnerHTML={{ __html: highlightedString }}
|
||||
/>
|
||||
</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
|
||||
@ -20,6 +20,7 @@ import {
|
||||
UnsupportedFunctionalityError
|
||||
} from 'ai'
|
||||
|
||||
import { ProviderSpecificError } from './provider-specific-error'
|
||||
import { Serializable } from './serialize'
|
||||
|
||||
export interface SerializedError {
|
||||
@ -80,7 +81,7 @@ export interface SerializedAiSdkInvalidArgumentError extends SerializedAiSdkErro
|
||||
export const isSerializedAiSdkInvalidArgumentError = (
|
||||
error: SerializedError
|
||||
): error is SerializedAiSdkInvalidArgumentError => {
|
||||
return isSerializedAiSdkError(error) && 'parameter' in error && 'value' in error
|
||||
return isSerializedAiSdkError(error) && 'message' in error && error.name === 'AI_InvalidArgumentError'
|
||||
}
|
||||
|
||||
export interface SerializedAiSdkInvalidDataContentError extends SerializedAiSdkError {
|
||||
@ -198,10 +199,20 @@ export interface SerializedAiSdkNoSuchToolError extends SerializedAiSdkError {
|
||||
readonly availableTools: string[] | null
|
||||
}
|
||||
|
||||
export interface SerializedAiSdkProviderSpecificError extends SerializedAiSdkError {
|
||||
readonly provider: string
|
||||
}
|
||||
|
||||
export const isSerializedAiSdkNoSuchToolError = (error: SerializedError): error is SerializedAiSdkNoSuchToolError => {
|
||||
return isSerializedAiSdkError(error) && 'toolName' in error && 'availableTools' in error
|
||||
}
|
||||
|
||||
export const isSerializedAiSdkProviderSpecificError = (
|
||||
error: SerializedError
|
||||
): error is SerializedAiSdkProviderSpecificError => {
|
||||
return isSerializedAiSdkError(error) && 'provider' in error
|
||||
}
|
||||
|
||||
export interface SerializedAiSdkRetryError extends SerializedAiSdkError {
|
||||
readonly reason: string
|
||||
readonly lastError: Serializable
|
||||
@ -277,6 +288,7 @@ export type AiSdkErrorUnion =
|
||||
| NoSuchModelError
|
||||
| NoSuchProviderError
|
||||
| NoSuchToolError
|
||||
| ProviderSpecificError
|
||||
| RetryError
|
||||
| ToolCallRepairError
|
||||
| TypeValidationError
|
||||
@ -297,6 +309,7 @@ export type SerializedAiSdkErrorUnion =
|
||||
| SerializedAiSdkNoSuchModelError
|
||||
| SerializedAiSdkNoSuchProviderError
|
||||
| SerializedAiSdkNoSuchToolError
|
||||
| SerializedAiSdkProviderSpecificError
|
||||
| SerializedAiSdkRetryError
|
||||
| SerializedAiSdkToolCallRepairError
|
||||
| SerializedAiSdkTypeValidationError
|
||||
@ -317,6 +330,7 @@ export const isSerializedAiSdkErrorUnion = (error: SerializedError): error is Se
|
||||
isSerializedAiSdkNoSuchModelError(error) ||
|
||||
isSerializedAiSdkNoSuchProviderError(error) ||
|
||||
isSerializedAiSdkNoSuchToolError(error) ||
|
||||
isSerializedAiSdkProviderSpecificError(error) ||
|
||||
isSerializedAiSdkRetryError(error) ||
|
||||
isSerializedAiSdkToolCallRepairError(error) ||
|
||||
isSerializedAiSdkTypeValidationError(error) ||
|
||||
|
||||
29
src/renderer/src/types/provider-specific-error.ts
Normal file
29
src/renderer/src/types/provider-specific-error.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { AISDKError } from 'ai'
|
||||
|
||||
const name = 'AI_ProviderSpecificError'
|
||||
const marker = `vercel.ai.error.${name}`
|
||||
const symbol = Symbol.for(marker)
|
||||
|
||||
export class ProviderSpecificError extends AISDKError {
|
||||
// @ts-ignore
|
||||
private readonly [symbol] = true // used in isInstance
|
||||
|
||||
readonly provider: string
|
||||
|
||||
constructor({
|
||||
message,
|
||||
provider,
|
||||
cause
|
||||
}: {
|
||||
message: string
|
||||
provider: string
|
||||
cause?: unknown
|
||||
}) {
|
||||
super({ name, message, cause })
|
||||
this.provider = provider
|
||||
}
|
||||
|
||||
static isInstance(error: unknown): error is ProviderSpecificError {
|
||||
return AISDKError.hasMarker(error, marker)
|
||||
}
|
||||
}
|
||||
@ -199,6 +199,7 @@ export const serializeError = (error: AiSdkErrorUnion): SerializedError => {
|
||||
? serializeInvalidToolInputError(error.originalError)
|
||||
: serializeNoSuchToolError(error.originalError)
|
||||
if ('functionality' in error) serializedError.functionality = error.functionality
|
||||
if ('provider' in error) serializedError.provider = error.provider
|
||||
|
||||
return serializedError
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user