Skip to main content
Glama
AgentsDetails.tsx12.4 kB
import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Separator } from "@/components/ui/separator"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import { useDashboardStore, useModalsStore } from "@/store"; import { Agent } from "@/types"; import { formatDateTime, formatRelativeTime, isActive } from "@/utils"; import { Activity, Brain, CircleX, ShieldCheck, User, Users, } from "lucide-react"; import { useMemo, useRef } from "react"; import { DashboardScrollArea } from "./DashboardScrollArea"; import { AgentDetailsModal } from "./AgentDetailsModal"; export type AgentsDetailsProps = { agents: Agent[] }; export const AgentsDetails = ({ agents }: AgentsDetailsProps) => { const { search, setSearch } = useDashboardStore((s) => ({ search: s.searchAgentsValue, setSearch: s.setSearchAgentsValue, })); const { openAgentDetailsModal, isAgentDetailsModalOpen, selectedAgent, closeAgentDetailsModal, } = useModalsStore((s) => ({ openAgentDetailsModal: s.openAgentDetailsModal, isAgentDetailsModalOpen: s.isAgentDetailsModalOpen, selectedAgent: s.selectedAgent, closeAgentDetailsModal: s.closeAgentDetailsModal, })); const filteredList = useMemo(() => { if (!search) return agents; return agents.filter((agent) => agent.identifier.toLowerCase().includes(search.toLowerCase()), ); }, [agents, search]); const inputRef = useRef<HTMLInputElement>(null); if (!agents?.length) { // If no agents are available, show a placeholder return ( <DashboardScrollArea> <div className="h-full flex items-center justify-center p-4"> <div className="text-center text-[var(--color-text-secondary)]"> <Users className="w-8 h-8 mx-auto mb-2 opacity-50" /> <p className="text-sm">No Agents</p> </div> </div> </DashboardScrollArea> ); } // Render the list of agents return ( <DashboardScrollArea> <div className="flex flex-col h-full relative px-3 gap-3"> <div className="sticky top-0 z-10 flex-shrink-0 bg-[var(--color-bg-container)] py-2"> <div className="flex items-center justify-between gap-3"> <Card className="bg-background"> <CardContent className="pt-6 grid gap-4 grid-cols-[70px_1px_70px_1px_200px]"> <div className="flex-col items-start justify-center"> <div className="text-sm font-medium text-[var(--color-text-primary)]"> Agents </div> <div className="text-2xl font-bold text-[var(--color-text-primary)]"> {agents.length} </div> </div> <Separator orientation="vertical" className="bg-border h-auto" /> <div className="flex-col items-start justify-center"> <div className="text-sm font-medium text-[var(--color-text-primary)]"> Calls </div> <div className="text-2xl font-bold text-[var(--color-text-primary)]"> {agents.reduce( (acc, agent) => acc + (agent.usage?.callCount || 0), 0, )} </div> </div> <Separator orientation="vertical" className="bg-border h-auto" /> <div className="flex-col items-start justify-center"> <div className="text-sm font-medium text-[var(--color-text-primary)]"> Last invocation </div> <div className="text-2xl font-bold text-[var(--color-text-primary)]"> {formatRelativeTime( agents.reduce((latest, agent) => { const lastCall = agent.usage?.lastCalledAt; if (!lastCall) return latest; const lastDate = new Date(lastCall).getTime(); return lastDate > latest ? lastDate : latest; }, 0), )} </div> </div> </CardContent> </Card> <div className="flex items-center focus-within:border-[var(--color-border-secondary)] focus-within:border-solid self-start"> <Input className="bg-background shadow-none rounded-md border-[1px] border-[var(--color-border-interactive)] focus-visible:ring-0 placeholder:text-[var(--color-text-secondary)] font-normal text-sm h-7.5 w-[180px]" placeholder="Search agents..." value={search} onChange={(e) => setSearch(e.target.value)} ref={inputRef} /> <Tooltip> <TooltipTrigger asChild> <Button onClick={() => { setSearch(""); inputRef.current?.focus(); }} variant="vanilla" className="background-transparent focus-visible:ring-0 hover:text-[var(--color-fg-interactive)] focus:text-[var(--color-fg-interactive)] focus-visible:bg-[var(--color-bg-container-overlay)] h-7 w-4 rounded-none" > <CircleX /> </Button> </TooltipTrigger> <TooltipContent align="center" className="shadow bg-[var(--color-bg-container)] text-[var(--color-fg-info)] text-xs" > Clear search </TooltipContent> </Tooltip> </div> </div> </div> {search && filteredList.length === 0 && ( <div className="h-full flex items-center justify-center p-4"> <div className="text-center text-[var(--color-text-secondary)]"> <Users className="w-8 h-8 mx-auto mb-2 opacity-50" /> <p>No results found</p> <Button variant="secondary" size="sm" className="inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:hover:bg-background disabled:hover:text-[var(--color-fg-interactive)] disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border bg-background shadow-sm hover:text-accent-foreground text-[9px] px-1 py-0.5 border-[var(--color-border-interactive)] text-[var(--color-fg-interactive)] hover:bg-[var(--color-bg-interactive-hover)] mt-4 hover:bg-[var(--color-bg-container-overlay)] text-[var(--color-text-secondary)] text-sm px-2 py-1" onClick={() => setSearch("")} > <CircleX className="w-3 h-3 text-[var(--color-fg-interactive)] cursor-pointer" /> Clear Search </Button> </div> </div> )} {filteredList.map((agent, index) => ( <Card key={`${agent.id}_${index}`} className={`mb-3 border-[var(--color-border-primary)] bg-[var(--color-bg-container-overlay)] rounded-md cursor-pointer hover:shadow-md transition-shadow ${isActive(agent.usage.lastCalledAt) ? "border-[var(--color-fg-success)] bg-[var(--color-bg-success)]" : ""}`} onClick={() => openAgentDetailsModal(agent)} > <CardHeader className="p-3 border-b border-[var(--color-border-primary)]"> <CardTitle key={`${agent.id}_${index}`} className="text-lg flex justify-start items-center whitespace-nowrap font-bold text-[var(--color-text-primary)]] p-0 select-none gap-3 grid grid-cols-[minmax(min-content,_240px)_1fr] leading-8" > <span className="flex items-center whitespace-nowrap gap-1"> <User className="text-[var(--color-fg-interactive)]" /> <span className="truncate max-w-[240px] text-[var(--color-fg-interactive)]"> {agent.identifier} </span> </span> <span className="font-semibold text-sm truncate max-w-[370px]"> Session ID: {agent.sessionIds?.[0] || "No session"} </span> </CardTitle> </CardHeader> <CardContent className="p-3 space-y-1.5"> <div className="flex flex-col h-full relative"> {/* <AgentControls agent={selectedAgent} /> */} <div className="grid grid-cols-3 gap-6 mb-2 "> <div className="text-xs"> <h4 className="text-sm font-medium text-[var(--color-text-secondary)] mb-0.5 flex items-center gap-1"> <ShieldCheck className="w-4 h-4" /> Session </h4> <p className="flex items-center gap-1"> <span className="font-semibold">ID:</span> <span className="truncate max-w-[120px] inline-block align-bottom select-all"> {agent.sessionIds?.[0] || "N/A"} </span> </p> <span className="font-semibold">Status:</span> <Badge variant="outline" className="capitalize ml-1 px-1 py-0 bg-[var(--color-bg-success)] text-[var(--color-fg-success)] border-[var(--color-border-success)]" > {agent.status} </Badge> <p className="flex items-center gap-1"> <span className="font-semibold">Last Activity:</span> {formatDateTime(agent.lastActivity)} </p> </div> {agent.llm && agent.llm.provider !== "unknown" && ( <div className="text-xs"> <h4 className="text-sm font-medium text-[var(--color-text-secondary)] mb-0.5 flex items-center gap-1"> <Brain className="w-4 h-4" /> LLM </h4> <p className="flex items-center gap-1"> <span className="font-semibold">Provider:</span> {agent.llm.provider} </p> <p className="flex items-center gap-1"> <span className="font-semibold">Model:</span> <span className="truncate max-w-[120px] inline-block align-bottom"> {"model" in agent.llm ? agent.llm.model : agent.llm.modelId} </span> </p> </div> )} {agent.usage && ( <div className="text-xs"> <h4 className="text-sm font-medium text-[var(--color-text-secondary)] mb-0.5 flex items-center gap-1"> <Activity className="w-4 h-4" /> Usage </h4> <p className="flex items-center gap-1"> <span className="font-semibold">Calls:</span> {agent.usage.callCount} </p> <p className="flex items-center gap-1"> <span className="font-semibold">Last Call:</span> {formatDateTime(agent.usage.lastCalledAt)} </p> </div> )} </div> </div> </CardContent> </Card> ))} </div> {/* Agent Details Modal */} <AgentDetailsModal agent={selectedAgent || null} isOpen={isAgentDetailsModalOpen} onClose={closeAgentDetailsModal} /> </DashboardScrollArea> ); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TheLunarCompany/lunar'

If you have feedback or need assistance with the MCP directory API, please join our Discord server