mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-03 03:14:57 -05:00
add find agent tool in coiplot-2
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
import { MessageSquareIcon } from "lucide-react";
|
||||
import { UIMessage, UIDataTypes, UITools, ToolUIPart } from "ai";
|
||||
import { FindBlocksTool } from "../../tools/FindBlocks/FindBlocks";
|
||||
import { FindAgentsTool } from "../../tools/FindAgents/FindAgents";
|
||||
|
||||
interface ChatMessagesContainerProps {
|
||||
messages: UIMessage<unknown, UIDataTypes, UITools>[];
|
||||
@@ -61,6 +62,14 @@ export const ChatMessagesContainer = ({
|
||||
part={part as ToolUIPart}
|
||||
/>
|
||||
);
|
||||
case "tool-find_agent":
|
||||
case "tool-find_library_agent":
|
||||
return (
|
||||
<FindAgentsTool
|
||||
key={`${message.id}-${i}`}
|
||||
part={part as ToolUIPart}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
} from "@/components/ui/sidebar";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
SparkleIcon,
|
||||
PlusIcon,
|
||||
SpinnerGapIcon,
|
||||
ChatCircleIcon,
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
"use client";
|
||||
|
||||
import { ToolUIPart } from "ai";
|
||||
import { CaretDownIcon } from "@phosphor-icons/react";
|
||||
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { MorphingTextAnimation } from "../../components/MorphingTextAnimation/MorphingTextAnimation";
|
||||
import {
|
||||
getAgentHref,
|
||||
getAnimationText,
|
||||
getFindAgentsOutput,
|
||||
getSourceLabelFromToolType,
|
||||
StateIcon,
|
||||
} from "./helpers";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export interface FindAgentsToolPart {
|
||||
type: string;
|
||||
toolCallId: string;
|
||||
state: ToolUIPart["state"];
|
||||
input?: unknown;
|
||||
output?: unknown;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
part: FindAgentsToolPart;
|
||||
}
|
||||
|
||||
export function FindAgentsTool({ part }: Props) {
|
||||
const text = getAnimationText(part);
|
||||
const output = getFindAgentsOutput(part);
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const query =
|
||||
typeof part.input === "object" && part.input !== null
|
||||
? String((part.input as { query?: unknown }).query ?? "").trim()
|
||||
: "";
|
||||
|
||||
const isAgentsFound =
|
||||
part.state === "output-available" && output?.type === "agents_found";
|
||||
const hasAgents =
|
||||
isAgentsFound &&
|
||||
output.agents.length > 0 &&
|
||||
(typeof output.count !== "number" || output.count > 0);
|
||||
const totalCount = isAgentsFound ? output.count : 0;
|
||||
const { label: sourceLabel, source } = getSourceLabelFromToolType(part.type);
|
||||
const scopeText =
|
||||
source === "library"
|
||||
? "in your library"
|
||||
: source === "marketplace"
|
||||
? "in marketplace"
|
||||
: "";
|
||||
|
||||
return (
|
||||
<div className="py-2">
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<StateIcon state={part.state} />
|
||||
<MorphingTextAnimation text={text} />
|
||||
</div>
|
||||
|
||||
{hasAgents && (
|
||||
<div className="mt-2 rounded-2xl border bg-background px-3 py-2">
|
||||
<button
|
||||
type="button"
|
||||
aria-expanded={isExpanded}
|
||||
onClick={() => setIsExpanded((v) => !v)}
|
||||
className="flex w-full items-center justify-between gap-3 py-1 text-left"
|
||||
>
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<span className="rounded-full border bg-muted px-2 py-0.5 text-[11px] font-medium text-muted-foreground">
|
||||
{sourceLabel}
|
||||
</span>
|
||||
<div className="min-w-0">
|
||||
<p className="truncate text-sm font-medium text-foreground">
|
||||
Agent results
|
||||
</p>
|
||||
<p className="truncate text-xs text-muted-foreground">
|
||||
Found {totalCount} {scopeText}
|
||||
{query ? ` for "${query}"` : ""}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<CaretDownIcon
|
||||
className={cn(
|
||||
"h-4 w-4 shrink-0 text-muted-foreground transition-transform",
|
||||
isExpanded && "rotate-180",
|
||||
)}
|
||||
weight="bold"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<AnimatePresence initial={false}>
|
||||
{isExpanded && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0, filter: "blur(10px)" }}
|
||||
animate={{ height: "auto", opacity: 1, filter: "blur(0px)" }}
|
||||
exit={{ height: 0, opacity: 0, filter: "blur(10px)" }}
|
||||
transition={
|
||||
shouldReduceMotion
|
||||
? { duration: 0 }
|
||||
: { type: "spring", bounce: 0.35, duration: 0.55 }
|
||||
}
|
||||
className="overflow-hidden"
|
||||
style={{ willChange: "height, opacity, filter" }}
|
||||
>
|
||||
<div className="grid gap-2 pb-2 pt-3 sm:grid-cols-2">
|
||||
{output.agents.map((agent) => {
|
||||
const href = getAgentHref(agent);
|
||||
const agentSource =
|
||||
agent.source === "library"
|
||||
? "Library"
|
||||
: agent.source === "marketplace"
|
||||
? "Marketplace"
|
||||
: null;
|
||||
return (
|
||||
<div
|
||||
key={agent.id}
|
||||
className="rounded-2xl border bg-background p-3"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="truncate text-sm font-medium text-foreground">
|
||||
{agent.name}
|
||||
</p>
|
||||
{agentSource && (
|
||||
<span className="shrink-0 rounded-full border bg-muted px-2 py-0.5 text-[11px] text-muted-foreground">
|
||||
{agentSource}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-1 line-clamp-2 text-xs text-muted-foreground">
|
||||
{agent.description}
|
||||
</p>
|
||||
</div>
|
||||
{href && (
|
||||
<Link
|
||||
href={href}
|
||||
className="shrink-0 text-xs font-medium text-purple-600 hover:text-purple-700"
|
||||
>
|
||||
Open
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
import { ToolUIPart } from "ai";
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
CircleNotchIcon,
|
||||
XCircleIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
|
||||
export interface FindAgentInput {
|
||||
query: string;
|
||||
}
|
||||
|
||||
export interface AgentInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
source?: "marketplace" | "library" | string;
|
||||
}
|
||||
|
||||
export interface AgentsFoundOutput {
|
||||
type: "agents_found";
|
||||
title?: string;
|
||||
message?: string;
|
||||
session_id?: string;
|
||||
agents: AgentInfo[];
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface NoResultsOutput {
|
||||
type: "no_results";
|
||||
message: string;
|
||||
suggestions?: string[];
|
||||
session_id?: string;
|
||||
}
|
||||
|
||||
export interface ErrorOutput {
|
||||
type: "error";
|
||||
message: string;
|
||||
error?: string;
|
||||
session_id?: string;
|
||||
}
|
||||
|
||||
export type FindAgentsOutput =
|
||||
| AgentsFoundOutput
|
||||
| NoResultsOutput
|
||||
| ErrorOutput;
|
||||
|
||||
export type FindAgentsToolType =
|
||||
| "tool-find_agent"
|
||||
| "tool-find_library_agent"
|
||||
| (string & {});
|
||||
|
||||
function parseOutput(output: unknown): FindAgentsOutput | null {
|
||||
if (!output) return null;
|
||||
if (typeof output === "string") {
|
||||
const trimmed = output.trim();
|
||||
if (!trimmed) return null;
|
||||
try {
|
||||
return JSON.parse(trimmed) as FindAgentsOutput;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (typeof output === "object") {
|
||||
return output as FindAgentsOutput;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getFindAgentsOutput(part: unknown): FindAgentsOutput | null {
|
||||
if (!part || typeof part !== "object") return null;
|
||||
return parseOutput((part as { output?: unknown }).output);
|
||||
}
|
||||
|
||||
export function getSourceLabelFromToolType(toolType?: FindAgentsToolType): {
|
||||
source: "marketplace" | "library" | "unknown";
|
||||
label: string;
|
||||
} {
|
||||
if (toolType === "tool-find_library_agent") {
|
||||
return { source: "library", label: "Library" };
|
||||
}
|
||||
if (toolType === "tool-find_agent") {
|
||||
return { source: "marketplace", label: "Marketplace" };
|
||||
}
|
||||
return { source: "unknown", label: "Agents" };
|
||||
}
|
||||
|
||||
export function getAnimationText(part: {
|
||||
type?: FindAgentsToolType;
|
||||
state: ToolUIPart["state"];
|
||||
input?: unknown;
|
||||
output?: unknown;
|
||||
}): string {
|
||||
const { label, source } = getSourceLabelFromToolType(part.type);
|
||||
switch (part.state) {
|
||||
case "input-streaming":
|
||||
return `Searching ${label.toLowerCase()} agents for you`;
|
||||
|
||||
case "input-available": {
|
||||
const query = (part.input as FindAgentInput | undefined)?.query?.trim();
|
||||
if (query) {
|
||||
return source === "library"
|
||||
? `Finding library agents matching "${query}"`
|
||||
: `Finding marketplace agents matching "${query}"`;
|
||||
}
|
||||
return source === "library" ? "Finding library agents" : "Finding agents";
|
||||
}
|
||||
|
||||
case "output-available": {
|
||||
const output = parseOutput(part.output);
|
||||
const query = (part.input as FindAgentInput | undefined)?.query?.trim();
|
||||
const scope = source === "library" ? "in your library" : "in marketplace";
|
||||
if (!output) {
|
||||
return query ? `Found agents ${scope} for "${query}"` : "Found agents";
|
||||
}
|
||||
if (output.type === "no_results") {
|
||||
return query
|
||||
? `No agents found ${scope} for "${query}"`
|
||||
: `No agents found ${scope}`;
|
||||
}
|
||||
if (output.type === "agents_found") {
|
||||
const count = output.count ?? output.agents?.length ?? 0;
|
||||
const countText = `Found ${count} agent${count === 1 ? "" : "s"}`;
|
||||
if (query) return `${countText} ${scope} for "${query}"`;
|
||||
return `${countText} ${scope}`;
|
||||
}
|
||||
if (output.type === "error") {
|
||||
return `Error finding agents ${scope}`;
|
||||
}
|
||||
return `Found agents ${scope}`;
|
||||
}
|
||||
|
||||
case "output-error":
|
||||
return source === "library"
|
||||
? "Error finding agents in your library"
|
||||
: "Error finding agents in marketplace";
|
||||
|
||||
default:
|
||||
return "Processing";
|
||||
}
|
||||
}
|
||||
|
||||
export function getAgentHref(agent: AgentInfo): string | null {
|
||||
if (agent.source === "library") {
|
||||
return `/library/agents/${encodeURIComponent(agent.id)}`;
|
||||
}
|
||||
|
||||
const [creator, slug, ...rest] = agent.id.split("/");
|
||||
if (!creator || !slug || rest.length > 0) return null;
|
||||
return `/marketplace/agent/${encodeURIComponent(creator)}/${encodeURIComponent(slug)}`;
|
||||
}
|
||||
|
||||
export function StateIcon({ state }: { state: ToolUIPart["state"] }) {
|
||||
switch (state) {
|
||||
case "input-streaming":
|
||||
case "input-available":
|
||||
return (
|
||||
<CircleNotchIcon
|
||||
className="h-4 w-4 animate-spin text-muted-foreground"
|
||||
weight="bold"
|
||||
/>
|
||||
);
|
||||
case "output-available":
|
||||
return <CheckCircleIcon className="h-4 w-4 text-green-500" />;
|
||||
case "output-error":
|
||||
return <XCircleIcon className="h-4 w-4 text-red-500" />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,7 @@ export type MessageBranchSelectorProps = HTMLAttributes<HTMLDivElement> & {
|
||||
|
||||
export const MessageBranchSelector = ({
|
||||
className,
|
||||
from,
|
||||
from: _from,
|
||||
...props
|
||||
}: MessageBranchSelectorProps) => {
|
||||
const { totalBranches } = useMessageBranch();
|
||||
@@ -223,7 +223,10 @@ export const MessageBranchSelector = ({
|
||||
|
||||
return (
|
||||
<ButtonGroup
|
||||
className="[&>*:not(:first-child)]:rounded-l-md [&>*:not(:last-child)]:rounded-r-md"
|
||||
className={cn(
|
||||
"[&>*:not(:first-child)]:rounded-l-md [&>*:not(:last-child)]:rounded-r-md",
|
||||
className,
|
||||
)}
|
||||
orientation="horizontal"
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -26,6 +26,7 @@ const buttonVariants = cva(
|
||||
sm: "h-8 rounded-md px-3 text-xs",
|
||||
lg: "h-10 rounded-md px-8",
|
||||
icon: "h-9 w-9",
|
||||
"icon-sm": "h-8 w-8",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
||||
Reference in New Issue
Block a user