diff --git a/docSite/content/zh-cn/docs/development/upgrading/4813.md b/docSite/content/zh-cn/docs/development/upgrading/4813.md index 1fdd705ba..ae6ecd4ed 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/4813.md +++ b/docSite/content/zh-cn/docs/development/upgrading/4813.md @@ -11,5 +11,9 @@ weight: 811 1. 2. 新增 - 文件上传方案调整,节点直接支持接收文件链接,插件自定义变量支持文件上传。 -3. 优化 - 知识库上传文件,优化报错提示 -4. 修复 - BI 图表生成无法写入文件 +3. 新增 - 对话记录增加时间显示。 +4. 优化 - 知识库上传文件,优化报错提示。 +5. 优化 - 全文检索语句,减少一轮查询。 +6. 优化 - 修改 findLast 为 [...array].reverse().find,适配旧版浏览器。 +7. 优化 - Markdown 组件自动空格,避免分割 url 中的中文。 +8. 修复 - BI 图表生成无法写入文件。 diff --git a/packages/global/core/workflow/runtime/utils.ts b/packages/global/core/workflow/runtime/utils.ts index 9fec266e6..4aadbf57d 100644 --- a/packages/global/core/workflow/runtime/utils.ts +++ b/packages/global/core/workflow/runtime/utils.ts @@ -34,7 +34,7 @@ export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number 2. Check that the workflow starts at the interaction node */ export const getLastInteractiveValue = (histories: ChatItemType[]) => { - const lastAIMessage = histories.findLast((item) => item.obj === ChatRoleEnum.AI); + const lastAIMessage = [...histories].reverse().find((item) => item.obj === ChatRoleEnum.AI); if (lastAIMessage) { const lastValue = lastAIMessage.value[lastAIMessage.value.length - 1]; diff --git a/packages/service/core/app/plugin/utils.ts b/packages/service/core/app/plugin/utils.ts index 4cbff4b95..4ea86b06e 100644 --- a/packages/service/core/app/plugin/utils.ts +++ b/packages/service/core/app/plugin/utils.ts @@ -7,14 +7,20 @@ import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants'; 1. Commercial plugin: n points per times 2. Other plugin: sum of children points */ -export const computedPluginUsage = async ( - plugin: PluginRuntimeType, - childrenUsage: ChatNodeUsageType[] -) => { +export const computedPluginUsage = async ({ + plugin, + childrenUsage, + error +}: { + plugin: PluginRuntimeType; + childrenUsage: ChatNodeUsageType[]; + error?: boolean; +}) => { const { source } = await splitCombinePluginId(plugin.id); // Commercial plugin: n points per times if (source === PluginSourceEnum.commercial) { + if (error) return 0; return plugin.currentCost ?? 0; } diff --git a/packages/service/core/workflow/dispatch/plugin/run.ts b/packages/service/core/workflow/dispatch/plugin/run.ts index 32dad35ae..6d5fe3164 100644 --- a/packages/service/core/workflow/dispatch/plugin/run.ts +++ b/packages/service/core/workflow/dispatch/plugin/run.ts @@ -112,7 +112,11 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise import('./chat/QuestionGuide'), { ssr: false const Markdown = ({ source = '', showAnimation = false, - isDisabled = false + isDisabled = false, + forbidZhFormat = false }: { source?: string; showAnimation?: boolean; isDisabled?: boolean; + forbidZhFormat?: boolean; }) => { const components = useMemo( () => ({ @@ -46,15 +48,35 @@ const Markdown = ({ ); const formatSource = useMemo(() => { - const formatSource = source + if (showAnimation || forbidZhFormat) return source; + + // 保护 URL 格式:https://, http://, /api/xxx + const urlPlaceholders: string[] = []; + const textWithProtectedUrls = source.replace( + /(https?:\/\/[^\s<]+[^<.,:;"')\]\s]|\/api\/[^\s]+)(?=\s|$)/g, + (match) => { + urlPlaceholders.push(match); + return `__URL_${urlPlaceholders.length - 1}__`; + } + ); + + // 处理中文与英文数字之间的分词 + const textWithSpaces = textWithProtectedUrls .replace( /([\u4e00-\u9fa5\u3000-\u303f])([a-zA-Z0-9])|([a-zA-Z0-9])([\u4e00-\u9fa5\u3000-\u303f])/g, '$1$3 $2$4' - ) // Chinese and english chars separated by space + ) + // 处理引用标记 .replace(/\n*(\[QUOTE SIGN\]\(.*\))/g, '$1'); - return formatSource; - }, [source]); + // 还原 URL + const finalText = textWithSpaces.replace( + /__URL_(\d+)__/g, + (_, index) => urlPlaceholders[parseInt(index)] + ); + + return finalText; + }, [showAnimation, source]); const urlTransform = useCallback((val: string) => { return val; diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx index 8af57c663..2f6de574c 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsx @@ -137,9 +137,8 @@ const ChatItem = (props: Props) => { }; const { t } = useTranslation(); - const { isChatting, chatType } = useContextSelector(ChatBoxContext, (v) => { - return { isChatting: v.isChatting, chatType: v.chatType }; - }); + const { isChatting, chatType } = useContextSelector(ChatBoxContext, (v) => v); + const isChatLog = chatType === 'log'; const { copyData } = useCopyData(); @@ -218,14 +217,14 @@ const ChatItem = (props: Props) => { {isChatting && type === ChatRoleEnum.AI && isLastChild ? null : ( - {chat.time && (isPc || chatType === 'log') && ( + {chat.time && (isPc || isChatLog) && ( {t(formatTimeToChatItemTime(chat.time) as any, { time: dayjs(chat.time).format('HH:mm') diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/WelcomeBox.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/WelcomeBox.tsx index 9b1b951c3..5c5e5072e 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/WelcomeBox.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/components/WelcomeBox.tsx @@ -22,7 +22,7 @@ const WelcomeBox = ({ welcomeText }: { welcomeText: string }) => { bg={'white'} boxShadow={'0 0 8px rgba(0,0,0,0.15)'} > - + diff --git a/projects/app/src/pages/app/detail/components/Plugin/Header.tsx b/projects/app/src/pages/app/detail/components/Plugin/Header.tsx index 921d92e51..fe2dd9dc2 100644 --- a/projects/app/src/pages/app/detail/components/Plugin/Header.tsx +++ b/projects/app/src/pages/app/detail/components/Plugin/Header.tsx @@ -64,7 +64,7 @@ const Header = () => { useDebounceEffect( () => { const savedSnapshot = - future.findLast((snapshot) => snapshot.isSaved) || + [...future].reverse().find((snapshot) => snapshot.isSaved) || past.find((snapshot) => snapshot.isSaved); const val = compareSnapshot( diff --git a/projects/app/src/pages/app/detail/components/Workflow/Header.tsx b/projects/app/src/pages/app/detail/components/Workflow/Header.tsx index d7ef86632..58f49e338 100644 --- a/projects/app/src/pages/app/detail/components/Workflow/Header.tsx +++ b/projects/app/src/pages/app/detail/components/Workflow/Header.tsx @@ -70,7 +70,7 @@ const Header = () => { useDebounceEffect( () => { const savedSnapshot = - future.findLast((snapshot) => snapshot.isSaved) || + [...future].reverse().find((snapshot) => snapshot.isSaved) || past.find((snapshot) => snapshot.isSaved); const val = compareSnapshot(