diff --git a/package.json b/package.json index 124bf61542..63b94808e9 100644 --- a/package.json +++ b/package.json @@ -228,6 +228,7 @@ "proxy-agent": "^6.5.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-error-boundary": "^6.0.0", "react-hotkeys-hook": "^4.6.1", "react-i18next": "^14.1.2", "react-infinite-scroll-component": "^6.1.0", diff --git a/src/main/services/CopilotService.ts b/src/main/services/CopilotService.ts index f5c773a7cc..345ff39374 100644 --- a/src/main/services/CopilotService.ts +++ b/src/main/services/CopilotService.ts @@ -68,7 +68,11 @@ class CopilotService { constructor() { this.tokenFilePath = path.join(app.getPath('userData'), '.copilot_token') - this.headers = { ...CONFIG.DEFAULT_HEADERS } + this.headers = { + ...CONFIG.DEFAULT_HEADERS, + accept: 'application/json', + 'user-agent': 'Visual Studio Code (desktop)' + } } /** @@ -93,6 +97,7 @@ class CopilotService { 'Sec-Fetch-Site': 'none', 'Sec-Fetch-Mode': 'no-cors', 'Sec-Fetch-Dest': 'empty', + accept: 'application/json', authorization: `token ${token}` } }) diff --git a/src/main/services/ShortcutService.ts b/src/main/services/ShortcutService.ts index 12f786d797..97216e6a65 100644 --- a/src/main/services/ShortcutService.ts +++ b/src/main/services/ShortcutService.ts @@ -204,7 +204,7 @@ export function registerShortcuts(window: BrowserWindow) { selectionAssistantSelectTextAccelerator = formatShortcutKey(shortcut.shortcut) break - //the following ZOOMs will register shortcuts seperately, so will return + //the following ZOOMs will register shortcuts separately, so will return case 'zoom_in': globalShortcut.register('CommandOrControl+=', () => handler(window)) globalShortcut.register('CommandOrControl+numadd', () => handler(window)) diff --git a/src/main/services/WindowService.ts b/src/main/services/WindowService.ts index e2c2dc4866..66b4b8d955 100644 --- a/src/main/services/WindowService.ts +++ b/src/main/services/WindowService.ts @@ -555,9 +555,9 @@ export class WindowService { // [Windows] hacky fix // the window is minimized only when in Windows platform - // because it's a workround for Windows, see `hideMiniWindow()` + // because it's a workaround for Windows, see `hideMiniWindow()` if (this.miniWindow?.isMinimized()) { - // don't let the window being seen before we finish adusting the position across screens + // don't let the window being seen before we finish adjusting the position across screens this.miniWindow?.setOpacity(0) // DO NOT use `restore()` here, Electron has the bug with screens of different scale factor // We have to use `show()` here, then set the position and bounds diff --git a/src/renderer/src/Router.tsx b/src/renderer/src/Router.tsx index 627fb37546..36d045aae5 100644 --- a/src/renderer/src/Router.tsx +++ b/src/renderer/src/Router.tsx @@ -4,6 +4,7 @@ import { FC, useMemo } from 'react' import { HashRouter, Route, Routes } from 'react-router-dom' import Sidebar from './components/app/Sidebar' +import { ErrorBoundary } from './components/ErrorBoundary' import TabsContainer from './components/Tab/TabContainer' import NavigationHandler from './handler/NavigationHandler' import { useNavbarPosition } from './hooks/useSettings' @@ -23,18 +24,20 @@ const Router: FC = () => { const routes = useMemo(() => { return ( - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + ) }, []) diff --git a/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx b/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx index 3a82db90fa..acb4a9c4f1 100644 --- a/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx +++ b/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx @@ -157,6 +157,7 @@ const IconWrapper = styled.div<{ $isStreaming: boolean }>` display: flex; align-items: center; justify-content: center; + flex-shrink: 0; width: 44px; height: 44px; background: ${(props) => @@ -177,13 +178,16 @@ const TitleSection = styled.div` gap: 6px; ` -const Title = styled.h3` - margin: 0 !important; - font-size: 14px !important; - font-weight: 600; - color: var(--color-text); +const Title = styled.span` + font-size: 14px; + font-weight: bold; + color: var(--color-text-1); line-height: 1.4; font-family: 'Ubuntu'; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; ` const TypeBadge = styled.div` diff --git a/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx b/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx index a548d5e163..216e247701 100644 --- a/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx +++ b/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx @@ -1,7 +1,7 @@ import CodeEditor, { CodeEditorHandles } from '@renderer/components/CodeEditor' import { isLinux, isMac, isWin } from '@renderer/config/constant' import { classNames } from '@renderer/utils' -import { Button, Modal, Splitter, Tooltip } from 'antd' +import { Button, Modal, Splitter, Tooltip, Typography } from 'antd' import { Code, Eye, Maximize2, Minimize2, SaveIcon, SquareSplitHorizontal, X } from 'lucide-react' import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -43,7 +43,7 @@ const HtmlArtifactsPopup: React.FC = ({ open, title, ht const renderHeader = () => ( setIsFullscreen(!isFullscreen)} className={classNames({ drag: isFullscreen })}> - {title} + {title} @@ -266,13 +266,13 @@ const HeaderRight = styled.div<{ $isFullscreen?: boolean }>` padding-right: ${({ $isFullscreen }) => ($isFullscreen ? (isWin ? '136px' : isLinux ? '120px' : '12px') : '12px')}; ` -const TitleText = styled.span` +const TitleText = styled(Typography.Text)` font-size: 16px; - font-weight: 600; + font-weight: bold; color: var(--color-text); white-space: nowrap; overflow: hidden; - text-overflow: ellipsis; + width: 50%; ` const ViewControls = styled.div` diff --git a/src/renderer/src/components/ErrorBoundary.tsx b/src/renderer/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000000..5bfeaeb620 --- /dev/null +++ b/src/renderer/src/components/ErrorBoundary.tsx @@ -0,0 +1,57 @@ +import { formatErrorMessage } from '@renderer/utils/error' +import { Alert, Button, Space } from 'antd' +import { ComponentType, ReactNode } from 'react' +import { ErrorBoundary, FallbackProps } from 'react-error-boundary' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +const DefaultFallback: ComponentType = (props: FallbackProps): ReactNode => { + const { t } = useTranslation() + const { error } = props + const debug = async () => { + await window.api.devTools.toggle() + } + const reload = async () => { + await window.api.reload() + } + return ( + + + + + + } + /> + + ) +} + +const ErrorBoundaryCustomized = ({ + children, + fallbackComponent +}: { + children: ReactNode + fallbackComponent?: ComponentType +}) => { + return {children} +} + +const ErrorContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + width: 100%; + padding: 8px; +` + +export { ErrorBoundaryCustomized as ErrorBoundary } diff --git a/src/renderer/src/config/prompts.ts b/src/renderer/src/config/prompts.ts index 8d52857553..a42b2452ca 100644 --- a/src/renderer/src/config/prompts.ts +++ b/src/renderer/src/config/prompts.ts @@ -166,7 +166,7 @@ export const SEARCH_SUMMARY_PROMPT = ` \` - 7. Follow up question: Based on knowledge, Fomula of Scaled Dot-Product Attention and Multi-Head Attention? + 7. Follow up question: Based on knowledge, Formula of Scaled Dot-Product Attention and Multi-Head Attention? Rephrased question: \` @@ -279,7 +279,7 @@ export const SEARCH_SUMMARY_PROMPT_WEB_ONLY = ` \` - 7. Follow up question: Based on knowledge, Fomula of Scaled Dot-Product Attention and Multi-Head Attention? + 7. Follow up question: Based on knowledge, Formula of Scaled Dot-Product Attention and Multi-Head Attention? Rephrased question: \` @@ -374,7 +374,7 @@ export const SEARCH_SUMMARY_PROMPT_KNOWLEDGE_ONLY = ` \` - 7. Follow up question: Based on knowledge, Fomula of Scaled Dot-Product Attention and Multi-Head Attention? + 7. Follow up question: Based on knowledge, Formula of Scaled Dot-Product Attention and Multi-Head Attention? Rephrased question: \` diff --git a/src/renderer/src/databases/index.ts b/src/renderer/src/databases/index.ts index fe0a5f5b5b..169988f4bf 100644 --- a/src/renderer/src/databases/index.ts +++ b/src/renderer/src/databases/index.ts @@ -66,7 +66,7 @@ db.version(6).stores({ // --- NEW VERSION 7 --- db.version(7) .stores({ - // Re-declare all tables for the new version + // Redeclare all tables for the new version files: 'id, name, origin_name, path, size, ext, type, created_at, count', topics: '&id', // Correct index for topics settings: '&id, value', @@ -79,7 +79,7 @@ db.version(7) db.version(8) .stores({ - // Re-declare all tables for the new version + // Redeclare all tables for the new version files: 'id, name, origin_name, path, size, ext, type, created_at, count', topics: '&id', // Correct index for topics settings: '&id, value', @@ -91,7 +91,7 @@ db.version(8) .upgrade((tx) => upgradeToV8(tx)) db.version(9).stores({ - // Re-declare all tables for the new version + // Redeclare all tables for the new version files: 'id, name, origin_name, path, size, ext, type, created_at, count', topics: '&id', // Correct index for topics settings: '&id, value', diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 2acbf399c8..b8e0f1def9 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Backup file format error" }, + "boundary": { + "default": { + "devtools": "Open debug panel", + "message": "It seems that something went wrong...", + "reload": "Reload" + } + }, "chat": { "chunk": { "non_json": "Returned an invalid data format" diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 92a3742373..a4830cfa84 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -808,6 +808,13 @@ "backup": { "file_format": "バックアップファイルの形式エラー" }, + "boundary": { + "default": { + "devtools": "デバッグパネルを開く", + "message": "何か問題が発生したようです...", + "reload": "再読み込み" + } + }, "chat": { "chunk": { "non_json": "無効なデータ形式が返されました" diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 1cc850db67..488e2e51f4 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Ошибка формата файла резервной копии" }, + "boundary": { + "default": { + "devtools": "Открыть панель отладки", + "message": "Похоже, возникла какая-то проблема...", + "reload": "Перезагрузить" + } + }, "chat": { "chunk": { "non_json": "Вернулся недопустимый формат данных" diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 90acff50ac..1d10d66034 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -808,6 +808,13 @@ "backup": { "file_format": "备份文件格式错误" }, + "boundary": { + "default": { + "devtools": "打开调试面板", + "message": "似乎出现了一些问题...", + "reload": "重新加载" + } + }, "chat": { "chunk": { "non_json": "返回了无效的数据格式" diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index b6f5456b17..881446f3d3 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -808,6 +808,13 @@ "backup": { "file_format": "備份檔案格式錯誤" }, + "boundary": { + "default": { + "devtools": "打開除錯面板", + "message": "似乎出現了一些問題...", + "reload": "重新載入" + } + }, "chat": { "chunk": { "non_json": "返回了無效的資料格式" diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 4a8ef581a6..3e5af2ed96 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Λάθος μορφή αρχείου που επιστρέφεται" }, + "boundary": { + "default": { + "devtools": "Άνοιγμα πίνακα αποσφαλμάτωσης", + "message": "Φαίνεται ότι προέκυψε κάποιο πρόβλημα...", + "reload": "Επαναφόρτωση" + } + }, "chat": { "chunk": { "non_json": "Επέστρεψε μη έγκυρη μορφή δεδομένων" diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index e426fca943..9bd15cfcdf 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Formato de archivo de copia de seguridad incorrecto" }, + "boundary": { + "default": { + "devtools": "Abrir el panel de depuración", + "message": "Parece que ha surgido un problema...", + "reload": "Recargar" + } + }, "chat": { "chunk": { "non_json": "Devuelve un formato de datos no válido" diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index ad64477c78..da5a60f510 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Le format du fichier de sauvegarde est incorrect" }, + "boundary": { + "default": { + "devtools": "Ouvrir le panneau de débogage", + "message": "Il semble que quelques problèmes soient survenus...", + "reload": "Recharger" + } + }, "chat": { "chunk": { "non_json": "a renvoyé un format de données invalide" diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index 789f5163cd..afad9be58d 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Formato do arquivo de backup está incorreto" }, + "boundary": { + "default": { + "devtools": "Abrir o painel de depuração", + "message": "Parece que ocorreu um problema...", + "reload": "Recarregar" + } + }, "chat": { "chunk": { "non_json": "Devolveu um formato de dados inválido" diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx index ece6389baa..a32eff2bb1 100644 --- a/src/renderer/src/pages/home/HomePage.tsx +++ b/src/renderer/src/pages/home/HomePage.tsx @@ -1,3 +1,4 @@ +import { ErrorBoundary } from '@renderer/components/ErrorBoundary' import { useAssistants } from '@renderer/hooks/useAssistant' import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings' import { useActiveTopic } from '@renderer/hooks/useTopic' @@ -100,20 +101,24 @@ const HomePage: FC = () => { )} {showAssistants && ( - + + + )} - + + + ) diff --git a/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx b/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx index fcf9b083fb..7df45c63ae 100644 --- a/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx +++ b/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx @@ -83,7 +83,7 @@ export const getFileIcon = (type?: string) => { export const FileNameRender: FC<{ file: FileMetadata }> = ({ file }) => { const [visible, setVisible] = useState(false) const isImage = (ext: string) => { - return ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp'].includes(ext) + return ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp'].includes(ext.toLocaleLowerCase()) } const fullName = FileManager.formatFileName(file) diff --git a/src/renderer/src/pages/home/Inputbar/WebSearchButton.tsx b/src/renderer/src/pages/home/Inputbar/WebSearchButton.tsx index 70b25084d5..3d84ef690a 100644 --- a/src/renderer/src/pages/home/Inputbar/WebSearchButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/WebSearchButton.tsx @@ -28,48 +28,62 @@ const WebSearchButton: FC = ({ ref, assistant, ToolbarButton }) => { const { providers } = useWebSearchProviders() const { updateAssistant } = useAssistant(assistant.id) + // 注意:assistant.enableWebSearch 有不同的语义 + /** 表示是否启用网络搜索 */ const enableWebSearch = assistant?.webSearchProviderId || assistant.enableWebSearch const WebSearchIcon = useCallback( - ({ pid, size = 18 }: { pid?: WebSearchProviderId; size?: number }) => { - const iconColor = enableWebSearch ? 'var(--color-primary)' : 'var(--color-icon)' - + ({ pid, size = 18, color }: { pid?: WebSearchProviderId; size?: number; color?: string }) => { switch (pid) { case 'bocha': - return + return case 'exa': // size微调,视觉上和其他图标平衡一些 - return + return case 'tavily': - return + return case 'searxng': - return + return case 'local-baidu': - return + return case 'local-bing': - return + return case 'local-google': - return + return default: - return + return } }, [enableWebSearch] ) - const updateSelectedWebSearchProvider = useCallback( + const updateWebSearchProvider = useCallback( async (providerId?: WebSearchProvider['id']) => { // TODO: updateAssistant有性能问题,会导致关闭快捷面板卡顿 - const currentWebSearchProviderId = assistant.webSearchProviderId - const newWebSearchProviderId = currentWebSearchProviderId === providerId ? undefined : providerId startTransition(() => { - updateAssistant({ ...assistant, webSearchProviderId: newWebSearchProviderId, enableWebSearch: false }) + updateAssistant({ + ...assistant, + webSearchProviderId: providerId, + enableWebSearch: false + }) }) }, [assistant, updateAssistant] ) - const updateSelectedWebSearchBuiltin = useCallback(async () => { + const updateQuickPanelItem = useCallback( + async (providerId?: WebSearchProvider['id']) => { + // TODO: updateAssistant有性能问题,会导致关闭快捷面板卡顿 + if (providerId === assistant.webSearchProviderId) { + updateWebSearchProvider(undefined) + } else { + updateWebSearchProvider(providerId) + } + }, + [assistant.webSearchProviderId, updateWebSearchProvider] + ) + + const updateToModelBuiltinWebSearch = useCallback(async () => { // TODO: updateAssistant有性能问题,会导致关闭快捷面板卡顿 startTransition(() => { updateAssistant({ ...assistant, webSearchProviderId: undefined, enableWebSearch: !assistant.enableWebSearch }) @@ -90,7 +104,7 @@ const WebSearchButton: FC = ({ ref, assistant, ToolbarButton }) => { icon: , isSelected: p.id === assistant?.webSearchProviderId, disabled: !WebSearchService.isWebSearchEnabled(p.id), - action: () => updateSelectedWebSearchProvider(p.id) + action: () => updateQuickPanelItem(p.id) })) .filter((o) => !o.disabled) @@ -103,7 +117,7 @@ const WebSearchButton: FC = ({ ref, assistant, ToolbarButton }) => { icon: , isSelected: assistant.enableWebSearch, disabled: !isWebSearchModelEnabled, - action: () => updateSelectedWebSearchBuiltin() + action: () => updateToModelBuiltinWebSearch() }) } @@ -115,36 +129,18 @@ const WebSearchButton: FC = ({ ref, assistant, ToolbarButton }) => { assistant?.webSearchProviderId, providers, t, - updateSelectedWebSearchBuiltin, - updateSelectedWebSearchProvider + updateQuickPanelItem, + updateToModelBuiltinWebSearch ]) const openQuickPanel = useCallback(() => { - if (assistant.webSearchProviderId) { - updateSelectedWebSearchProvider(undefined) - return - } - - if (assistant.enableWebSearch) { - updateSelectedWebSearchBuiltin() - return - } - quickPanel.open({ title: t('chat.input.web_search.label'), list: providerItems, symbol: '?', pageSize: 9 }) - }, [ - assistant.webSearchProviderId, - assistant.enableWebSearch, - quickPanel, - t, - providerItems, - updateSelectedWebSearchProvider, - updateSelectedWebSearchBuiltin - ]) + }, [quickPanel, t, providerItems]) const handleOpenQuickPanel = useCallback(() => { if (quickPanel.isVisible && quickPanel.symbol === '?') { @@ -154,18 +150,28 @@ const WebSearchButton: FC = ({ ref, assistant, ToolbarButton }) => { } }, [openQuickPanel, quickPanel]) + const onClick = useCallback(() => { + if (enableWebSearch) { + updateWebSearchProvider(undefined) + } else { + handleOpenQuickPanel() + } + }, [enableWebSearch, handleOpenQuickPanel, updateWebSearchProvider]) + useImperativeHandle(ref, () => ({ openQuickPanel })) + const color = enableWebSearch ? 'var(--color-primary)' : 'var(--color-icon)' + return ( - - + + ) diff --git a/src/renderer/src/pages/settings/DocProcessSettings/OcrProviderSettings.tsx b/src/renderer/src/pages/settings/DocProcessSettings/OcrProviderSettings.tsx index a9ba128d7a..501b8d792f 100644 --- a/src/renderer/src/pages/settings/DocProcessSettings/OcrProviderSettings.tsx +++ b/src/renderer/src/pages/settings/DocProcessSettings/OcrProviderSettings.tsx @@ -1,4 +1,5 @@ // import { loggerService } from '@logger' +import { ErrorBoundary } from '@renderer/components/ErrorBoundary' import { isBuiltinOcrProvider, OcrProvider } from '@renderer/types' import { getOcrProviderLogo } from '@renderer/utils/ocr' import { Avatar, Divider, Flex } from 'antd' @@ -35,7 +36,7 @@ const OcrProviderSettings = ({ provider }: Props) => { - {getProviderSettings()} + {getProviderSettings()} ) } diff --git a/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx b/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx index 9ad2d111ad..2a829e7925 100644 --- a/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx +++ b/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx @@ -1,4 +1,5 @@ import { PictureOutlined } from '@ant-design/icons' +import { ErrorBoundary } from '@renderer/components/ErrorBoundary' import { useTheme } from '@renderer/context/ThemeProvider' import { useAppSelector } from '@renderer/store' import { OcrProvider } from '@renderer/types' @@ -26,7 +27,7 @@ const OcrSettings: FC = () => { ] return ( - <> + {t('settings.tool.ocr.title')} @@ -35,7 +36,7 @@ const OcrSettings: FC = () => { - + ) } export default OcrSettings diff --git a/src/renderer/src/pages/settings/DocProcessSettings/OcrTesseractSettings.tsx b/src/renderer/src/pages/settings/DocProcessSettings/OcrTesseractSettings.tsx index 7e94a31194..8c85cee8bc 100644 --- a/src/renderer/src/pages/settings/DocProcessSettings/OcrTesseractSettings.tsx +++ b/src/renderer/src/pages/settings/DocProcessSettings/OcrTesseractSettings.tsx @@ -13,7 +13,6 @@ export const OcrTesseractSettings = () => { const { t } = useTranslation() const { provider } = useOcrProvider(BuiltinOcrProviderIds.tesseract) - // TODO: use error boundary if (!isOcrTesseractProvider(provider)) { throw new Error('Not tesseract provider.') } diff --git a/src/renderer/src/services/ApiService.ts b/src/renderer/src/services/ApiService.ts index e6d2c5655c..415b704225 100644 --- a/src/renderer/src/services/ApiService.ts +++ b/src/renderer/src/services/ApiService.ts @@ -477,8 +477,9 @@ export async function fetchChatCompletion({ assistant.settings?.reasoning_effort !== undefined) || (isReasoningModel(model) && (!isSupportedThinkingTokenModel(model) || !isSupportedReasoningEffortModel(model))) + // NOTE:assistant.enableWebSearch 的语义是是否启用模型内置搜索功能 const enableWebSearch = - (assistant.enableWebSearch && isWebSearchModel(model)) || + (assistant.webSearchProviderId && isWebSearchModel(model)) || isOpenRouterBuiltInWebSearchModel(model) || model.id.includes('sonar') || false diff --git a/src/renderer/src/utils/mcp-tools.ts b/src/renderer/src/utils/mcp-tools.ts index a1f1a2b095..6534c2e103 100644 --- a/src/renderer/src/utils/mcp-tools.ts +++ b/src/renderer/src/utils/mcp-tools.ts @@ -185,7 +185,7 @@ export function mcpToolsToAnthropicTools(mcpTools: MCPTool[]): Array const t: ToolUnion = { name: tool.id, description: tool.description, - // @ts-ignore ignore type as it it unknow + // @ts-ignore ignore type as it it unknown input_schema: tool.inputSchema } return t diff --git a/src/renderer/src/utils/translate.ts b/src/renderer/src/utils/translate.ts index 9e7e48590b..979f38dc83 100644 --- a/src/renderer/src/utils/translate.ts +++ b/src/renderer/src/utils/translate.ts @@ -53,7 +53,7 @@ export const detectLanguage = async (inputText: string): Promise => { - logger.info('Detect langugage by llm') + logger.info('Detect language by llm') let detectedLang = '' await fetchLanguageDetection({ text: sliceByTokens(inputText, 0, 100), @@ -65,7 +65,7 @@ const detectLanguageByLLM = async (inputText: string): Promise { - logger.info('Detect langugage by franc') + logger.info('Detect language by franc') const iso3 = franc(inputText) const isoMap: Record = { diff --git a/src/renderer/src/windows/mini/home/HomeWindow.tsx b/src/renderer/src/windows/mini/home/HomeWindow.tsx index 3444de7257..a8df8a6387 100644 --- a/src/renderer/src/windows/mini/home/HomeWindow.tsx +++ b/src/renderer/src/windows/mini/home/HomeWindow.tsx @@ -21,7 +21,7 @@ import { getMainTextContent } from '@renderer/utils/messageUtils/find' import { defaultLanguage } from '@shared/config/constant' import { IpcChannel } from '@shared/IpcChannel' import { Divider } from 'antd' -import { isEmpty } from 'lodash' +import { cloneDeep, isEmpty } from 'lodash' import { last } from 'lodash' import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -256,9 +256,19 @@ const HomeWindow: FC<{ draggable?: boolean }> = ({ draggable = true }) => { setIsFirstMessage(false) setUserInputText('') + const newAssistant = cloneDeep(currentAssistant) + if (!newAssistant.settings) { + newAssistant.settings = {} + } + newAssistant.settings.streamOutput = true + // 显式关闭这些功能 + // newAssistant.webSearchProviderId = undefined + newAssistant.mcpServers = undefined + // newAssistant.knowledge_bases = undefined + await fetchChatCompletion({ messages: messagesForContext, - assistant: { ...currentAssistant, settings: { streamOutput: true } }, + assistant: newAssistant, onChunkReceived: (chunk: Chunk) => { switch (chunk.type) { case ChunkType.THINKING_START: diff --git a/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx b/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx index 9f3762b15f..b1d3c18853 100644 --- a/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx +++ b/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx @@ -1,4 +1,5 @@ import { LoadingOutlined } from '@ant-design/icons' +import { loggerService } from '@logger' import CopyButton from '@renderer/components/CopyButton' import { useTopicMessages } from '@renderer/hooks/useMessageOperations' import { useSettings } from '@renderer/hooks/useSettings' @@ -21,6 +22,7 @@ import styled from 'styled-components' import { processMessages } from './ActionUtils' import WindowFooter from './WindowFooter' +const logger = loggerService.withContext('ActionGeneral') interface Props { action: ActionItem scrollToBottom?: () => void @@ -112,6 +114,7 @@ const ActionGeneral: FC = React.memo(({ action, scrollToBottom }) => { } if (!assistantRef.current || !topicRef.current) return + logger.debug('Before peocess message', { assistant: assistantRef.current }) processMessages( assistantRef.current, topicRef.current, diff --git a/src/renderer/src/windows/selection/action/components/ActionUtils.ts b/src/renderer/src/windows/selection/action/components/ActionUtils.ts index 541a4e3bb0..5f41b9b107 100644 --- a/src/renderer/src/windows/selection/action/components/ActionUtils.ts +++ b/src/renderer/src/windows/selection/action/components/ActionUtils.ts @@ -10,6 +10,7 @@ import { Chunk, ChunkType } from '@renderer/types/chunk' import { AssistantMessageStatus, MessageBlockStatus } from '@renderer/types/newMessage' import { formatErrorMessage, isAbortError } from '@renderer/utils/error' import { createErrorBlock, createMainTextBlock, createThinkingBlock } from '@renderer/utils/messageUtils/create' +import { cloneDeep } from 'lodash' const logger = loggerService.withContext('ActionUtils') @@ -53,9 +54,19 @@ export const processMessages = async ( let finished = false + const newAssistant = cloneDeep(assistant) + if (!newAssistant.settings) { + newAssistant.settings = {} + } + newAssistant.settings.streamOutput = true + // 显式关闭这些功能 + newAssistant.webSearchProviderId = undefined + newAssistant.mcpServers = undefined + // newAssistant.knowledge_bases = undefined + await fetchChatCompletion({ messages: [userMessage], - assistant: { ...assistant, settings: { streamOutput: true } }, + assistant: newAssistant, onChunkReceived: (chunk: Chunk) => { if (finished) { return diff --git a/yarn.lock b/yarn.lock index 85a7d07e99..97a6971c04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8538,6 +8538,7 @@ __metadata: proxy-agent: "npm:^6.5.0" react: "npm:^19.0.0" react-dom: "npm:^19.0.0" + react-error-boundary: "npm:^6.0.0" react-hotkeys-hook: "npm:^4.6.1" react-i18next: "npm:^14.1.2" react-infinite-scroll-component: "npm:^6.1.0" @@ -19057,6 +19058,17 @@ __metadata: languageName: node linkType: hard +"react-error-boundary@npm:^6.0.0": + version: 6.0.0 + resolution: "react-error-boundary@npm:6.0.0" + dependencies: + "@babel/runtime": "npm:^7.12.5" + peerDependencies: + react: ">=16.13.1" + checksum: 10c0/1914d600dee95a14f14af4afe9867b0d35c26c4f7826d23208800ba2a99728659029aad60a6ef95e13430b4d79c2c4c9b3585f50bf508450478760d2e4e732d8 + languageName: node + linkType: hard + "react-hotkeys-hook@npm:^4.6.1": version: 4.6.2 resolution: "react-hotkeys-hook@npm:4.6.2"