feat: chat error msg (#4826)

* perf: i18n

* feat: chat error msg

* feat: doc
This commit is contained in:
Archer
2025-05-16 12:07:11 +08:00
committed by GitHub
parent 554b2ca8dc
commit e145f63554
18 changed files with 74 additions and 42 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

View File

@@ -12,6 +12,7 @@ weight: 791
1. 切换 SessionId 来替代 JWT 实现登录鉴权,可控制最大登录客户端数量。 1. 切换 SessionId 来替代 JWT 实现登录鉴权,可控制最大登录客户端数量。
2. 新的商业版 License 管理模式。 2. 新的商业版 License 管理模式。
3. 公众号调用,显示记录 chat 对话错误,方便排查。
## ⚙️ 优化 ## ⚙️ 优化

View File

@@ -132,7 +132,9 @@ weight: 506
### 公众号没响应 ### 公众号没响应
检查应用对话日志,如果有对话日志,但是微信公众号无响应,则是白名单 IP未成功。 检查应用对话日志,如果有对话日志,但是微信公众号无响应,则是白名单 IP未成功。
添加白名单IP 后,通常需要等待几分钟微信更新。 添加白名单IP 后,通常需要等待几分钟微信更新。可以在对话日志中,找点错误日志。
![](/imgs/official_account_faq.png)
### 如何新开一个聊天记录 ### 如何新开一个聊天记录

View File

@@ -112,6 +112,7 @@ export type ChatItemSchema = (UserChatItemType | SystemChatItemType | AIChatItem
appId: string; appId: string;
time: Date; time: Date;
durationSeconds?: number; durationSeconds?: number;
errorMsg?: string;
}; };
export type AdminFbkType = { export type AdminFbkType = {
@@ -143,6 +144,7 @@ export type ChatSiteItemType = (UserChatItemType | SystemChatItemType | AIChatIt
responseData?: ChatHistoryItemResType[]; responseData?: ChatHistoryItemResType[];
time?: Date; time?: Date;
durationSeconds?: number; durationSeconds?: number;
errorMsg?: string;
} & ChatBoxInputType & } & ChatBoxInputType &
ResponseTagItemType; ResponseTagItemType;

View File

@@ -57,14 +57,19 @@ export const addLog = {
level === LogLevelEnum.error && console.error(obj); level === LogLevelEnum.error && console.error(obj);
// store // store log
if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) { if (level >= STORE_LOG_LEVEL && connectionMongo.connection.readyState === 1) {
// store log (async () => {
getMongoLog().create({ try {
text: msg, await getMongoLog().create({
level, text: msg,
metadata: obj level,
}); metadata: obj
});
} catch (error) {
console.error('store log error', error);
}
})();
} }
}, },
debug(msg: string, obj?: Record<string, any>) { debug(msg: string, obj?: Record<string, any>) {

View File

@@ -61,6 +61,7 @@ const ChatItemSchema = new Schema({
type: Array, type: Array,
default: [] default: []
}, },
errorMsg: String,
userGoodFeedback: { userGoodFeedback: {
type: String type: String
}, },

View File

@@ -32,6 +32,7 @@ type Props = {
content: [UserChatItemType & { dataId?: string }, AIChatItemType & { dataId?: string }]; content: [UserChatItemType & { dataId?: string }, AIChatItemType & { dataId?: string }];
metadata?: Record<string, any>; metadata?: Record<string, any>;
durationSeconds: number; //s durationSeconds: number; //s
errorMsg?: string;
}; };
export async function saveChat({ export async function saveChat({
@@ -50,6 +51,7 @@ export async function saveChat({
outLinkUid, outLinkUid,
content, content,
durationSeconds, durationSeconds,
errorMsg,
metadata = {} metadata = {}
}: Props) { }: Props) {
if (!chatId || chatId === 'NO_RECORD_HISTORIES') return; if (!chatId || chatId === 'NO_RECORD_HISTORIES') return;
@@ -104,7 +106,8 @@ export async function saveChat({
return { return {
...item, ...item,
[DispatchNodeResponseKeyEnum.nodeResponse]: nodeResponse, [DispatchNodeResponseKeyEnum.nodeResponse]: nodeResponse,
durationSeconds durationSeconds,
errorMsg
}; };
} }
return item; return item;

View File

@@ -35,6 +35,7 @@
"delete_all_input_guide_confirm": "Are you sure you want to clear the input guide lexicon?", "delete_all_input_guide_confirm": "Are you sure you want to clear the input guide lexicon?",
"download_chunks": "Download data", "download_chunks": "Download data",
"empty_directory": "This directory is empty~", "empty_directory": "This directory is empty~",
"error_message": "error message",
"file_amount_over": "Exceeded maximum file quantity {{max}}", "file_amount_over": "Exceeded maximum file quantity {{max}}",
"file_input": "File input", "file_input": "File input",
"file_input_tip": "You can obtain the link to the corresponding file through the \"File Link\" of the [Plug-in Start] node", "file_input_tip": "You can obtain the link to the corresponding file through the \"File Link\" of the [Plug-in Start] node",

View File

@@ -1134,7 +1134,7 @@
"support.wallet.subscription.AI points usage tip": "Each time the AI model is called, a certain amount of AI points will be consumed. For specific calculation standards, please refer to the 'Pricing' above.", "support.wallet.subscription.AI points usage tip": "Each time the AI model is called, a certain amount of AI points will be consumed. For specific calculation standards, please refer to the 'Pricing' above.",
"support.wallet.subscription.Ai points": "AI Points Calculation Standards", "support.wallet.subscription.Ai points": "AI Points Calculation Standards",
"support.wallet.subscription.Current plan": "Current Package", "support.wallet.subscription.Current plan": "Current Package",
"support.wallet.subscription.Extra ai points": "Extra AI Points", "support.wallet.subscription.Extra ai points": "AI points",
"support.wallet.subscription.Extra dataset size": "Extra Dataset Capacity", "support.wallet.subscription.Extra dataset size": "Extra Dataset Capacity",
"support.wallet.subscription.Extra plan": "Extra Resource Pack", "support.wallet.subscription.Extra plan": "Extra Resource Pack",
"support.wallet.subscription.Extra plan tip": "When the standard package is not enough, you can purchase extra resource packs to continue using", "support.wallet.subscription.Extra plan tip": "When the standard package is not enough, you can purchase extra resource packs to continue using",
@@ -1147,7 +1147,7 @@
"support.wallet.subscription.Team plan and usage": "Package and Usage", "support.wallet.subscription.Team plan and usage": "Package and Usage",
"support.wallet.subscription.Training weight": "Training Priority: {{weight}}", "support.wallet.subscription.Training weight": "Training Priority: {{weight}}",
"support.wallet.subscription.Update extra ai points": "Extra AI Points", "support.wallet.subscription.Update extra ai points": "Extra AI Points",
"support.wallet.subscription.Update extra dataset size": "Extra Storage", "support.wallet.subscription.Update extra dataset size": "Storage",
"support.wallet.subscription.Upgrade plan": "Upgrade Package", "support.wallet.subscription.Upgrade plan": "Upgrade Package",
"support.wallet.subscription.ai_model": "AI Language Model", "support.wallet.subscription.ai_model": "AI Language Model",
"support.wallet.subscription.function.History store": "{{amount}} Days of Chat History Retention", "support.wallet.subscription.function.History store": "{{amount}} Days of Chat History Retention",
@@ -1156,9 +1156,9 @@
"support.wallet.subscription.function.Max dataset size": "{{amount}} Dataset Indexes", "support.wallet.subscription.function.Max dataset size": "{{amount}} Dataset Indexes",
"support.wallet.subscription.function.Max members": "{{amount}} Team Members", "support.wallet.subscription.function.Max members": "{{amount}} Team Members",
"support.wallet.subscription.function.Points": "{{amount}} AI Points", "support.wallet.subscription.function.Points": "{{amount}} AI Points",
"support.wallet.subscription.mode.Month": "Monthly", "support.wallet.subscription.mode.Month": "Month",
"support.wallet.subscription.mode.Period": "Subscription Period", "support.wallet.subscription.mode.Period": "Subscription Period",
"support.wallet.subscription.mode.Year": "Yearly", "support.wallet.subscription.mode.Year": "Year",
"support.wallet.subscription.mode.Year sale": "Two Months Free", "support.wallet.subscription.mode.Year sale": "Two Months Free",
"support.wallet.subscription.point": "Points", "support.wallet.subscription.point": "Points",
"support.wallet.subscription.standardSubLevel.custom": "Custom", "support.wallet.subscription.standardSubLevel.custom": "Custom",
@@ -1167,7 +1167,7 @@
"support.wallet.subscription.standardSubLevel.experience": "Experience", "support.wallet.subscription.standardSubLevel.experience": "Experience",
"support.wallet.subscription.standardSubLevel.experience_desc": "Unlock the full functionality of FastGPT", "support.wallet.subscription.standardSubLevel.experience_desc": "Unlock the full functionality of FastGPT",
"support.wallet.subscription.standardSubLevel.free": "Free", "support.wallet.subscription.standardSubLevel.free": "Free",
"support.wallet.subscription.standardSubLevel.free desc": "Basic functions can be used for free every month. If the system is not logged in for 30 consecutive days, the Dataset will be automatically cleared.", "support.wallet.subscription.standardSubLevel.free desc": "Free trial of core features. \nIf you haven't logged in for 30 days, the knowledge base will be cleared.",
"support.wallet.subscription.standardSubLevel.team": "Team", "support.wallet.subscription.standardSubLevel.team": "Team",
"support.wallet.subscription.standardSubLevel.team_desc": "Suitable for small teams to build Dataset applications and provide external services", "support.wallet.subscription.standardSubLevel.team_desc": "Suitable for small teams to build Dataset applications and provide external services",
"support.wallet.subscription.status.active": "Active", "support.wallet.subscription.status.active": "Active",

View File

@@ -35,6 +35,7 @@
"delete_all_input_guide_confirm": "确定要清空输入引导词库吗?", "delete_all_input_guide_confirm": "确定要清空输入引导词库吗?",
"download_chunks": "下载数据", "download_chunks": "下载数据",
"empty_directory": "这个目录已经没东西可选了~", "empty_directory": "这个目录已经没东西可选了~",
"error_message": "错误信息",
"file_amount_over": "超出最大文件数量 {{max}}", "file_amount_over": "超出最大文件数量 {{max}}",
"file_input": "系统文件", "file_input": "系统文件",
"file_input_tip": "可通过【插件开始】节点的“文件链接”获取对应文件的链接", "file_input_tip": "可通过【插件开始】节点的“文件链接”获取对应文件的链接",

View File

@@ -1166,7 +1166,7 @@
"support.wallet.subscription.standardSubLevel.experience": "体验版", "support.wallet.subscription.standardSubLevel.experience": "体验版",
"support.wallet.subscription.standardSubLevel.experience_desc": "可解锁 FastGPT 完整功能", "support.wallet.subscription.standardSubLevel.experience_desc": "可解锁 FastGPT 完整功能",
"support.wallet.subscription.standardSubLevel.free": "免费版", "support.wallet.subscription.standardSubLevel.free": "免费版",
"support.wallet.subscription.standardSubLevel.free desc": "每月均可免费使用基础功能,连续 30 天未登录系统,将会自动清除知识库", "support.wallet.subscription.standardSubLevel.free desc": "核心功能免费试用。30 天未登录,将会清空知识库",
"support.wallet.subscription.standardSubLevel.team": "团队版", "support.wallet.subscription.standardSubLevel.team": "团队版",
"support.wallet.subscription.standardSubLevel.team_desc": "适合小团队构建知识库应用并提供对外服务", "support.wallet.subscription.standardSubLevel.team_desc": "适合小团队构建知识库应用并提供对外服务",
"support.wallet.subscription.status.active": "生效中", "support.wallet.subscription.status.active": "生效中",

View File

@@ -33,6 +33,7 @@
"delete_all_input_guide_confirm": "確定要清除輸入導引詞彙庫嗎?", "delete_all_input_guide_confirm": "確定要清除輸入導引詞彙庫嗎?",
"download_chunks": "下載資料", "download_chunks": "下載資料",
"empty_directory": "此目錄中已無項目可選~", "empty_directory": "此目錄中已無項目可選~",
"error_message": "錯誤訊息",
"file_amount_over": "超出檔案數量上限 {{max}}", "file_amount_over": "超出檔案數量上限 {{max}}",
"file_input": "檔案輸入", "file_input": "檔案輸入",
"file_input_tip": "可透過「外掛程式啟動」節點的「檔案連結」取得對應檔案的連結", "file_input_tip": "可透過「外掛程式啟動」節點的「檔案連結」取得對應檔案的連結",

View File

@@ -1166,7 +1166,7 @@
"support.wallet.subscription.standardSubLevel.experience": "體驗版", "support.wallet.subscription.standardSubLevel.experience": "體驗版",
"support.wallet.subscription.standardSubLevel.experience_desc": "可解鎖 FastGPT 完整功能", "support.wallet.subscription.standardSubLevel.experience_desc": "可解鎖 FastGPT 完整功能",
"support.wallet.subscription.standardSubLevel.free": "免費版", "support.wallet.subscription.standardSubLevel.free": "免費版",
"support.wallet.subscription.standardSubLevel.free desc": "每月可免費使用基本功能。若連續 30 天未登入系統,系統將自動清除知識庫", "support.wallet.subscription.standardSubLevel.free desc": "核心功能免費試用。 \n30 天未登錄,將會清空知識庫",
"support.wallet.subscription.standardSubLevel.team": "團隊版", "support.wallet.subscription.standardSubLevel.team": "團隊版",
"support.wallet.subscription.standardSubLevel.team_desc": "適合小團隊建構知識庫應用並提供對外服務", "support.wallet.subscription.standardSubLevel.team_desc": "適合小團隊建構知識庫應用並提供對外服務",
"support.wallet.subscription.status.active": "使用中", "support.wallet.subscription.status.active": "使用中",

View File

@@ -1,6 +1,6 @@
{ {
"name": "app", "name": "app",
"version": "4.9.8", "version": "4.9.9",
"private": false, "private": false,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",

View File

@@ -35,6 +35,7 @@ import {
import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils'; import { addStatisticalDataToHistoryItem } from '@/global/core/chat/utils';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import ChatBoxDivider from '../../../Divider';
const ResponseTags = dynamic(() => import('./ResponseTags')); const ResponseTags = dynamic(() => import('./ResponseTags'));
@@ -371,7 +372,20 @@ const ChatItem = (props: Props) => {
</> </>
)} )}
{/* Example: Response tags. A set of dialogs only needs to be displayed once*/} {/* Example: Response tags. A set of dialogs only needs to be displayed once*/}
{i === splitAiResponseResults.length - 1 && <>{children}</>} {i === splitAiResponseResults.length - 1 && (
<>
{/* error message */}
{!!chat.errorMsg && (
<Box mt={2}>
<ChatBoxDivider icon={'common/errorFill'} text={t('chat:error_message')} />
<Box fontSize={'xs'} color={'myGray.500'}>
{chat.errorMsg}
</Box>
</Box>
)}
{children}
</>
)}
{/* 对话框底部的复制按钮 */} {/* 对话框底部的复制按钮 */}
{type == ChatRoleEnum.AI && {type == ChatRoleEnum.AI &&
value[0]?.type !== 'interactive' && value[0]?.type !== 'interactive' &&

View File

@@ -160,7 +160,7 @@ const Standard = ({
<Box fontSize={['32px', '42px']} fontWeight={'bold'} color={'myGray.900'}> <Box fontSize={['32px', '42px']} fontWeight={'bold'} color={'myGray.900'}>
{item.price} {item.price}
</Box> </Box>
<Box color={'myGray.500'} h={'40px'} fontSize={'xs'}> <Box color={'myGray.500'} minH={'40px'} fontSize={'xs'}>
{t(item.desc as any, { title: feConfigs?.systemTitle })} {t(item.desc as any, { title: feConfigs?.systemTitle })}
</Box> </Box>
@@ -183,17 +183,6 @@ const Standard = ({
</Button> </Button>
); );
} }
// feature:
// if (
// item.level === myStandardPlan?.nextSubLevel &&
// selectSubMode === myStandardPlan?.nextMode
// ) {
// return (
// <Button mt={4} mb={6} w={'100%'} variant={'whiteBase'} isDisabled>
// {t('common:support.wallet.subscription.Next plan')}
// </Button>
// );
// }
if (isCurrentPlan) { if (isCurrentPlan) {
return ( return (
<Button <Button
@@ -312,7 +301,7 @@ const RowTabs = ({
px={'12px'} px={'12px'}
py={'7px'} py={'7px'}
userSelect={'none'} userSelect={'none'}
w={['150px', '170px']} w={['150px', '190px']}
{...(value === item.value {...(value === item.value
? { ? {
color: 'white', color: 'white',

View File

@@ -55,12 +55,14 @@ async function handler(
const isPlugin = app.type === AppTypeEnum.plugin; const isPlugin = app.type === AppTypeEnum.plugin;
const isOutLink = authType === GetChatTypeEnum.outLink; const isOutLink = authType === GetChatTypeEnum.outLink;
const commonField =
'dataId obj value adminFeedback userGoodFeedback userBadFeedback time hideInUI durationSeconds errorMsg';
const fieldMap = { const fieldMap = {
[GetChatTypeEnum.normal]: `dataId obj value adminFeedback userBadFeedback userGoodFeedback time hideInUI durationSeconds ${ [GetChatTypeEnum.normal]: `${commonField} ${
DispatchNodeResponseKeyEnum.nodeResponse DispatchNodeResponseKeyEnum.nodeResponse
} ${loadCustomFeedbacks ? 'customFeedbacks' : ''}`, } ${loadCustomFeedbacks ? 'customFeedbacks' : ''}`,
[GetChatTypeEnum.outLink]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI durationSeconds ${DispatchNodeResponseKeyEnum.nodeResponse}`, [GetChatTypeEnum.outLink]: `${commonField} ${DispatchNodeResponseKeyEnum.nodeResponse}`,
[GetChatTypeEnum.team]: `dataId obj value userGoodFeedback userBadFeedback adminFeedback time hideInUI durationSeconds ${DispatchNodeResponseKeyEnum.nodeResponse}` [GetChatTypeEnum.team]: `${commonField} ${DispatchNodeResponseKeyEnum.nodeResponse}`
}; };
const { total, histories } = await getChatItems({ const { total, histories } = await getChatItems({

View File

@@ -109,6 +109,12 @@ function checkRes(data: ResponseDataType) {
*/ */
function responseError(err: any) { function responseError(err: any) {
console.log('error->', '请求错误', err); console.log('error->', '请求错误', err);
const isOutlinkPage = {
'/chat/share': true,
'/chat/team': true,
'/login': true
}[window.location.pathname];
const data = err?.response?.data || err; const data = err?.response?.data || err;
if (!err) { if (!err) {
@@ -123,7 +129,7 @@ function responseError(err: any) {
// 有报错响应 // 有报错响应
if (data?.code in TOKEN_ERROR_CODE) { if (data?.code in TOKEN_ERROR_CODE) {
if (!['/chat/share', '/chat/team', '/login'].includes(window.location.pathname)) { if (!isOutlinkPage) {
clearToken(); clearToken();
window.location.replace( window.location.replace(
getWebReqUrl(`/login?lastRoute=${encodeURIComponent(location.pathname + location.search)}`) getWebReqUrl(`/login?lastRoute=${encodeURIComponent(location.pathname + location.search)}`)
@@ -133,13 +139,17 @@ function responseError(err: any) {
return Promise.reject({ message: i18nT('common:unauth_token') }); return Promise.reject({ message: i18nT('common:unauth_token') });
} }
if ( if (
data?.statusText === TeamErrEnum.aiPointsNotEnough || data?.statusText &&
data?.statusText === TeamErrEnum.datasetSizeNotEnough || [
data?.statusText === TeamErrEnum.datasetAmountNotEnough || TeamErrEnum.aiPointsNotEnough,
data?.statusText === TeamErrEnum.appAmountNotEnough || TeamErrEnum.datasetSizeNotEnough,
data?.statusText === TeamErrEnum.pluginAmountNotEnough || TeamErrEnum.datasetAmountNotEnough,
data?.statusText === TeamErrEnum.websiteSyncNotEnough || TeamErrEnum.appAmountNotEnough,
data?.statusText === TeamErrEnum.reRankNotEnough TeamErrEnum.pluginAmountNotEnough,
TeamErrEnum.websiteSyncNotEnough,
TeamErrEnum.reRankNotEnough
].includes(data?.statusText) &&
!isOutlinkPage
) { ) {
useSystemStore.getState().setNotSufficientModalType(data.statusText); useSystemStore.getState().setNotSufficientModalType(data.statusText);
return Promise.reject(data); return Promise.reject(data);