wu981526092's picture
🚀 Deploy AgentGraph: Complete agent monitoring and knowledge graph system
c2ea5ed
raw
history blame
6.47 kB
import React, { useState } from "react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { GitBranch, Trash2 } from "lucide-react";
import { Trace } from "@/types";
import { useAgentGraph } from "@/context/AgentGraphContext";
import { api } from "@/lib/api";
interface TraceItemProps {
trace: Trace;
}
export function TraceItem({ trace }: TraceItemProps) {
const { state, actions } = useAgentGraph();
const { selectedTrace } = state;
const [isDeleting, setIsDeleting] = useState(false);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const isSelected = selectedTrace?.trace_id === trace.trace_id;
const handleSelect = () => {
// Set trace as selected and switch to trace-kg view
actions.setSelectedTrace(trace);
actions.setActiveView("trace-kg");
};
const handleDeleteClick = (e: React.MouseEvent) => {
e.stopPropagation(); // Prevent trace selection
setDeleteDialogOpen(true);
};
const handleDeleteConfirm = async () => {
setIsDeleting(true);
try {
await api.traces.delete(trace.trace_id);
// Refresh traces list by reloading
window.location.reload();
} catch (error) {
console.error("Error deleting trace:", error);
alert(
`Error deleting trace: ${
error instanceof Error ? error.message : String(error)
}`
);
} finally {
setIsDeleting(false);
setDeleteDialogOpen(false);
}
};
const handleDeleteCancel = () => {
setDeleteDialogOpen(false);
};
const hasKnowledgeGraphs = () => {
return trace.knowledge_graphs && trace.knowledge_graphs.length > 0;
};
const formatDate = (dateString?: string) => {
if (!dateString) return "";
return new Date(dateString).toLocaleDateString();
};
const formatSize = (bytes?: number) => {
if (!bytes) return "";
const mb = bytes / (1024 * 1024);
return `${mb.toFixed(1)} MB`;
};
const kgCount = trace.knowledge_graphs?.length || 0;
return (
<>
<div className={`group relative`}>
{/* Delete button - appears on hover */}
<Button
variant="ghost"
size="sm"
className="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity h-8 w-8 p-0 bg-background/80 hover:bg-destructive hover:text-destructive-foreground"
onClick={handleDeleteClick}
disabled={isDeleting}
title="Delete trace"
>
<Trash2 className="h-4 w-4" />
</Button>
<Button
variant={isSelected ? "secondary" : "ghost"}
className="w-full justify-start p-3 h-auto min-h-[4rem] overflow-hidden"
onClick={handleSelect}
disabled={isDeleting}
>
<div className="flex items-start gap-3 w-full">
<div className="flex-1 min-w-0 text-left">
<div className="flex items-center gap-2 mb-1">
<span className="font-medium truncate">{trace.filename}</span>
{hasKnowledgeGraphs() && (
<div className="flex items-center gap-1 text-xs text-muted-foreground">
<GitBranch className="h-3 w-3" />
{(() => {
// Count only final knowledge graphs (same logic as main content)
const finalKGs = trace.knowledge_graphs!.filter(
(kg) =>
kg.is_final === true ||
(kg.window_index === null && kg.window_total !== null)
);
const finalCount = finalKGs.length;
return finalCount;
})()}
{/* Show total window count across all final KGs */}
{(() => {
const totalWindows = trace.knowledge_graphs!.reduce(
(acc, kg) =>
acc + (kg.window_knowledge_graphs?.length || 0),
0
);
return totalWindows > 0 ? (
<span className="text-[10px]">+{totalWindows}W</span>
) : null;
})()}
</div>
)}
</div>
<div className="flex items-center gap-4 text-xs text-muted-foreground">
<span>{formatDate(trace.upload_timestamp)}</span>
{trace.character_count && (
<span>{trace.character_count.toLocaleString()} chars</span>
)}
{trace.size && <span>{formatSize(trace.size)}</span>}
</div>
</div>
</div>
</Button>
</div>
{/* Delete Confirmation Dialog */}
<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<DialogContent className="sm:max-w-md max-w-[90vw] w-full">
<DialogHeader>
<DialogTitle>Delete Trace</DialogTitle>
<DialogDescription className="space-y-2">
<div className="break-words">
Are you sure you want to delete the trace "{trace.filename}"?
</div>
<div className="space-y-1 text-sm">
<div>This will permanently remove:</div>
<div>• The trace and all its content</div>
<div>• All associated knowledge graphs ({kgCount} found)</div>
<div>• All entities and relationships</div>
<div>• All prompt reconstruction data</div>
<div>• All perturbation test results</div>
<div>• All causal analysis results</div>
</div>
<div className="font-medium text-destructive">
This action cannot be undone.
</div>
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline" onClick={handleDeleteCancel}>
Cancel
</Button>
<Button
variant="destructive"
onClick={handleDeleteConfirm}
disabled={isDeleting}
>
{isDeleting ? "Deleting..." : "Delete"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
);
}