From cce361d774f4f4f2f18fa4bbcaefa078aa1c3687 Mon Sep 17 00:00:00 2001 From: balibabu Date: Sun, 28 Sep 2025 18:43:20 +0800 Subject: [PATCH] Feat: Filter the agent list by owner and category #9869 (#10344) ### What problem does this PR solve? Feat: Filter the agent list by owner and category #9869 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- .../components/data-pipeline-select/index.tsx | 5 +- web/src/constants/agent.ts | 5 ++ web/src/hooks/use-agent-request.ts | 70 ++++++++++++++----- web/src/hooks/use-knowledge-request.ts | 23 ++++++ web/src/interfaces/database/agent.ts | 3 +- web/src/pages/agent/constant.tsx | 5 -- web/src/pages/agents/agent-card.tsx | 6 +- .../pages/agents/hooks/use-create-agent.ts | 2 +- .../pages/agents/hooks/use-selelct-filters.ts | 23 ++++++ web/src/pages/agents/index.tsx | 17 ++++- web/src/pages/datasets/use-select-owners.ts | 13 +--- web/src/services/agent-service.ts | 5 -- web/src/utils/api.ts | 1 - web/src/utils/dataset-util.ts | 24 ------- web/src/utils/list-filter-util.ts | 29 ++++++++ 15 files changed, 158 insertions(+), 73 deletions(-) create mode 100644 web/src/pages/agents/hooks/use-selelct-filters.ts create mode 100644 web/src/utils/list-filter-util.ts diff --git a/web/src/components/data-pipeline-select/index.tsx b/web/src/components/data-pipeline-select/index.tsx index 4b71aba91..80c74705f 100644 --- a/web/src/components/data-pipeline-select/index.tsx +++ b/web/src/components/data-pipeline-select/index.tsx @@ -1,3 +1,4 @@ +import { AgentCategory } from '@/constants/agent'; import { useTranslate } from '@/hooks/common-hooks'; import { useFetchAgentList } from '@/hooks/use-agent-request'; import { buildSelectOptions } from '@/utils/component-util'; @@ -33,8 +34,8 @@ export function DataFlowSelect(props: IProps) { const toDataPipLine = () => { toDataPipeline?.(); }; - const { data: dataPipelineOptions, loading } = useFetchAgentList({ - canvas_category: 'dataflow_canvas', + const { data: dataPipelineOptions } = useFetchAgentList({ + canvas_category: AgentCategory.DataflowCanvas, }); const options = useMemo(() => { const option = buildSelectOptions( diff --git a/web/src/constants/agent.ts b/web/src/constants/agent.ts index 6cb0cc92a..7e9340885 100644 --- a/web/src/constants/agent.ts +++ b/web/src/constants/agent.ts @@ -47,3 +47,8 @@ export const initialLlmBaseValues = { presence_penalty: 0.4, max_tokens: 256, }; + +export enum AgentCategory { + AgentCanvas = 'agent_canvas', + DataflowCanvas = 'dataflow_canvas', +} diff --git a/web/src/hooks/use-agent-request.ts b/web/src/hooks/use-agent-request.ts index 65c6fc4bf..51b75e9ee 100644 --- a/web/src/hooks/use-agent-request.ts +++ b/web/src/hooks/use-agent-request.ts @@ -1,4 +1,5 @@ import { FileUploadProps } from '@/components/file-upload'; +import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-filter-submit'; import message from '@/components/ui/message'; import { AgentGlobals } from '@/constants/agent'; import { @@ -33,6 +34,7 @@ import { } from './logic-hooks'; export const enum AgentApiAction { + FetchAgentListByPage = 'fetchAgentListByPage', FetchAgentList = 'fetchAgentList', UpdateAgentSetting = 'updateAgentSetting', DeleteAgent = 'deleteAgent', @@ -114,16 +116,36 @@ export const useFetchAgentListByPage = () => { const { searchString, handleInputChange } = useHandleSearchChange(); const { pagination, setPagination } = useGetPaginationWithRouter(); const debouncedSearchString = useDebounce(searchString, { wait: 500 }); + const { filterValue, handleFilterSubmit } = useHandleFilterSubmit(); + const canvasCategory = Array.isArray(filterValue.canvasCategory) + ? filterValue.canvasCategory + : []; + const owner = filterValue.owner; + + const requestParams = { + keywords: debouncedSearchString, + page_size: pagination.pageSize, + page: pagination.current, + canvas_category: + canvasCategory.length === 1 ? canvasCategory[0] : undefined, + owner_ids: '', + }; + + if (Array.isArray(owner) && owner.length > 0) { + requestParams.owner_ids = + `${owner[0]}` + owner.slice(1).map((id) => `&owner_ids=${id}`); + } const { data, isFetching: loading } = useQuery<{ canvas: IFlow[]; total: number; }>({ queryKey: [ - AgentApiAction.FetchAgentList, + AgentApiAction.FetchAgentListByPage, { debouncedSearchString, ...pagination, + filterValue, }, ], placeholderData: (previousData) => { @@ -134,13 +156,9 @@ export const useFetchAgentListByPage = () => { }, gcTime: 0, queryFn: async () => { - const { data } = await agentService.listCanvasTeam( + const { data } = await agentService.listCanvas( { - params: { - keywords: debouncedSearchString, - page_size: pagination.pageSize, - page: pagination.current, - }, + params: requestParams, }, true, ); @@ -164,6 +182,8 @@ export const useFetchAgentListByPage = () => { handleInputChange: onInputChange, pagination: { ...pagination, total: data?.total }, setPagination, + filterValue, + handleFilterSubmit, }; }; @@ -181,7 +201,7 @@ export const useUpdateAgentSetting = () => { if (ret?.data?.code === 0) { message.success('success'); queryClient.invalidateQueries({ - queryKey: [AgentApiAction.FetchAgentList], + queryKey: [AgentApiAction.FetchAgentListByPage], }); } else { message.error(ret?.data?.data); @@ -205,7 +225,7 @@ export const useDeleteAgent = () => { const { data } = await agentService.removeCanvas({ canvasIds }); if (data.code === 0) { queryClient.invalidateQueries({ - queryKey: [AgentApiAction.FetchAgentList], + queryKey: [AgentApiAction.FetchAgentListByPage], }); } return data?.data ?? []; @@ -289,7 +309,7 @@ export const useSetAgent = (showMessage: boolean = true) => { ); } queryClient.invalidateQueries({ - queryKey: [AgentApiAction.FetchAgentList], + queryKey: [AgentApiAction.FetchAgentListByPage], }); } return data; @@ -657,17 +677,14 @@ export const useFetchPrompt = () => { }; export const useFetchAgentList = ({ - canvas_category = 'agent_canvas', -}: IPipeLineListRequest): { - data: { + canvas_category, +}: IPipeLineListRequest) => { + const { data, isFetching: loading } = useQuery<{ canvas: IFlow[]; total: number; - }; - loading: boolean; -} => { - const { data, isFetching: loading } = useQuery({ - queryKey: ['fetchPipeLineList'], - initialData: [], + }>({ + queryKey: [AgentApiAction.FetchAgentList], + initialData: { canvas: [], total: 0 }, gcTime: 0, queryFn: async () => { const { data } = await fetchPipeLineList({ canvas_category }); @@ -699,3 +716,18 @@ export const useCancelDataflow = () => { return { data, loading, cancelDataflow: mutateAsync }; }; + +// export const useFetchKnowledgeList = () => { +// const { data, isFetching: loading } = useQuery({ +// queryKey: [AgentApiAction.FetchAgentList], +// initialData: [], +// gcTime: 0, // https://tanstack.com/query/latest/docs/framework/react/guides/caching?from=reactQueryV3 +// queryFn: async () => { +// const { data } = await agentService.listCanvas(); + +// return data?.data ?? []; +// }, +// }); + +// return { list: data, loading }; +// }; diff --git a/web/src/hooks/use-knowledge-request.ts b/web/src/hooks/use-knowledge-request.ts index e8b7cbe87..d16aa40a9 100644 --- a/web/src/hooks/use-knowledge-request.ts +++ b/web/src/hooks/use-knowledge-request.ts @@ -30,6 +30,7 @@ export const enum KnowledgeApiAction { FetchKnowledgeDetail = 'fetchKnowledgeDetail', FetchKnowledgeGraph = 'fetchKnowledgeGraph', FetchMetadata = 'fetchMetadata', + FetchKnowledgeList = 'fetchKnowledgeList', } export const useKnowledgeBaseId = (): string => { @@ -304,3 +305,25 @@ export function useFetchKnowledgeMetadata(kbIds: string[] = []) { return { data, loading }; } + +export const useFetchKnowledgeList = ( + shouldFilterListWithoutDocument: boolean = false, +): { + list: IKnowledge[]; + loading: boolean; +} => { + const { data, isFetching: loading } = useQuery({ + queryKey: [KnowledgeApiAction.FetchKnowledgeList], + initialData: [], + gcTime: 0, // https://tanstack.com/query/latest/docs/framework/react/guides/caching?from=reactQueryV3 + queryFn: async () => { + const { data } = await listDataset(); + const list = data?.data?.kbs ?? []; + return shouldFilterListWithoutDocument + ? list.filter((x: IKnowledge) => x.chunk_num > 0) + : list; + }, + }); + + return { list: data, loading }; +}; diff --git a/web/src/interfaces/database/agent.ts b/web/src/interfaces/database/agent.ts index 32014ea6a..8bd33e89b 100644 --- a/web/src/interfaces/database/agent.ts +++ b/web/src/interfaces/database/agent.ts @@ -30,6 +30,7 @@ export interface ISwitchForm { no: string; } +import { AgentCategory } from '@/constants/agent'; import { Edge, Node } from '@xyflow/react'; import { IReference, Message } from './chat'; @@ -273,5 +274,5 @@ export interface IPipeLineListRequest { keywords?: string; orderby?: string; desc?: boolean; - canvas_category?: 'agent_canvas' | 'dataflow_canvas'; + canvas_category?: AgentCategory; } diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index a0ca13b00..3e0a18b44 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -924,8 +924,3 @@ export enum AgentExceptionMethod { Comment = 'comment', Goto = 'goto', } - -export enum AgentCategory { - AgentCanvas = 'agent_canvas', - DataflowCanvas = 'dataflow_canvas', -} diff --git a/web/src/pages/agents/agent-card.tsx b/web/src/pages/agents/agent-card.tsx index be20cc57b..2ae1ab0c8 100644 --- a/web/src/pages/agents/agent-card.tsx +++ b/web/src/pages/agents/agent-card.tsx @@ -2,10 +2,10 @@ import { HomeCard } from '@/components/home-card'; import { MoreButton } from '@/components/more-button'; import { SharedBadge } from '@/components/shared-badge'; import { Button } from '@/components/ui/button'; +import { AgentCategory } from '@/constants/agent'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { IFlow } from '@/interfaces/database/agent'; -import { DatabaseZap } from 'lucide-react'; -import { AgentCategory } from '../agent/constant'; +import { Route } from 'lucide-react'; import { AgentDropdown } from './agent-dropdown'; import { useRenameAgent } from './use-rename-agent'; @@ -33,7 +33,7 @@ export function AgentCard({ data, showAgentRenameModal }: DatasetCardProps) { icon={ data.canvas_category === AgentCategory.DataflowCanvas && ( ) } diff --git a/web/src/pages/agents/hooks/use-create-agent.ts b/web/src/pages/agents/hooks/use-create-agent.ts index 7ad835858..da1915290 100644 --- a/web/src/pages/agents/hooks/use-create-agent.ts +++ b/web/src/pages/agents/hooks/use-create-agent.ts @@ -1,7 +1,7 @@ +import { AgentCategory } from '@/constants/agent'; import { useSetModalState } from '@/hooks/common-hooks'; import { EmptyDsl, useSetAgent } from '@/hooks/use-agent-request'; import { DSL } from '@/interfaces/database/agent'; -import { AgentCategory } from '@/pages/agent/constant'; import { BeginId, Operator, diff --git a/web/src/pages/agents/hooks/use-selelct-filters.ts b/web/src/pages/agents/hooks/use-selelct-filters.ts new file mode 100644 index 000000000..e1ea755ee --- /dev/null +++ b/web/src/pages/agents/hooks/use-selelct-filters.ts @@ -0,0 +1,23 @@ +import { FilterCollection } from '@/components/list-filter-bar/interface'; +import { useFetchAgentList } from '@/hooks/use-agent-request'; +import { buildOwnersFilter, groupListByType } from '@/utils/list-filter-util'; +import { useMemo } from 'react'; + +export function useSelectFilters() { + const { data } = useFetchAgentList({}); + + const canvasCategory = useMemo(() => { + return groupListByType(data.canvas, 'canvas_category', 'canvas_category'); + }, [data.canvas]); + + const filters: FilterCollection[] = [ + buildOwnersFilter(data.canvas), + { + field: 'canvasCategory', + list: canvasCategory, + label: 'Canvas category', + }, + ]; + + return filters; +} diff --git a/web/src/pages/agents/index.tsx b/web/src/pages/agents/index.tsx index 7ecc1ae39..ab2db3e3b 100644 --- a/web/src/pages/agents/index.tsx +++ b/web/src/pages/agents/index.tsx @@ -17,13 +17,21 @@ import { useCallback } from 'react'; import { AgentCard } from './agent-card'; import { CreateAgentDialog } from './create-agent-dialog'; import { useCreateAgentOrPipeline } from './hooks/use-create-agent'; +import { useSelectFilters } from './hooks/use-selelct-filters'; import { UploadAgentDialog } from './upload-agent-dialog'; import { useHandleImportJsonFile } from './use-import-json'; import { useRenameAgent } from './use-rename-agent'; export default function Agents() { - const { data, pagination, setPagination, searchString, handleInputChange } = - useFetchAgentListByPage(); + const { + data, + pagination, + setPagination, + searchString, + handleInputChange, + filterValue, + handleFilterSubmit, + } = useFetchAgentListByPage(); const { navigateToAgentTemplates } = useNavigatePage(); const { @@ -50,6 +58,8 @@ export default function Agents() { hideFileUploadModal, } = useHandleImportJsonFile(); + const filters = useSelectFilters(); + const handlePageChange = useCallback( (page: number, pageSize?: number) => { setPagination({ page, pageSize }); @@ -65,6 +75,9 @@ export default function Agents() { searchString={searchString} onSearchChange={handleInputChange} icon="agent" + filters={filters} + onChange={handleFilterSubmit} + value={filterValue} > diff --git a/web/src/pages/datasets/use-select-owners.ts b/web/src/pages/datasets/use-select-owners.ts index 50913a023..1de279508 100644 --- a/web/src/pages/datasets/use-select-owners.ts +++ b/web/src/pages/datasets/use-select-owners.ts @@ -1,18 +1,11 @@ import { FilterCollection } from '@/components/list-filter-bar/interface'; -import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; -import { groupListByType } from '@/utils/dataset-util'; -import { useMemo } from 'react'; +import { useFetchKnowledgeList } from '@/hooks/use-knowledge-request'; +import { buildOwnersFilter } from '@/utils/list-filter-util'; export function useSelectOwners() { const { list } = useFetchKnowledgeList(); - const owners = useMemo(() => { - return groupListByType(list, 'tenant_id', 'nickname'); - }, [list]); - - const filters: FilterCollection[] = [ - { field: 'owner', list: owners, label: 'Owner' }, - ]; + const filters: FilterCollection[] = [buildOwnersFilter(list)]; return filters; } diff --git a/web/src/services/agent-service.ts b/web/src/services/agent-service.ts index b2c60b434..9fe7de1fc 100644 --- a/web/src/services/agent-service.ts +++ b/web/src/services/agent-service.ts @@ -17,7 +17,6 @@ const { testDbConnect, getInputElements, debug, - listCanvasTeam, settingCanvas, uploadCanvasFile, trace, @@ -85,10 +84,6 @@ const methods = { url: debug, method: 'post', }, - listCanvasTeam: { - url: listCanvasTeam, - method: 'get', - }, settingCanvas: { url: settingCanvas, method: 'post', diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index 0086fe3fb..c6e04ca1a 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -147,7 +147,6 @@ export default { // flow listTemplates: `${api_host}/canvas/templates`, listCanvas: `${api_host}/canvas/list`, - listCanvasTeam: `${api_host}/canvas/list`, getCanvas: `${api_host}/canvas/get`, getCanvasSSE: `${api_host}/canvas/getsse`, removeCanvas: `${api_host}/canvas/rm`, diff --git a/web/src/utils/dataset-util.ts b/web/src/utils/dataset-util.ts index 6a5d95104..b8e8df7d7 100644 --- a/web/src/utils/dataset-util.ts +++ b/web/src/utils/dataset-util.ts @@ -7,27 +7,3 @@ export function isKnowledgeGraphParser(parserId: DocumentParserType) { export function isNaiveParser(parserId: DocumentParserType) { return parserId === DocumentParserType.Naive; } - -export type FilterType = { - id: string; - label: string; - count: number; -}; - -export function groupListByType>( - list: T[], - idField: string, - labelField: string, -) { - const fileTypeList: FilterType[] = []; - list.forEach((x) => { - const item = fileTypeList.find((y) => y.id === x[idField]); - if (!item) { - fileTypeList.push({ id: x[idField], label: x[labelField], count: 1 }); - } else { - item.count += 1; - } - }); - - return fileTypeList; -} diff --git a/web/src/utils/list-filter-util.ts b/web/src/utils/list-filter-util.ts new file mode 100644 index 000000000..727f55e9b --- /dev/null +++ b/web/src/utils/list-filter-util.ts @@ -0,0 +1,29 @@ +export type FilterType = { + id: string; + label: string; + count: number; +}; + +export function groupListByType>( + list: T[], + idField: string, + labelField: string, +) { + const fileTypeList: FilterType[] = []; + list.forEach((x) => { + const item = fileTypeList.find((y) => y.id === x[idField]); + if (!item) { + fileTypeList.push({ id: x[idField], label: x[labelField], count: 1 }); + } else { + item.count += 1; + } + }); + + return fileTypeList; +} + +export function buildOwnersFilter>(list: T[]) { + const owners = groupListByType(list, 'tenant_id', 'nickname'); + + return { field: 'owner', list: owners, label: 'Owner' }; +}