Browse Source

Feat: Display agent history versions #3221 (#8942)

### What problem does this PR solve?

Feat: Display agent history versions #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
tags/v0.20.0
balibabu 3 months ago
parent
commit
7eb5ea3814
No account linked to committer's email address

+ 43
- 0
web/src/hooks/use-agent-request.ts View File

@@ -33,6 +33,8 @@ export const enum AgentApiAction {
TestDbConnect = 'testDbConnect',
DebugSingle = 'debugSingle',
FetchInputForm = 'fetchInputForm',
FetchVersionList = 'fetchVersionList',
FetchVersion = 'fetchVersion',
}

export const EmptyDsl = {
@@ -396,3 +398,44 @@ export const useFetchInputForm = (componentId?: string) => {

return data;
};

export const useFetchVersionList = () => {
const { id } = useParams();
const { data, isFetching: loading } = useQuery<
Array<{ created_at: string; title: string; id: string }>
>({
queryKey: [AgentApiAction.FetchVersionList],
initialData: [],
gcTime: 0,
queryFn: async () => {
const { data } = await flowService.getListVersion({}, id);

return data?.data ?? [];
},
});

return { data, loading };
};

export const useFetchVersion = (
version_id?: string,
): {
data?: IFlow;
loading: boolean;
} => {
const { data, isFetching: loading } = useQuery({
queryKey: [AgentApiAction.FetchVersion, version_id],
initialData: undefined,
gcTime: 0,
enabled: !!version_id, // Only call API when both values are provided
queryFn: async () => {
if (!version_id) return undefined;

const { data } = await flowService.getVersion({}, version_id);

return data?.data ?? undefined;
},
});

return { data, loading };
};

+ 1
- 1
web/src/pages/agent/canvas/index.tsx View File

@@ -56,7 +56,7 @@ import { SwitchNode } from './node/switch-node';
import { TemplateNode } from './node/template-node';
import { ToolNode } from './node/tool-node';

const nodeTypes: NodeTypes = {
export const nodeTypes: NodeTypes = {
ragNode: RagNode,
categorizeNode: CategorizeNode,
beginNode: BeginNode,

+ 1
- 1
web/src/pages/agent/canvas/node/switch-node.tsx View File

@@ -64,7 +64,7 @@ const ConditionBlock = ({
function InnerSwitchNode({ id, data, selected }: NodeProps<ISwitchNode>) {
const { positions } = useBuildSwitchHandlePositions({ data, id });
return (
<ToolBar selected={selected} id={id} label={data.label}>
<ToolBar selected={selected} id={id} label={data.label} showRun={false}>
<NodeWrapper selected={selected}>
<CommonHandle
type="target"

+ 10
- 1
web/src/pages/agent/index.tsx View File

@@ -38,6 +38,7 @@ import {
import { useShowEmbedModal } from './hooks/use-show-dialog';
import { BeginQuery } from './interface';
import { UploadAgentDialog } from './upload-agent-dialog';
import { VersionDialog } from './version-dialog';

function AgentDropdownMenuItem({
children,
@@ -79,6 +80,11 @@ export default function Agent() {
handleRun();
}
}, [getBeginNodeDataQuery, handleRun, showChatDrawer]);
const {
visible: versionDialogVisible,
hideModal: hideVersionDialog,
showModal: showVersionDialog,
} = useSetModalState();

const { showEmbedModal, hideEmbedModal, embedVisible, beta } =
useShowEmbedModal();
@@ -98,7 +104,7 @@ export default function Agent() {
<CirclePlay />
Run app
</Button>
<Button variant={'secondary'}>
<Button variant={'secondary'} onClick={showVersionDialog}>
<History />
History version
</Button>
@@ -159,6 +165,9 @@ export default function Agent() {
isAgent
></EmbedDialog>
)}
{versionDialogVisible && (
<VersionDialog hideModal={hideVersionDialog}></VersionDialog>
)}
</section>
);
}

+ 131
- 0
web/src/pages/agent/version-dialog/index.tsx View File

@@ -0,0 +1,131 @@
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Spin } from '@/components/ui/spin';
import {
useFetchVersion,
useFetchVersionList,
} from '@/hooks/use-agent-request';
import { IModalProps } from '@/interfaces/common';
import { cn } from '@/lib/utils';
import { formatDate } from '@/utils/date';
import { downloadJsonFile } from '@/utils/file-util';
import {
Background,
ConnectionMode,
ReactFlow,
ReactFlowProvider,
} from '@xyflow/react';
import { ArrowDownToLine } from 'lucide-react';
import { ReactNode, useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { nodeTypes } from '../canvas';

export function VersionDialog({
hideModal,
}: IModalProps<any> & { initialName?: string; title?: ReactNode }) {
const { t } = useTranslation();
const { data, loading } = useFetchVersionList();
const [selectedId, setSelectedId] = useState<string>('');
const { data: agent, loading: versionLoading } = useFetchVersion(selectedId);

const handleClick = useCallback(
(id: string) => () => {
setSelectedId(id);
},
[],
);

const downloadFile = useCallback(() => {
const graph = agent?.dsl.graph;
if (graph) {
downloadJsonFile(graph, agent?.title);
}
}, [agent?.dsl.graph, agent?.title]);

useEffect(() => {
if (data.length > 0) {
setSelectedId(data[0].id);
}
}, [data]);

return (
<Dialog open onOpenChange={hideModal}>
<DialogContent className="max-w-[60vw]">
<DialogHeader>
<DialogTitle>{t('flow.historyversion')}</DialogTitle>
</DialogHeader>
<section className="flex gap-2 relative">
<div className="w-1/3 max-h-[60vh] overflow-auto min-h-[40vh]">
{loading ? (
<Spin className="top-1/2"></Spin>
) : (
<ul className="space-y-2">
{data.map((x) => (
<li
key={x.id}
className={cn('cursor-pointer', {
'bg-card rounded p-1': x.id === selectedId,
})}
onClick={handleClick(x.id)}
>
{x.title}
</li>
))}
</ul>
)}
</div>

<div className="relative flex-1 ">
{versionLoading ? (
<Spin className="top-1/2" />
) : (
<Card className="h-full">
<CardContent className="h-full p-5">
<section className="flex justify-between">
<div>
<div className="pb-1">{agent?.title}</div>
<p className="text-text-sub-title text-xs">
{formatDate(agent?.create_date)}
</p>
</div>
<Button variant={'ghost'} onClick={downloadFile}>
<ArrowDownToLine />
</Button>
</section>
<ReactFlowProvider key={`flow-${selectedId}`}>
<ReactFlow
connectionMode={ConnectionMode.Loose}
nodes={agent?.dsl.graph?.nodes || []}
edges={
agent?.dsl.graph?.edges.flatMap((x) => ({
...x,
type: 'default',
})) || []
}
fitView
nodeTypes={nodeTypes}
edgeTypes={{}}
zoomOnScroll={true}
panOnDrag={true}
zoomOnDoubleClick={false}
preventScrolling={true}
minZoom={0.1}
>
<Background />
</ReactFlow>
</ReactFlowProvider>
</CardContent>
</Card>
)}
</div>
</section>
</DialogContent>
</Dialog>
);
}

+ 1
- 0
web/tailwind.css View File

@@ -199,6 +199,7 @@
--background-checked: rgba(76, 164, 231, 1);

--background-highlight: rgba(76, 164, 231, 0.1);
--background-agent: rgba(22, 22, 24, 1);

--input-border: rgba(255, 255, 255, 0.2);


Loading…
Cancel
Save