diff --git a/web/app/(commonLayout)/apps/AppCard.tsx b/web/app/(commonLayout)/apps/AppCard.tsx index e3ef016c85..5e3b4843ba 100644 --- a/web/app/(commonLayout)/apps/AppCard.tsx +++ b/web/app/(commonLayout)/apps/AppCard.tsx @@ -10,7 +10,13 @@ import cn from '@/utils/classnames' import type { App } from '@/types/app' import Confirm from '@/app/components/base/confirm' import Toast, { ToastContext } from '@/app/components/base/toast' -import { copyApp, deleteApp, exportAppConfig, updateAppInfo } from '@/service/apps' +import { + copyApp, + deleteApp, + exportAppConfig, + fetchApiKeysList, + updateAppInfo, +} from '@/service/apps' import DuplicateAppModal from '@/app/components/app/duplicate-modal' import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal' import AppIcon from '@/app/components/base/app-icon' @@ -34,8 +40,8 @@ import { AppTypeIcon } from '@/app/components/app/type-selector' import Button from '@/app/components/base/button' export type AppCardProps = { - app: App - onRefresh?: () => void + app: App; + onRefresh?: () => void; } const AppCard = ({ app, onRefresh }: AppCardProps) => { @@ -60,54 +66,62 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { try { await deleteApp(app.id) notify({ type: 'success', message: t('app.appDeleted') }) - if (onRefresh) - onRefresh() + if (onRefresh) onRefresh() mutateApps() onPlanInfoChanged() } catch (e: any) { notify({ type: 'error', - message: `${t('app.appDeleteFailed')}${'message' in e ? `: ${e.message}` : ''}`, + message: `${t('app.appDeleteFailed')}${ + 'message' in e ? `: ${e.message}` : '' + }`, }) } setShowConfirmDelete(false) - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [app.id]) - const onEdit: CreateAppModalProps['onConfirm'] = useCallback(async ({ + const onEdit: CreateAppModalProps['onConfirm'] = useCallback( + async ({ + name, + icon_type, + icon, + icon_background, + description, + use_icon_as_answer_icon, + }) => { + try { + await updateAppInfo({ + appID: app.id, + name, + icon_type, + icon, + icon_background, + description, + use_icon_as_answer_icon, + }) + setShowEditModal(false) + notify({ + type: 'success', + message: t('app.editDone'), + }) + if (onRefresh) onRefresh() + mutateApps() + } + catch { + notify({ type: 'error', message: t('app.editFailed') }) + } + }, + [app.id, mutateApps, notify, onRefresh, t], + ) + + const onCopy: DuplicateAppModalProps['onConfirm'] = async ({ name, icon_type, icon, icon_background, - description, - use_icon_as_answer_icon, }) => { - try { - await updateAppInfo({ - appID: app.id, - name, - icon_type, - icon, - icon_background, - description, - use_icon_as_answer_icon, - }) - setShowEditModal(false) - notify({ - type: 'success', - message: t('app.editDone'), - }) - if (onRefresh) - onRefresh() - mutateApps() - } - catch { - notify({ type: 'error', message: t('app.editFailed') }) - } - }, [app.id, mutateApps, notify, onRefresh, t]) - - const onCopy: DuplicateAppModalProps['onConfirm'] = async ({ name, icon_type, icon, icon_background }) => { try { const newApp = await copyApp({ appID: app.id, @@ -123,8 +137,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { message: t('app.newApp.appCreated'), }) localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') - if (onRefresh) - onRefresh() + if (onRefresh) onRefresh() mutateApps() onPlanInfoChanged() getRedirection(isCurrentWorkspaceEditor, newApp, push) @@ -157,8 +170,12 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { return } try { - const workflowDraft = await fetchWorkflowDraft(`/apps/${app.id}/workflows/draft`) - const list = (workflowDraft.environment_variables || []).filter(env => env.value_type === 'secret') + const workflowDraft = await fetchWorkflowDraft( + `/apps/${app.id}/workflows/draft`, + ) + const list = (workflowDraft.environment_variables || []).filter( + env => env.value_type === 'secret', + ) if (list.length === 0) { onExport() return @@ -171,8 +188,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { } const onSwitch = () => { - if (onRefresh) - onRefresh() + if (onRefresh) onRefresh() mutateApps() setShowSwitchModal(false) } @@ -211,16 +227,18 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { e.preventDefault() setShowConfirmDelete(true) } - const onClickInstalledApp = async (e: React.MouseEvent) => { + const onClickInstalledApp = async ( + e: React.MouseEvent, + ) => { e.stopPropagation() props.onClick?.() e.preventDefault() try { - const { installed_apps }: any = await fetchInstalledAppList(app.id) || {} + const { installed_apps }: any + = (await fetchInstalledAppList(app.id)) || {} if (installed_apps?.length > 0) window.open(`/explore/installed/${installed_apps[0].id}`, '_blank') - else - throw new Error('No app found in Explore') + else throw new Error('No app found in Explore') } catch (e: any) { Toast.notify({ type: 'error', message: `${e.message || e}` }) @@ -242,10 +260,12 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { <>
- {t('app.switch')} + + {t('app.switch')} +
)} @@ -270,7 +290,15 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { useEffect(() => { setTags(app.tags) }, [app.tags]) - + const runWorkflow = async (e, appId) => { + e.preventDefault() + e.stopPropagation() + const { data: apiKeysList } = await fetchApiKeysList({ + url: `/apps/${appId}/api-keys`, + }) + if (apiKeysList?.length > 0) push(`app/${appId}/run`) + else notify({ type: 'error', message: t('app.notPublish') }) + } return ( <>
{ e.preventDefault() getRedirection(isCurrentWorkspaceEditor, app, push) }} - className='relative h-[160px] group col-span-1 bg-components-card-bg border-[1px] border-solid border-components-card-border rounded-xl shadow-sm inline-flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg' + className="relative h-[160px] group col-span-1 bg-components-card-bg border-[1px] border-solid border-components-card-border rounded-xl shadow-sm inline-flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg" > -
-
+
+
{ background={app.icon_background} imageUrl={app.icon_url} /> - +
-
-
-
{app.name}
+
+
+
+ {app.name} +
-
- {app.mode === 'advanced-chat' &&
{t('app.types.advanced').toUpperCase()}
} - {app.mode === 'chat' &&
{t('app.types.chatbot').toUpperCase()}
} - {app.mode === 'agent-chat' &&
{t('app.types.agent').toUpperCase()}
} - {app.mode === 'workflow' &&
{t('app.types.workflow').toUpperCase()}
} - {app.mode === 'completion' &&
{t('app.types.completion').toUpperCase()}
} +
+ {app.mode === 'advanced-chat' && ( +
+ {t('app.types.advanced').toUpperCase()} +
+ )} + {app.mode === 'chat' && ( +
+ {t('app.types.chatbot').toUpperCase()} +
+ )} + {app.mode === 'agent-chat' && ( +
+ {t('app.types.agent').toUpperCase()} +
+ )} + {app.mode === 'workflow' && ( +
+ {t('app.types.workflow').toUpperCase()} +
+ )} + {app.mode === 'completion' && ( +
+ {t('app.types.completion').toUpperCase()} +
+ )}
- +
-
+
{app.description}
-
+
{isCurrentWorkspaceEditor && ( <> -
{ - e.stopPropagation() - e.preventDefault() - }}> -
+
{ + e.stopPropagation() + e.preventDefault() + }} + > +
tag.id)} selectedTags={tags} @@ -344,17 +410,15 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { />
-
-
+
+
} position="br" trigger="click" btnElement={ -
- +
+
} btnClassName={open => @@ -364,7 +428,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ) } popupClassName={ - (app.mode === 'completion' || app.mode === 'chat') + app.mode === 'completion' || app.mode === 'chat' ? '!w-[256px] translate-x-[-224px]' : '!w-[160px] translate-x-[-128px]' } diff --git a/web/app/components/run/node/node.tsx b/web/app/components/run/node/node.tsx index ec2f160c26..f953e17021 100644 --- a/web/app/components/run/node/node.tsx +++ b/web/app/components/run/node/node.tsx @@ -84,7 +84,6 @@ const NodePanel: FC = ({ const isRetryNode = hasRetryNode(nodeInfo.node_type) && !!nodeInfo.retryDetail?.length const isAgentNode = nodeInfo.node_type === BlockEnum.Agent && !!nodeInfo.agentLog?.length const isToolNode = nodeInfo.node_type === BlockEnum.Tool && !!nodeInfo.agentLog?.length - console.log(nodeInfo) return (
diff --git a/web/app/components/run/text-generation/index.tsx b/web/app/components/run/text-generation/index.tsx index 164c757ab7..cf55e88775 100644 --- a/web/app/components/run/text-generation/index.tsx +++ b/web/app/components/run/text-generation/index.tsx @@ -29,7 +29,7 @@ import Loading from '@/app/components/base/loading' import { userInputsFormToPromptVariables } from '@/utils/model-config' import Res from '@/app/components/run/text-generation/result' import type { InstalledApp } from '@/models/explore' -import { DEFAULT_VALUE_MAX_LEN, appDefaultIconBackground } from '@/config' +import { appDefaultIconBackground } from '@/config' import Toast from '@/app/components/base/toast' import type { VisionFile, VisionSettings } from '@/types/app' import { Resolution, TransferMethod } from '@/types/app' @@ -80,14 +80,8 @@ const TextGeneration: FC = ({ const router = useRouter() const pathname = usePathname() - useEffect(() => { - const params = new URLSearchParams(searchParams) - if (params.has('mode')) { - params.delete('mode') - router.replace(`${pathname}?${params.toString()}`) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) + + const [queryInputs, setQueryInputs] = useState(null) // Notice this situation isCallBatchAPI but not in batch tab const [isCallBatchAPI, setIsCallBatchAPI] = useState(false) @@ -143,7 +137,25 @@ const TextGeneration: FC = ({ // eslint-disable-next-line ts/no-use-before-define showResSidebar() } + useEffect(() => { + const params = new URLSearchParams(searchParams) + if (params.has('mode')) { + params.delete('mode') + router.replace(`${pathname}?${params.toString()}`) + } + const queryInputs = Object.fromEntries(searchParams.entries()) + if(queryInputs) { + setQueryInputs(queryInputs) + setInputs(queryInputs) + router.replace(pathname) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + useEffect(() => { + if(queryInputs && typeof queryInputs === 'object' && Object.keys(queryInputs).length > 0) + handleSend() + }, [queryInputs]) const [controlRetry, setControlRetry] = useState(0) const handleRetryAllFailedTask = () => { setControlRetry(Date.now()) @@ -193,137 +205,7 @@ const TextGeneration: FC = ({ res[t('share.generation.completionResult')] = result return res }) - const checkBatchInputs = (data: string[][]) => { - if (!data || data.length === 0) { - notify({ type: 'error', message: t('share.generation.errorMsg.empty') }) - return false - } - const headerData = data[0] - let isMapVarName = true - promptConfig?.prompt_variables.forEach((item, index) => { - if (!isMapVarName) - return - - if (item.name !== headerData[index]) - isMapVarName = false - }) - - if (!isMapVarName) { - notify({ type: 'error', message: t('share.generation.errorMsg.fileStructNotMatch') }) - return false - } - - let payloadData = data.slice(1) - if (payloadData.length === 0) { - notify({ type: 'error', message: t('share.generation.errorMsg.atLeastOne') }) - return false - } - - // check middle empty line - const allEmptyLineIndexes = payloadData.filter(item => item.every(i => i === '')).map(item => payloadData.indexOf(item)) - if (allEmptyLineIndexes.length > 0) { - let hasMiddleEmptyLine = false - let startIndex = allEmptyLineIndexes[0] - 1 - allEmptyLineIndexes.forEach((index) => { - if (hasMiddleEmptyLine) - return - - if (startIndex + 1 !== index) { - hasMiddleEmptyLine = true - return - } - startIndex++ - }) - - if (hasMiddleEmptyLine) { - notify({ type: 'error', message: t('share.generation.errorMsg.emptyLine', { rowIndex: startIndex + 2 }) }) - return false - } - } - - // check row format - payloadData = payloadData.filter(item => !item.every(i => i === '')) - // after remove empty rows in the end, checked again - if (payloadData.length === 0) { - notify({ type: 'error', message: t('share.generation.errorMsg.atLeastOne') }) - return false - } - let errorRowIndex = 0 - let requiredVarName = '' - let moreThanMaxLengthVarName = '' - let maxLength = 0 - payloadData.forEach((item, index) => { - if (errorRowIndex !== 0) - return - - promptConfig?.prompt_variables.forEach((varItem, varIndex) => { - if (errorRowIndex !== 0) - return - if (varItem.type === 'string') { - const maxLen = varItem.max_length || DEFAULT_VALUE_MAX_LEN - if (item[varIndex].length > maxLen) { - moreThanMaxLengthVarName = varItem.name - maxLength = maxLen - errorRowIndex = index + 1 - return - } - } - if (!varItem.required) - return - - if (item[varIndex].trim() === '') { - requiredVarName = varItem.name - errorRowIndex = index + 1 - } - }) - }) - - if (errorRowIndex !== 0) { - if (requiredVarName) - notify({ type: 'error', message: t('share.generation.errorMsg.invalidLine', { rowIndex: errorRowIndex + 1, varName: requiredVarName }) }) - if (moreThanMaxLengthVarName) - notify({ type: 'error', message: t('share.generation.errorMsg.moreThanMaxLengthLine', { rowIndex: errorRowIndex + 1, varName: moreThanMaxLengthVarName, maxLength }) }) - - return false - } - return true - } - const handleRunBatch = (data: string[][]) => { - if (!checkBatchInputs(data)) - return - if (!allTasksFinished) { - notify({ type: 'info', message: t('appDebug.errorMessage.waitForBatchResponse') }) - return - } - - const payloadData = data.filter(item => !item.every(i => i === '')).slice(1) - const varLen = promptConfig?.prompt_variables.length || 0 - setIsCallBatchAPI(true) - const allTaskList: Task[] = payloadData.map((item, i) => { - const inputs: Record = {} - if (varLen > 0) { - item.slice(0, varLen).forEach((input, index) => { - inputs[promptConfig?.prompt_variables[index].key as string] = input - }) - } - return { - id: i + 1, - status: i < GROUP_SIZE ? TaskStatus.running : TaskStatus.pending, - params: { - inputs, - }, - } - }) - setAllTaskList(allTaskList) - setCurrGroupNum(0) - setControlSend(Date.now()) - // clear run once task status - setControlStopResponding(Date.now()) - - // eslint-disable-next-line ts/no-use-before-define - showResSidebar() - } const handleCompleted = (completionRes: string, taskId?: number, isSuccess?: boolean) => { const allTaskListLatest = getLatestTaskList() const batchCompletionResLatest = getBatchCompletionRes() diff --git a/web/app/components/run/text-generation/result/index.tsx b/web/app/components/run/text-generation/result/index.tsx index 90b38df6ab..682f19fa4e 100644 --- a/web/app/components/run/text-generation/result/index.tsx +++ b/web/app/components/run/text-generation/result/index.tsx @@ -330,9 +330,12 @@ const Result: FC = ({ } } + const controlSendRef = useRef() const [controlClearMoreLikeThis, setControlClearMoreLikeThis] = useState(0) useEffect(() => { - if (controlSend) { + if (controlSend && controlSendRef?.current !== controlSend) { + controlSendRef.current = controlSend + handleSend() setControlClearMoreLikeThis(Date.now()) } diff --git a/web/app/components/workflow/apo/tool-preview/apo-tools-preview.tsx b/web/app/components/workflow/apo/tool-preview/apo-tools-preview.tsx index c373293e07..cf3b328c01 100644 --- a/web/app/components/workflow/apo/tool-preview/apo-tools-preview.tsx +++ b/web/app/components/workflow/apo/tool-preview/apo-tools-preview.tsx @@ -110,18 +110,18 @@ const ApoToolsPreview = ({ onSelect, apoToolType, hidePopover }: ApoToolsPreview
{toolDetail.label[language]}
- 描述:<>{toolDetail.description[language]} + {t('apo.tool.desc')}:<>{toolDetail.description[language]}
{apoToolType === 'apo_select' ? ( <> - {toolDetail?.display.type === 'metric' &&
单位:{toolDetail.display.unit}
} - {toolDetail?.display?.type &&
输出:{t(`apo.displayType.${toolDetail?.display?.type}`)}
} + {toolDetail?.display.type === 'metric' &&
{t('apo.tool.unit')}:{toolDetail.display.unit}
} + {toolDetail?.display?.type &&
{t('apo.tool.output')}:{t(`apo.displayType.${toolDetail?.display?.type}`)}
}
@@ -132,7 +132,7 @@ const ApoToolsPreview = ({ onSelect, apoToolType, hidePopover }: ApoToolsPreview ) : (
- 输入参数: + {t('apo.tool.input')}:
{toolDetail?.parameters.map(parameter => ( { } return <>
- {/* {t('tools.setBuiltInTools.parameters')} */} - 数据测试 + + {t('apo.tool.test')}
setFormValues(newValues) } >
diff --git a/web/app/components/workflow/block-selector/index.tsx b/web/app/components/workflow/block-selector/index.tsx index c359f4b479..929cf576cb 100644 --- a/web/app/components/workflow/block-selector/index.tsx +++ b/web/app/components/workflow/block-selector/index.tsx @@ -99,7 +99,7 @@ const NodeSelector: FC = ({ return t('workflow.tabs.searchTool') return '' }, [activeTab, t]) - const TabContent = () =>
+ const TabContent = () =>
e.stopPropagation()}> {activeTab === TabsEnum.Blocks && ( = ({ )}
-
+
= ({ const { data: workflowTools } = useAllWorkflowTools() return ( -
e.stopPropagation()} className='h-full flex flex-col'> +
e.stopPropagation()} className='h-full flex flex-col min-h-[80vh]'> { !noBlocks && (
diff --git a/web/i18n/en-US/apo.ts b/web/i18n/en-US/apo.ts index 66f0b2bc6e..02feac52a9 100644 --- a/web/i18n/en-US/apo.ts +++ b/web/i18n/en-US/apo.ts @@ -16,7 +16,14 @@ const translation = { custom: 'Custom query return data', metric: 'Metric Line Chart', }, - + tool: { + test: 'Test', + use: 'Use', + desc: 'Description', + output: 'Output', + input: 'Input', + unit: 'Unit', + }, } export default translation diff --git a/web/i18n/en-US/app.ts b/web/i18n/en-US/app.ts index 27279130b1..bed4259532 100644 --- a/web/i18n/en-US/app.ts +++ b/web/i18n/en-US/app.ts @@ -180,6 +180,7 @@ const translation = { noParams: 'No parameters needed', }, showMyCreatedAppsOnly: 'Created by me', + notPublish: 'This workflow has not been published or the API-Key has not been created', } export default translation diff --git a/web/i18n/zh-Hans/apo.ts b/web/i18n/zh-Hans/apo.ts index a01190f8d8..473fde01c2 100644 --- a/web/i18n/zh-Hans/apo.ts +++ b/web/i18n/zh-Hans/apo.ts @@ -16,6 +16,14 @@ const translation = { custom: '自定义查询返回数据', metric: '指标折线图', }, + tool: { + test: '数据测试', + use: '使用', + desc: '描述', + output: '输出', + input: '输入', + unit: '单位', + }, } diff --git a/web/i18n/zh-Hans/app.ts b/web/i18n/zh-Hans/app.ts index ba44d4db31..f573946cbe 100644 --- a/web/i18n/zh-Hans/app.ts +++ b/web/i18n/zh-Hans/app.ts @@ -181,6 +181,7 @@ const translation = { }, openInExplore: '在“探索”中打开', showMyCreatedAppsOnly: '我创建的', + notPublish: '此工作流还未被发布或未创建API-Key', } export default translation diff --git a/web/service/base.ts b/web/service/base.ts index 8376ef95f4..29aa23e730 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -570,7 +570,6 @@ export const sseV1Post = ( const abortController = new AbortController() const token = localStorage.getItem('console_token') - console.log(baseOptions) const options = Object.assign({}, baseOptions, { method: 'POST', signal: abortController.signal,