### What problem does this PR solve? Fix: Fixed the issue where the error prompt box on the Agent page would be covered #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)tags/v0.20.0
| @@ -8,17 +8,14 @@ export const CodeTemplateStrMap = { | |||
| return f"result: {arg1 + arg2}" | |||
| `, | |||
| [ProgrammingLanguage.Javascript]: `const axios = require('axios'); | |||
| async function main(args) { | |||
| async function main({}) { | |||
| try { | |||
| const response = await axios.get('https://github.com/infiniflow/ragflow'); | |||
| console.log('Body:', response.data); | |||
| return 'Body:' + response.data; | |||
| } catch (error) { | |||
| console.error('Error:', error.message); | |||
| return 'Error:' + error.message; | |||
| } | |||
| } | |||
| module.exports = { main }; | |||
| `, | |||
| }`, | |||
| }; | |||
| export enum AgentGlobals { | |||
| @@ -6,7 +6,7 @@ import { IDebugSingleRequestBody } from '@/interfaces/request/agent'; | |||
| import i18n from '@/locales/config'; | |||
| import { BeginId } from '@/pages/agent/constant'; | |||
| import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks'; | |||
| import flowService from '@/services/flow-service'; | |||
| import agentService from '@/services/agent-service'; | |||
| import { buildMessageListWithUuid } from '@/utils/chat'; | |||
| import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | |||
| import { useDebounce } from 'ahooks'; | |||
| @@ -85,7 +85,7 @@ export const useFetchAgentTemplates = () => { | |||
| queryKey: [AgentApiAction.FetchAgentTemplates], | |||
| initialData: [], | |||
| queryFn: async () => { | |||
| const { data } = await flowService.listTemplates(); | |||
| const { data } = await agentService.listTemplates(); | |||
| if (Array.isArray(data?.data)) { | |||
| data.data.unshift({ | |||
| id: uuid(), | |||
| @@ -121,11 +121,16 @@ export const useFetchAgentListByPage = () => { | |||
| initialData: { kbs: [], total: 0 }, | |||
| gcTime: 0, | |||
| queryFn: async () => { | |||
| const { data } = await flowService.listCanvasTeam({ | |||
| keywords: debouncedSearchString, | |||
| page_size: pagination.pageSize, | |||
| page: pagination.current, | |||
| }); | |||
| const { data } = await agentService.listCanvasTeam( | |||
| { | |||
| params: { | |||
| keywords: debouncedSearchString, | |||
| page_size: pagination.pageSize, | |||
| page: pagination.current, | |||
| }, | |||
| }, | |||
| true, | |||
| ); | |||
| return data?.data ?? []; | |||
| }, | |||
| @@ -159,7 +164,7 @@ export const useUpdateAgentSetting = () => { | |||
| } = useMutation({ | |||
| mutationKey: [AgentApiAction.UpdateAgentSetting], | |||
| mutationFn: async (params: any) => { | |||
| const ret = await flowService.settingCanvas(params); | |||
| const ret = await agentService.settingCanvas(params); | |||
| if (ret?.data?.code === 0) { | |||
| message.success('success'); | |||
| queryClient.invalidateQueries({ | |||
| @@ -184,7 +189,7 @@ export const useDeleteAgent = () => { | |||
| } = useMutation({ | |||
| mutationKey: [AgentApiAction.DeleteAgent], | |||
| mutationFn: async (canvasIds: string[]) => { | |||
| const { data } = await flowService.removeCanvas({ canvasIds }); | |||
| const { data } = await agentService.removeCanvas({ canvasIds }); | |||
| if (data.code === 0) { | |||
| queryClient.invalidateQueries({ | |||
| queryKey: [AgentApiAction.FetchAgentList], | |||
| @@ -217,7 +222,7 @@ export const useFetchAgent = (): { | |||
| refetchOnWindowFocus: false, | |||
| gcTime: 0, | |||
| queryFn: async () => { | |||
| const { data } = await flowService.getCanvas({}, sharedId || id); | |||
| const { data } = await agentService.fetchCanvas(sharedId || id); | |||
| const messageList = buildMessageListWithUuid( | |||
| get(data, 'data.dsl.messages', []), | |||
| @@ -240,7 +245,7 @@ export const useResetAgent = () => { | |||
| } = useMutation({ | |||
| mutationKey: [AgentApiAction.ResetAgent], | |||
| mutationFn: async () => { | |||
| const { data } = await flowService.resetCanvas({ id }); | |||
| const { data } = await agentService.resetCanvas({ id }); | |||
| return data; | |||
| }, | |||
| }); | |||
| @@ -262,7 +267,7 @@ export const useSetAgent = () => { | |||
| dsl?: DSL; | |||
| avatar?: string; | |||
| }) => { | |||
| const { data = {} } = await flowService.setCanvas(params); | |||
| const { data = {} } = await agentService.setCanvas(params); | |||
| if (data.code === 0) { | |||
| message.success( | |||
| i18n.t(`message.${params?.id ? 'modified' : 'created'}`), | |||
| @@ -295,7 +300,7 @@ export const useUploadCanvasFile = () => { | |||
| }); | |||
| } | |||
| const { data } = await flowService.uploadCanvasFile(nextBody); | |||
| const { data } = await agentService.uploadCanvasFile(nextBody); | |||
| if (data?.code === 0) { | |||
| message.success(i18n.t('message.uploaded')); | |||
| } | |||
| @@ -326,7 +331,7 @@ export const useFetchMessageTrace = () => { | |||
| enabled: !!id && !!messageId, | |||
| refetchInterval: 3000, | |||
| queryFn: async () => { | |||
| const { data } = await flowService.trace({ | |||
| const { data } = await agentService.trace({ | |||
| canvas_id: id, | |||
| message_id: messageId, | |||
| }); | |||
| @@ -346,7 +351,7 @@ export const useTestDbConnect = () => { | |||
| } = useMutation({ | |||
| mutationKey: [AgentApiAction.TestDbConnect], | |||
| mutationFn: async (params: any) => { | |||
| const ret = await flowService.testDbConnect(params); | |||
| const ret = await agentService.testDbConnect(params); | |||
| if (ret?.data?.code === 0) { | |||
| message.success(ret?.data?.data); | |||
| } else { | |||
| @@ -368,7 +373,7 @@ export const useDebugSingle = () => { | |||
| } = useMutation({ | |||
| mutationKey: [AgentApiAction.FetchInputForm], | |||
| mutationFn: async (params: IDebugSingleRequestBody) => { | |||
| const ret = await flowService.debugSingle({ id, ...params }); | |||
| const ret = await agentService.debugSingle({ id, ...params }); | |||
| if (ret?.data?.code !== 0) { | |||
| message.error(ret?.data?.message); | |||
| } | |||
| @@ -387,7 +392,7 @@ export const useFetchInputForm = (componentId?: string) => { | |||
| initialData: {}, | |||
| enabled: !!id && !!componentId, | |||
| queryFn: async () => { | |||
| const { data } = await flowService.inputForm({ | |||
| const { data } = await agentService.inputForm({ | |||
| id, | |||
| component_id: componentId, | |||
| }); | |||
| @@ -408,7 +413,7 @@ export const useFetchVersionList = () => { | |||
| initialData: [], | |||
| gcTime: 0, | |||
| queryFn: async () => { | |||
| const { data } = await flowService.getListVersion({}, id); | |||
| const { data } = await agentService.fetchVersionList(id); | |||
| return data?.data ?? []; | |||
| }, | |||
| @@ -431,7 +436,7 @@ export const useFetchVersion = ( | |||
| queryFn: async () => { | |||
| if (!version_id) return undefined; | |||
| const { data } = await flowService.getVersion({}, version_id); | |||
| const { data } = await agentService.fetchVersion(version_id); | |||
| return data?.data ?? undefined; | |||
| }, | |||
| @@ -12,7 +12,6 @@ import { | |||
| } from '@/components/ui/form'; | |||
| import { Input, NumberInput } from '@/components/ui/input'; | |||
| import { RAGFlowSelect } from '@/components/ui/select'; | |||
| import { Textarea } from '@/components/ui/textarea'; | |||
| import { buildOptions } from '@/utils/form'; | |||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||
| import { memo, useMemo } from 'react'; | |||
| @@ -100,23 +99,7 @@ function AgentForm({ node }: INextOperatorForm) { | |||
| }} | |||
| > | |||
| <FormContainer> | |||
| {isSubAgent && ( | |||
| <> | |||
| <DescriptionField></DescriptionField> | |||
| <FormField | |||
| control={form.control} | |||
| name={`user_prompt`} | |||
| render={({ field }) => ( | |||
| <FormItem className="flex-1"> | |||
| <FormLabel>Subagent Input</FormLabel> | |||
| <FormControl> | |||
| <Textarea {...field}></Textarea> | |||
| </FormControl> | |||
| </FormItem> | |||
| )} | |||
| /> | |||
| </> | |||
| )} | |||
| {isSubAgent && <DescriptionField></DescriptionField>} | |||
| <LargeModelFormField></LargeModelFormField> | |||
| <FormField | |||
| control={form.control} | |||
| @@ -0,0 +1,102 @@ | |||
| import api from '@/utils/api'; | |||
| import { registerNextServer } from '@/utils/register-server'; | |||
| const { | |||
| getCanvasSSE, | |||
| setCanvas, | |||
| listCanvas, | |||
| resetCanvas, | |||
| removeCanvas, | |||
| runCanvas, | |||
| listTemplates, | |||
| testDbConnect, | |||
| getInputElements, | |||
| debug, | |||
| listCanvasTeam, | |||
| settingCanvas, | |||
| uploadCanvasFile, | |||
| trace, | |||
| inputForm, | |||
| fetchVersionList, | |||
| fetchVersion, | |||
| fetchCanvas, | |||
| } = api; | |||
| const methods = { | |||
| fetchCanvas: { | |||
| url: fetchCanvas, | |||
| method: 'get', | |||
| }, | |||
| getCanvasSSE: { | |||
| url: getCanvasSSE, | |||
| method: 'get', | |||
| }, | |||
| setCanvas: { | |||
| url: setCanvas, | |||
| method: 'post', | |||
| }, | |||
| fetchVersionList: { | |||
| url: fetchVersionList, | |||
| method: 'get', | |||
| }, | |||
| fetchVersion: { | |||
| url: fetchVersion, | |||
| method: 'get', | |||
| }, | |||
| listCanvas: { | |||
| url: listCanvas, | |||
| method: 'get', | |||
| }, | |||
| resetCanvas: { | |||
| url: resetCanvas, | |||
| method: 'post', | |||
| }, | |||
| removeCanvas: { | |||
| url: removeCanvas, | |||
| method: 'post', | |||
| }, | |||
| runCanvas: { | |||
| url: runCanvas, | |||
| method: 'post', | |||
| }, | |||
| listTemplates: { | |||
| url: listTemplates, | |||
| method: 'get', | |||
| }, | |||
| testDbConnect: { | |||
| url: testDbConnect, | |||
| method: 'post', | |||
| }, | |||
| getInputElements: { | |||
| url: getInputElements, | |||
| method: 'get', | |||
| }, | |||
| debugSingle: { | |||
| url: debug, | |||
| method: 'post', | |||
| }, | |||
| listCanvasTeam: { | |||
| url: listCanvasTeam, | |||
| method: 'get', | |||
| }, | |||
| settingCanvas: { | |||
| url: settingCanvas, | |||
| method: 'post', | |||
| }, | |||
| uploadCanvasFile: { | |||
| url: uploadCanvasFile, | |||
| method: 'post', | |||
| }, | |||
| trace: { | |||
| url: trace, | |||
| method: 'get', | |||
| }, | |||
| inputForm: { | |||
| url: inputForm, | |||
| method: 'get', | |||
| }, | |||
| } as const; | |||
| const agentService = registerNextServer<keyof typeof methods>(methods); | |||
| export default agentService; | |||
| @@ -148,6 +148,9 @@ export default { | |||
| trace: `${api_host}/canvas/trace`, | |||
| // agent | |||
| inputForm: `${api_host}/canvas/input_form`, | |||
| fetchVersionList: (id: string) => `${api_host}/canvas/getlistversion/${id}`, | |||
| fetchVersion: (id: string) => `${api_host}/canvas/getversion/${id}`, | |||
| fetchCanvas: (id: string) => `${api_host}/canvas/get/${id}`, | |||
| // mcp server | |||
| listMcpServer: `${api_host}/mcp_server/list`, | |||
| @@ -0,0 +1,146 @@ | |||
| import message from '@/components/ui/message'; | |||
| import { Authorization } from '@/constants/authorization'; | |||
| import i18n from '@/locales/config'; | |||
| import authorizationUtil, { | |||
| getAuthorization, | |||
| redirectToLogin, | |||
| } from '@/utils/authorization-util'; | |||
| import { notification } from 'antd'; | |||
| import axios from 'axios'; | |||
| import { convertTheKeysOfTheObjectToSnake } from './common-util'; | |||
| const FAILED_TO_FETCH = 'Failed to fetch'; | |||
| export const RetcodeMessage = { | |||
| 200: i18n.t('message.200'), | |||
| 201: i18n.t('message.201'), | |||
| 202: i18n.t('message.202'), | |||
| 204: i18n.t('message.204'), | |||
| 400: i18n.t('message.400'), | |||
| 401: i18n.t('message.401'), | |||
| 403: i18n.t('message.403'), | |||
| 404: i18n.t('message.404'), | |||
| 406: i18n.t('message.406'), | |||
| 410: i18n.t('message.410'), | |||
| 413: i18n.t('message.413'), | |||
| 422: i18n.t('message.422'), | |||
| 500: i18n.t('message.500'), | |||
| 502: i18n.t('message.502'), | |||
| 503: i18n.t('message.503'), | |||
| 504: i18n.t('message.504'), | |||
| }; | |||
| export type ResultCode = | |||
| | 200 | |||
| | 201 | |||
| | 202 | |||
| | 204 | |||
| | 400 | |||
| | 401 | |||
| | 403 | |||
| | 404 | |||
| | 406 | |||
| | 410 | |||
| | 413 | |||
| | 422 | |||
| | 500 | |||
| | 502 | |||
| | 503 | |||
| | 504; | |||
| const errorHandler = (error: { | |||
| response: Response; | |||
| message: string; | |||
| }): Response => { | |||
| const { response } = error; | |||
| if (error.message === FAILED_TO_FETCH) { | |||
| notification.error({ | |||
| description: i18n.t('message.networkAnomalyDescription'), | |||
| message: i18n.t('message.networkAnomaly'), | |||
| }); | |||
| } else { | |||
| if (response && response.status) { | |||
| const errorText = | |||
| RetcodeMessage[response.status as ResultCode] || response.statusText; | |||
| const { status, url } = response; | |||
| notification.error({ | |||
| message: `${i18n.t('message.requestError')} ${status}: ${url}`, | |||
| description: errorText, | |||
| }); | |||
| } | |||
| } | |||
| return response ?? { data: { code: 1999 } }; | |||
| }; | |||
| const request = axios.create({ | |||
| // errorHandler, | |||
| timeout: 300000, | |||
| // getResponse: true, | |||
| }); | |||
| request.interceptors.request.use( | |||
| (config) => { | |||
| const data = convertTheKeysOfTheObjectToSnake(config.data); | |||
| const params = convertTheKeysOfTheObjectToSnake(config.params); | |||
| const newConfig = { ...config, data, params }; | |||
| if (!newConfig.skipToken) { | |||
| newConfig.headers.set(Authorization, getAuthorization()); | |||
| } | |||
| return newConfig; | |||
| }, | |||
| function (error) { | |||
| return Promise.reject(error); | |||
| }, | |||
| ); | |||
| request.interceptors.response.use( | |||
| async (response) => { | |||
| if (response?.status === 413 || response?.status === 504) { | |||
| message.error(RetcodeMessage[response?.status as ResultCode]); | |||
| } | |||
| if (response.config.responseType === 'blob') { | |||
| return response; | |||
| } | |||
| const data = response?.data; | |||
| if (data?.code === 100) { | |||
| message.error(data?.message); | |||
| } else if (data?.code === 401) { | |||
| notification.error({ | |||
| message: data?.message, | |||
| description: data?.message, | |||
| duration: 3, | |||
| }); | |||
| authorizationUtil.removeAll(); | |||
| redirectToLogin(); | |||
| } else if (data?.code !== 0) { | |||
| notification.error({ | |||
| message: `${i18n.t('message.hint')} : ${data?.code}`, | |||
| description: data?.message, | |||
| duration: 3, | |||
| }); | |||
| } | |||
| return response; | |||
| }, | |||
| function (error) { | |||
| console.log('🚀 ~ error:', error); | |||
| errorHandler(error); | |||
| return Promise.reject(error); | |||
| }, | |||
| ); | |||
| export default request; | |||
| export const get = (url: string) => { | |||
| return request.get(url); | |||
| }; | |||
| export const post = (url: string, body: any) => { | |||
| return request.post(url, { data: body }); | |||
| }; | |||
| export const drop = () => {}; | |||
| export const put = () => {}; | |||
| @@ -1,5 +1,8 @@ | |||
| import { AxiosRequestConfig, AxiosResponse } from 'axios'; | |||
| import { isObject } from 'lodash'; | |||
| import omit from 'lodash/omit'; | |||
| import { RequestMethod } from 'umi-request'; | |||
| import request from './next-request'; | |||
| type Service<T extends string> = Record< | |||
| T, | |||
| @@ -39,3 +42,39 @@ const registerServer = <T extends string>( | |||
| }; | |||
| export default registerServer; | |||
| export function registerNextServer<T extends string>( | |||
| requestRecord: Record< | |||
| T, | |||
| { url: string | ((...args: Array<any>) => string); method: string } | |||
| >, | |||
| ) { | |||
| type Server = Record< | |||
| T, | |||
| ( | |||
| config?: | |||
| | AxiosRequestConfig<any> | |||
| | Record<string, any> | |||
| | string | |||
| | number | |||
| | boolean | |||
| | undefined, | |||
| useAxiosNativeConfig?: boolean, | |||
| ) => Promise<AxiosResponse<any, any>> | |||
| >; | |||
| const server: Server = {} as Server; | |||
| for (const name in requestRecord) { | |||
| if (Object.prototype.hasOwnProperty.call(requestRecord, name)) { | |||
| const { url, method } = requestRecord[name]; | |||
| server[name] = (config, useAxiosNativeConfig = false) => { | |||
| const nextConfig = useAxiosNativeConfig ? config : { data: config }; | |||
| const finalConfig = isObject(nextConfig) ? nextConfig : {}; | |||
| const nextUrl = typeof url === 'function' ? url(config) : url; | |||
| return request({ url: nextUrl, method, ...finalConfig }); | |||
| }; | |||
| } | |||
| } | |||
| return server; | |||
| } | |||