From e32519617ecf7e58a010ca73d2dbdfe8c785f906 Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 13 Jul 2023 09:30:16 +0800 Subject: [PATCH] feat: batch run support export as csv file (#556) --- .../share/text-generation/index.tsx | 52 ++++++++++++++----- .../share/text-generation/result/index.tsx | 10 ++-- .../run-batch/res-download/index.tsx | 41 +++++++++++++++ web/i18n/lang/common.en.ts | 1 + web/i18n/lang/common.zh.ts | 1 + web/i18n/lang/share-app.en.ts | 1 + web/i18n/lang/share-app.zh.ts | 1 + 7 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 web/app/components/share/text-generation/run-batch/res-download/index.tsx diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index db5134dd4e18b9..e63dfb6d81e384 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -10,6 +10,7 @@ import Button from '../../base/button' import { checkOrSetAccessToken } from '../utils' import s from './style.module.css' import RunBatch from './run-batch' +import ResDownload from './run-batch/res-download' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import RunOnce from '@/app/components/share/text-generation/run-once' import { fetchSavedMessage as doFetchSavedMessage, fetchAppInfo, fetchAppParams, removeMessage, saveMessage } from '@/service/share' @@ -24,7 +25,6 @@ import SavedItems from '@/app/components/app/text-generate/saved-items' import type { InstalledApp } from '@/models/explore' import { appDefaultIconBackground } from '@/config' import Toast from '@/app/components/base/toast' - const PARALLEL_LIMIT = 5 enum TaskStatus { pending = 'pending', @@ -105,6 +105,20 @@ const TextGeneration: FC = ({ const noPendingTask = pendingTaskList.length === 0 const showTaskList = allTaskList.filter(task => task.status !== TaskStatus.pending) const allTaskFinished = allTaskList.every(task => task.status === TaskStatus.completed) + const [batchCompletionRes, setBatchCompletionRes, getBatchCompletionRes] = useGetState>({}) + const exportRes = allTaskList.map((task) => { + if (allTaskList.length > 0 && !allTaskFinished) + return {} + const batchCompletionResLatest = getBatchCompletionRes() + const res: Record = {} + const { inputs, query } = task.params + promptConfig?.prompt_variables.forEach((v) => { + res[v.name] = inputs[v.key] + }) + res[t('share.generation.queryTitle')] = query + res[t('share.generation.completionResult')] = batchCompletionResLatest[task.id] + return res + }) const checkBatchInputs = (data: string[][]) => { if (!data || data.length === 0) { notify({ type: 'error', message: t('share.generation.errorMsg.empty') }) @@ -232,10 +246,9 @@ const TextGeneration: FC = ({ // eslint-disable-next-line @typescript-eslint/no-use-before-define showResSidebar() } - - const handleCompleted = (taskId?: number, isSuccess?: boolean) => { - // console.log(taskId, isSuccess) + const handleCompleted = (completionRes: string, taskId?: number) => { const allTasklistLatest = getLatestTaskList() + const batchCompletionResLatest = getBatchCompletionRes() const pendingTaskList = allTasklistLatest.filter(task => task.status === TaskStatus.pending) const nextPendingTaskId = pendingTaskList[0]?.id // console.log(`start: ${allTasklistLatest.map(item => item.status).join(',')}`) @@ -256,6 +269,12 @@ const TextGeneration: FC = ({ }) // console.log(`end: ${newAllTaskList.map(item => item.status).join(',')}`) setAllTaskList(newAllTaskList) + if (taskId) { + setBatchCompletionRes({ + ...batchCompletionResLatest, + [`${taskId}`]: completionRes, + }) + } } const fetchInitData = async () => { @@ -344,14 +363,23 @@ const TextGeneration: FC = ({
{t('share.generation.title')}
- {!isPC && ( -
- -
- )} +
+ {allTaskList.length > 0 && allTaskFinished && ( + + )} + {!isPC && ( +
+ +
+ )} +
+
diff --git a/web/app/components/share/text-generation/result/index.tsx b/web/app/components/share/text-generation/result/index.tsx index 3a19f83e91e807..4d1a9dbe06df1c 100644 --- a/web/app/components/share/text-generation/result/index.tsx +++ b/web/app/components/share/text-generation/result/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import React, { useEffect, useState } from 'react' -import { useBoolean } from 'ahooks' +import { useBoolean, useGetState } from 'ahooks' import { t } from 'i18next' import cn from 'classnames' import TextGenerationRes from '@/app/components/app/text-generate/item' @@ -27,7 +27,7 @@ export type IResultProps = { onShowRes: () => void handleSaveMessage: (messageId: string) => void taskId?: number - onCompleted: (taskId?: number, success?: boolean) => void + onCompleted: (completionRes: string, taskId?: number, success?: boolean) => void } const Result: FC = ({ @@ -53,7 +53,7 @@ const Result: FC = ({ setResponsingFalse() }, [controlStopResponding]) - const [completionRes, setCompletionRes] = useState('') + const [completionRes, setCompletionRes, getCompletionRes] = useGetState('') const { notify } = Toast const isNoData = !completionRes @@ -141,11 +141,11 @@ const Result: FC = ({ onCompleted: () => { setResponsingFalse() setMessageId(tempMessageId) - onCompleted(taskId, true) + onCompleted(getCompletionRes(), taskId, true) }, onError() { setResponsingFalse() - onCompleted(taskId, false) + onCompleted(getCompletionRes(), taskId, false) }, }, isInstalledApp, installedAppInfo?.id) } diff --git a/web/app/components/share/text-generation/run-batch/res-download/index.tsx b/web/app/components/share/text-generation/run-batch/res-download/index.tsx new file mode 100644 index 00000000000000..e5af04da9272b7 --- /dev/null +++ b/web/app/components/share/text-generation/run-batch/res-download/index.tsx @@ -0,0 +1,41 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import { + useCSVDownloader, +} from 'react-papaparse' +import { useTranslation } from 'react-i18next' +import cn from 'classnames' +import { Download02 as DownloadIcon } from '@/app/components/base/icons/src/vender/solid/general' +import Button from '@/app/components/base/button' +export type IResDownloadProps = { + isMobile: boolean + values: Record[] +} + +const ResDownload: FC = ({ + isMobile, + values, +}) => { + const { t } = useTranslation() + const { CSVDownloader, Type } = useCSVDownloader() + + return ( + + + + ) +} +export default React.memo(ResDownload) diff --git a/web/i18n/lang/common.en.ts b/web/i18n/lang/common.en.ts index 059aad438c5992..c0ddaa0b83a312 100644 --- a/web/i18n/lang/common.en.ts +++ b/web/i18n/lang/common.en.ts @@ -21,6 +21,7 @@ const translation = { copy: 'Copy', lineBreak: 'Line break', sure: 'I\'m sure', + download: 'Download', }, placeholder: { input: 'Please enter', diff --git a/web/i18n/lang/common.zh.ts b/web/i18n/lang/common.zh.ts index 529f2516c8ecf4..c558e09485e26e 100644 --- a/web/i18n/lang/common.zh.ts +++ b/web/i18n/lang/common.zh.ts @@ -21,6 +21,7 @@ const translation = { copy: '复制', lineBreak: '换行', sure: '我确定', + download: '下载', }, placeholder: { input: '请输入', diff --git a/web/i18n/lang/share-app.en.ts b/web/i18n/lang/share-app.en.ts index 55974bcc879d13..c717865c4dc96f 100644 --- a/web/i18n/lang/share-app.en.ts +++ b/web/i18n/lang/share-app.en.ts @@ -41,6 +41,7 @@ const translation = { }, title: 'AI Completion', queryTitle: 'Query content', + completionResult: 'Completion result', queryPlaceholder: 'Write your query content...', run: 'Execute', copy: 'Copy', diff --git a/web/i18n/lang/share-app.zh.ts b/web/i18n/lang/share-app.zh.ts index fcb108f610da47..4f4c1966ffcdfd 100644 --- a/web/i18n/lang/share-app.zh.ts +++ b/web/i18n/lang/share-app.zh.ts @@ -37,6 +37,7 @@ const translation = { }, title: 'AI 智能书写', queryTitle: '查询内容', + completionResult: '生成结果', queryPlaceholder: '请输入文本内容', run: '运行', copy: '拷贝',