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)
This commit is contained in:
balibabu
2025-09-28 18:43:20 +08:00
committed by GitHub
parent 7a63b6386e
commit cce361d774
15 changed files with 158 additions and 73 deletions

View File

@ -1,3 +1,4 @@
import { AgentCategory } from '@/constants/agent';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { useFetchAgentList } from '@/hooks/use-agent-request'; import { useFetchAgentList } from '@/hooks/use-agent-request';
import { buildSelectOptions } from '@/utils/component-util'; import { buildSelectOptions } from '@/utils/component-util';
@ -33,8 +34,8 @@ export function DataFlowSelect(props: IProps) {
const toDataPipLine = () => { const toDataPipLine = () => {
toDataPipeline?.(); toDataPipeline?.();
}; };
const { data: dataPipelineOptions, loading } = useFetchAgentList({ const { data: dataPipelineOptions } = useFetchAgentList({
canvas_category: 'dataflow_canvas', canvas_category: AgentCategory.DataflowCanvas,
}); });
const options = useMemo(() => { const options = useMemo(() => {
const option = buildSelectOptions( const option = buildSelectOptions(

View File

@ -47,3 +47,8 @@ export const initialLlmBaseValues = {
presence_penalty: 0.4, presence_penalty: 0.4,
max_tokens: 256, max_tokens: 256,
}; };
export enum AgentCategory {
AgentCanvas = 'agent_canvas',
DataflowCanvas = 'dataflow_canvas',
}

View File

@ -1,4 +1,5 @@
import { FileUploadProps } from '@/components/file-upload'; import { FileUploadProps } from '@/components/file-upload';
import { useHandleFilterSubmit } from '@/components/list-filter-bar/use-handle-filter-submit';
import message from '@/components/ui/message'; import message from '@/components/ui/message';
import { AgentGlobals } from '@/constants/agent'; import { AgentGlobals } from '@/constants/agent';
import { import {
@ -33,6 +34,7 @@ import {
} from './logic-hooks'; } from './logic-hooks';
export const enum AgentApiAction { export const enum AgentApiAction {
FetchAgentListByPage = 'fetchAgentListByPage',
FetchAgentList = 'fetchAgentList', FetchAgentList = 'fetchAgentList',
UpdateAgentSetting = 'updateAgentSetting', UpdateAgentSetting = 'updateAgentSetting',
DeleteAgent = 'deleteAgent', DeleteAgent = 'deleteAgent',
@ -114,16 +116,36 @@ export const useFetchAgentListByPage = () => {
const { searchString, handleInputChange } = useHandleSearchChange(); const { searchString, handleInputChange } = useHandleSearchChange();
const { pagination, setPagination } = useGetPaginationWithRouter(); const { pagination, setPagination } = useGetPaginationWithRouter();
const debouncedSearchString = useDebounce(searchString, { wait: 500 }); 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<{ const { data, isFetching: loading } = useQuery<{
canvas: IFlow[]; canvas: IFlow[];
total: number; total: number;
}>({ }>({
queryKey: [ queryKey: [
AgentApiAction.FetchAgentList, AgentApiAction.FetchAgentListByPage,
{ {
debouncedSearchString, debouncedSearchString,
...pagination, ...pagination,
filterValue,
}, },
], ],
placeholderData: (previousData) => { placeholderData: (previousData) => {
@ -134,13 +156,9 @@ export const useFetchAgentListByPage = () => {
}, },
gcTime: 0, gcTime: 0,
queryFn: async () => { queryFn: async () => {
const { data } = await agentService.listCanvasTeam( const { data } = await agentService.listCanvas(
{ {
params: { params: requestParams,
keywords: debouncedSearchString,
page_size: pagination.pageSize,
page: pagination.current,
},
}, },
true, true,
); );
@ -164,6 +182,8 @@ export const useFetchAgentListByPage = () => {
handleInputChange: onInputChange, handleInputChange: onInputChange,
pagination: { ...pagination, total: data?.total }, pagination: { ...pagination, total: data?.total },
setPagination, setPagination,
filterValue,
handleFilterSubmit,
}; };
}; };
@ -181,7 +201,7 @@ export const useUpdateAgentSetting = () => {
if (ret?.data?.code === 0) { if (ret?.data?.code === 0) {
message.success('success'); message.success('success');
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: [AgentApiAction.FetchAgentList], queryKey: [AgentApiAction.FetchAgentListByPage],
}); });
} else { } else {
message.error(ret?.data?.data); message.error(ret?.data?.data);
@ -205,7 +225,7 @@ export const useDeleteAgent = () => {
const { data } = await agentService.removeCanvas({ canvasIds }); const { data } = await agentService.removeCanvas({ canvasIds });
if (data.code === 0) { if (data.code === 0) {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: [AgentApiAction.FetchAgentList], queryKey: [AgentApiAction.FetchAgentListByPage],
}); });
} }
return data?.data ?? []; return data?.data ?? [];
@ -289,7 +309,7 @@ export const useSetAgent = (showMessage: boolean = true) => {
); );
} }
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: [AgentApiAction.FetchAgentList], queryKey: [AgentApiAction.FetchAgentListByPage],
}); });
} }
return data; return data;
@ -657,17 +677,14 @@ export const useFetchPrompt = () => {
}; };
export const useFetchAgentList = ({ export const useFetchAgentList = ({
canvas_category = 'agent_canvas', canvas_category,
}: IPipeLineListRequest): { }: IPipeLineListRequest) => {
data: { const { data, isFetching: loading } = useQuery<{
canvas: IFlow[]; canvas: IFlow[];
total: number; total: number;
}; }>({
loading: boolean; queryKey: [AgentApiAction.FetchAgentList],
} => { initialData: { canvas: [], total: 0 },
const { data, isFetching: loading } = useQuery({
queryKey: ['fetchPipeLineList'],
initialData: [],
gcTime: 0, gcTime: 0,
queryFn: async () => { queryFn: async () => {
const { data } = await fetchPipeLineList({ canvas_category }); const { data } = await fetchPipeLineList({ canvas_category });
@ -699,3 +716,18 @@ export const useCancelDataflow = () => {
return { data, loading, cancelDataflow: mutateAsync }; return { data, loading, cancelDataflow: mutateAsync };
}; };
// export const useFetchKnowledgeList = () => {
// const { data, isFetching: loading } = useQuery<IFlow[]>({
// 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 };
// };

View File

@ -30,6 +30,7 @@ export const enum KnowledgeApiAction {
FetchKnowledgeDetail = 'fetchKnowledgeDetail', FetchKnowledgeDetail = 'fetchKnowledgeDetail',
FetchKnowledgeGraph = 'fetchKnowledgeGraph', FetchKnowledgeGraph = 'fetchKnowledgeGraph',
FetchMetadata = 'fetchMetadata', FetchMetadata = 'fetchMetadata',
FetchKnowledgeList = 'fetchKnowledgeList',
} }
export const useKnowledgeBaseId = (): string => { export const useKnowledgeBaseId = (): string => {
@ -304,3 +305,25 @@ export function useFetchKnowledgeMetadata(kbIds: string[] = []) {
return { data, loading }; 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 };
};

View File

@ -30,6 +30,7 @@ export interface ISwitchForm {
no: string; no: string;
} }
import { AgentCategory } from '@/constants/agent';
import { Edge, Node } from '@xyflow/react'; import { Edge, Node } from '@xyflow/react';
import { IReference, Message } from './chat'; import { IReference, Message } from './chat';
@ -273,5 +274,5 @@ export interface IPipeLineListRequest {
keywords?: string; keywords?: string;
orderby?: string; orderby?: string;
desc?: boolean; desc?: boolean;
canvas_category?: 'agent_canvas' | 'dataflow_canvas'; canvas_category?: AgentCategory;
} }

View File

@ -924,8 +924,3 @@ export enum AgentExceptionMethod {
Comment = 'comment', Comment = 'comment',
Goto = 'goto', Goto = 'goto',
} }
export enum AgentCategory {
AgentCanvas = 'agent_canvas',
DataflowCanvas = 'dataflow_canvas',
}

View File

@ -2,10 +2,10 @@ import { HomeCard } from '@/components/home-card';
import { MoreButton } from '@/components/more-button'; import { MoreButton } from '@/components/more-button';
import { SharedBadge } from '@/components/shared-badge'; import { SharedBadge } from '@/components/shared-badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { AgentCategory } from '@/constants/agent';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { IFlow } from '@/interfaces/database/agent'; import { IFlow } from '@/interfaces/database/agent';
import { DatabaseZap } from 'lucide-react'; import { Route } from 'lucide-react';
import { AgentCategory } from '../agent/constant';
import { AgentDropdown } from './agent-dropdown'; import { AgentDropdown } from './agent-dropdown';
import { useRenameAgent } from './use-rename-agent'; import { useRenameAgent } from './use-rename-agent';
@ -33,7 +33,7 @@ export function AgentCard({ data, showAgentRenameModal }: DatasetCardProps) {
icon={ icon={
data.canvas_category === AgentCategory.DataflowCanvas && ( data.canvas_category === AgentCategory.DataflowCanvas && (
<Button variant={'ghost'} size={'sm'}> <Button variant={'ghost'} size={'sm'}>
<DatabaseZap /> <Route />
</Button> </Button>
) )
} }

View File

@ -1,7 +1,7 @@
import { AgentCategory } from '@/constants/agent';
import { useSetModalState } from '@/hooks/common-hooks'; import { useSetModalState } from '@/hooks/common-hooks';
import { EmptyDsl, useSetAgent } from '@/hooks/use-agent-request'; import { EmptyDsl, useSetAgent } from '@/hooks/use-agent-request';
import { DSL } from '@/interfaces/database/agent'; import { DSL } from '@/interfaces/database/agent';
import { AgentCategory } from '@/pages/agent/constant';
import { import {
BeginId, BeginId,
Operator, Operator,

View File

@ -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;
}

View File

@ -17,13 +17,21 @@ import { useCallback } from 'react';
import { AgentCard } from './agent-card'; import { AgentCard } from './agent-card';
import { CreateAgentDialog } from './create-agent-dialog'; import { CreateAgentDialog } from './create-agent-dialog';
import { useCreateAgentOrPipeline } from './hooks/use-create-agent'; import { useCreateAgentOrPipeline } from './hooks/use-create-agent';
import { useSelectFilters } from './hooks/use-selelct-filters';
import { UploadAgentDialog } from './upload-agent-dialog'; import { UploadAgentDialog } from './upload-agent-dialog';
import { useHandleImportJsonFile } from './use-import-json'; import { useHandleImportJsonFile } from './use-import-json';
import { useRenameAgent } from './use-rename-agent'; import { useRenameAgent } from './use-rename-agent';
export default function Agents() { export default function Agents() {
const { data, pagination, setPagination, searchString, handleInputChange } = const {
useFetchAgentListByPage(); data,
pagination,
setPagination,
searchString,
handleInputChange,
filterValue,
handleFilterSubmit,
} = useFetchAgentListByPage();
const { navigateToAgentTemplates } = useNavigatePage(); const { navigateToAgentTemplates } = useNavigatePage();
const { const {
@ -50,6 +58,8 @@ export default function Agents() {
hideFileUploadModal, hideFileUploadModal,
} = useHandleImportJsonFile(); } = useHandleImportJsonFile();
const filters = useSelectFilters();
const handlePageChange = useCallback( const handlePageChange = useCallback(
(page: number, pageSize?: number) => { (page: number, pageSize?: number) => {
setPagination({ page, pageSize }); setPagination({ page, pageSize });
@ -65,6 +75,9 @@ export default function Agents() {
searchString={searchString} searchString={searchString}
onSearchChange={handleInputChange} onSearchChange={handleInputChange}
icon="agent" icon="agent"
filters={filters}
onChange={handleFilterSubmit}
value={filterValue}
> >
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger> <DropdownMenuTrigger>

View File

@ -1,18 +1,11 @@
import { FilterCollection } from '@/components/list-filter-bar/interface'; import { FilterCollection } from '@/components/list-filter-bar/interface';
import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; import { useFetchKnowledgeList } from '@/hooks/use-knowledge-request';
import { groupListByType } from '@/utils/dataset-util'; import { buildOwnersFilter } from '@/utils/list-filter-util';
import { useMemo } from 'react';
export function useSelectOwners() { export function useSelectOwners() {
const { list } = useFetchKnowledgeList(); const { list } = useFetchKnowledgeList();
const owners = useMemo(() => { const filters: FilterCollection[] = [buildOwnersFilter(list)];
return groupListByType(list, 'tenant_id', 'nickname');
}, [list]);
const filters: FilterCollection[] = [
{ field: 'owner', list: owners, label: 'Owner' },
];
return filters; return filters;
} }

View File

@ -17,7 +17,6 @@ const {
testDbConnect, testDbConnect,
getInputElements, getInputElements,
debug, debug,
listCanvasTeam,
settingCanvas, settingCanvas,
uploadCanvasFile, uploadCanvasFile,
trace, trace,
@ -85,10 +84,6 @@ const methods = {
url: debug, url: debug,
method: 'post', method: 'post',
}, },
listCanvasTeam: {
url: listCanvasTeam,
method: 'get',
},
settingCanvas: { settingCanvas: {
url: settingCanvas, url: settingCanvas,
method: 'post', method: 'post',

View File

@ -147,7 +147,6 @@ export default {
// flow // flow
listTemplates: `${api_host}/canvas/templates`, listTemplates: `${api_host}/canvas/templates`,
listCanvas: `${api_host}/canvas/list`, listCanvas: `${api_host}/canvas/list`,
listCanvasTeam: `${api_host}/canvas/list`,
getCanvas: `${api_host}/canvas/get`, getCanvas: `${api_host}/canvas/get`,
getCanvasSSE: `${api_host}/canvas/getsse`, getCanvasSSE: `${api_host}/canvas/getsse`,
removeCanvas: `${api_host}/canvas/rm`, removeCanvas: `${api_host}/canvas/rm`,

View File

@ -7,27 +7,3 @@ export function isKnowledgeGraphParser(parserId: DocumentParserType) {
export function isNaiveParser(parserId: DocumentParserType) { export function isNaiveParser(parserId: DocumentParserType) {
return parserId === DocumentParserType.Naive; return parserId === DocumentParserType.Naive;
} }
export type FilterType = {
id: string;
label: string;
count: number;
};
export function groupListByType<T extends Record<string, any>>(
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;
}

View File

@ -0,0 +1,29 @@
export type FilterType = {
id: string;
label: string;
count: number;
};
export function groupListByType<T extends Record<string, any>>(
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<T extends Record<string, any>>(list: T[]) {
const owners = groupListByType(list, 'tenant_id', 'nickname');
return { field: 'owner', list: owners, label: 'Owner' };
}