From c87de93498758fd3f88874df1d6c884e21fb7c2a Mon Sep 17 00:00:00 2001 From: josc146 Date: Fri, 27 Oct 2023 11:36:29 +0800 Subject: [PATCH] allow conversation with some document (.pdf, .txt) --- backend-golang/file.go | 20 ++- backend-python/routes/file_process.py | 19 +-- frontend/src/_locales/ja/main.json | 9 +- frontend/src/_locales/zh-hans/main.json | 9 +- frontend/src/pages/Chat.tsx | 129 ++++++++++++++++++-- frontend/src/stores/commonStore.ts | 20 +++ frontend/src/utils/index.tsx | 7 ++ frontend/wailsjs/go/backend_golang/App.d.ts | 2 + frontend/wailsjs/go/backend_golang/App.js | 4 + main.go | 1 + 10 files changed, 195 insertions(+), 25 deletions(-) diff --git a/backend-golang/file.go b/backend-golang/file.go index 1b441f0..e4264e8 100644 --- a/backend-golang/file.go +++ b/backend-golang/file.go @@ -53,12 +53,12 @@ type FileInfo struct { ModTime string `json:"modTime"` } -func (a *App) ReadFileInfo(fileName string) (FileInfo, error) { +func (a *App) ReadFileInfo(fileName string) (*FileInfo, error) { info, err := os.Stat(a.exDir + fileName) if err != nil { - return FileInfo{}, err + return nil, err } - return FileInfo{ + return &FileInfo{ Name: info.Name(), Size: info.Size(), IsDir: info.IsDir(), @@ -145,6 +145,20 @@ func (a *App) OpenSaveFileDialogBytes(filterPattern string, defaultFileName stri return path, nil } +// Only return the path of the selected file, because communication between frontend and backend is slow. Use AssetServer Handler to read the file. +func (a *App) OpenOpenFileDialog(filterPattern string) (string, error) { + path, err := wruntime.OpenFileDialog(a.ctx, wruntime.OpenDialogOptions{ + Filters: []wruntime.FileFilter{{Pattern: filterPattern}}, + }) + if err != nil { + return "", err + } + if path == "" { + return "", nil + } + return path, nil +} + func (a *App) OpenFileFolder(path string, relative bool) error { var absPath string var err error diff --git a/backend-python/routes/file_process.py b/backend-python/routes/file_process.py index 1195c8b..9746bcf 100644 --- a/backend-python/routes/file_process.py +++ b/backend-python/routes/file_process.py @@ -58,17 +58,22 @@ async def file_to_text( file_parsers = {".txt": parse_text, ".pdf": parse_pdf} - file_ext = os.path.splitext(params.file_name)[-1] + file_name = file_data.filename or params.file_name + file_ext = os.path.splitext(file_name)[-1] if file_ext not in file_parsers: raise HTTPException(status.HTTP_400_BAD_REQUEST, "file type not supported") - pages: Iterator[Document] = file_parsers[file_ext]( - Blob.from_data( - await file_data.read(), - encoding=params.file_encoding, - path=params.file_name, + try: + pages: Iterator[Document] = file_parsers[file_ext]( + Blob.from_data( + await file_data.read(), + encoding=params.file_encoding, + path=file_name, + ) ) - ) + pages = list(pages) + except Exception as e: + raise HTTPException(status.HTTP_400_BAD_REQUEST, f"{e}") return {"pages": pages} diff --git a/frontend/src/_locales/ja/main.json b/frontend/src/_locales/ja/main.json index c5872e6..3fcd2b1 100644 --- a/frontend/src/_locales/ja/main.json +++ b/frontend/src/_locales/ja/main.json @@ -254,5 +254,12 @@ "User Name": "ユーザー名", "Assistant Name": "アシスタント名", "Insert default system prompt at the beginning": "最初にデフォルトのシステムプロンプトを挿入", - "Format Content": "内容フォーマットの規格化" + "Format Content": "内容フォーマットの規格化", + "Add An Attachment (Accepts pdf, txt)": "添付ファイルを追加 (pdf, txtを受け付けます)", + "Uploading Attachment": "添付ファイルアップロード中", + "Remove Attachment": "添付ファイルを削除", + "The content of file": "ファイル", + "is as follows. When replying to me, consider the file content and respond accordingly:": "の内容は以下の通りです。私に返信する際は、ファイルの内容を考慮して適切に返信してください:", + "What's the file name": "ファイル名は何ですか", + "The file name is: ": "ファイル名は次のとおりです: " } \ No newline at end of file diff --git a/frontend/src/_locales/zh-hans/main.json b/frontend/src/_locales/zh-hans/main.json index 173c9bd..4cac8a9 100644 --- a/frontend/src/_locales/zh-hans/main.json +++ b/frontend/src/_locales/zh-hans/main.json @@ -254,5 +254,12 @@ "User Name": "用户名称", "Assistant Name": "AI名称", "Insert default system prompt at the beginning": "在开头自动插入默认系统提示", - "Format Content": "规范格式" + "Format Content": "规范格式", + "Add An Attachment (Accepts pdf, txt)": "添加一个附件 (支持pdf, txt)", + "Uploading Attachment": "正在上传附件", + "Remove Attachment": "移除附件", + "The content of file": "文件", + "is as follows. When replying to me, consider the file content and respond accordingly:": "内容如下。回复时考虑文件内容并做出相应回复:", + "What's the file name": "文件名是什么", + "The file name is: ": "文件名是:" } \ No newline at end of file diff --git a/frontend/src/pages/Chat.tsx b/frontend/src/pages/Chat.tsx index 1da9ca8..90569fd 100644 --- a/frontend/src/pages/Chat.tsx +++ b/frontend/src/pages/Chat.tsx @@ -10,14 +10,22 @@ import { KebabHorizontalIcon, PencilIcon, SyncIcon, TrashIcon } from '@primer/oc import logo from '../assets/images/logo.png'; import MarkdownRender from '../components/MarkdownRender'; import { ToolTipButton } from '../components/ToolTipButton'; -import { ArrowCircleUp28Regular, Delete28Regular, RecordStop28Regular, Save28Regular } from '@fluentui/react-icons'; +import { + ArrowCircleUp28Regular, + ArrowClockwise16Regular, + Attach16Regular, + Delete28Regular, + Dismiss16Regular, + RecordStop28Regular, + Save28Regular +} from '@fluentui/react-icons'; import { CopyButton } from '../components/CopyButton'; import { ReadButton } from '../components/ReadButton'; import { toast } from 'react-toastify'; import { WorkHeader } from '../components/WorkHeader'; import { DialogButton } from '../components/DialogButton'; -import { OpenFileFolder, OpenSaveFileDialog } from '../../wailsjs/go/backend_golang/App'; -import { toastWithButton } from '../utils'; +import { OpenFileFolder, OpenOpenFileDialog, OpenSaveFileDialog } from '../../wailsjs/go/backend_golang/App'; +import { bytesToReadable, toastWithButton } from '../utils'; import { PresetsButton } from './PresetsManager/PresetsButton'; import { useMediaQuery } from 'usehooks-ts'; @@ -267,6 +275,16 @@ const ChatPanel: FC = observer(() => { let targetRange = commonStore.conversationOrder.slice(startIndex, endIndex); const messages: ConversationMessage[] = []; + if (commonStore.attachmentContent) { + messages.push({ + role: 'user', + content: t('The content of file') + ` "${commonStore.attachmentName}" ` + + t('is as follows. When replying to me, consider the file content and respond accordingly:') + + '\n\n' + commonStore.attachmentContent + }); + messages.push({ role: 'user', content: t('What\'s the file name') }); + messages.push({ role: 'assistant', content: t('The file name is: ') + commonStore.attachmentName }); + } targetRange.forEach((uuid, index) => { if (uuid === welcomeUuid) return; @@ -385,16 +403,101 @@ const ChatPanel: FC = observer(() => { commonStore.setConversation({}); commonStore.setConversationOrder([]); }} /> -