Skip to content

Commit

Permalink
feat: remove chat from agent edit
Browse files Browse the repository at this point in the history
- made header sticky
- adds animation and Try it Out! CTA to open agent in new tab
- removes footer with chat actions and save status

Signed-off-by: Ryan Hopper-Lowe <[email protected]>
  • Loading branch information
ryanhopperlowe committed Feb 10, 2025
1 parent 82f65b4 commit 8414dee
Show file tree
Hide file tree
Showing 15 changed files with 263 additions and 319 deletions.
278 changes: 104 additions & 174 deletions ui/admin/app/components/agent/Agent.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { GearIcon } from "@radix-ui/react-icons";
import { BlocksIcon, LibraryIcon, PlusIcon, WrenchIcon } from "lucide-react";
import { BlocksIcon, LibraryIcon, WrenchIcon } from "lucide-react";
import { useCallback, useEffect, useState } from "react";

import { Agent as AgentType } from "~/lib/model/agents";
import { AssistantNamespace } from "~/lib/model/assistants";
import { cn } from "~/lib/utils";

import { AgentAlias } from "~/components/agent/AgentAlias";
import { useAgent } from "~/components/agent/AgentContext";
import { AgentForm } from "~/components/agent/AgentForm";
import { AgentIntroForm } from "~/components/agent/AgentIntroForm";
import { PastThreads } from "~/components/agent/PastThreads";
import { ToolForm } from "~/components/agent/ToolForm";
import { AgentCapabilityForm } from "~/components/agent/shared/AgentCapabilityForm";
import { AgentModelSelect } from "~/components/agent/shared/AgentModelSelect";
Expand All @@ -24,23 +22,13 @@ import {
AccordionItem,
AccordionTrigger,
} from "~/components/ui/accordion";
import { Button } from "~/components/ui/button";
import { CardDescription } from "~/components/ui/card";
import { ScrollArea } from "~/components/ui/scroll-area";
import { useDebounce } from "~/hooks/useDebounce";

type AgentProps = {
className?: string;
currentThreadId?: string | null;
onRefresh?: (threadId: string | null) => void;
};

export function Agent({ className, currentThreadId, onRefresh }: AgentProps) {
const { agent, updateAgent, refreshAgent, isUpdating, lastUpdated, error } =
useAgent();
export function Agent() {
const { agent, updateAgent, refreshAgent } = useAgent();

const [agentUpdates, setAgentUpdates] = useState(agent);
const [enableScrollStick, setEnableScrollStick] = useState(false);

useEffect(() => {
setAgentUpdates((prev) => {
Expand Down Expand Up @@ -75,167 +63,109 @@ export function Agent({ className, currentThreadId, onRefresh }: AgentProps) {
[agent, agentUpdates, debouncedUpdateAgent]
);

const handleThreadSelect = useCallback(
(threadId: string) => {
onRefresh?.(threadId);
},
[onRefresh]
);

const handleAccordionValueChange = useCallback((value: string[]) => {
setEnableScrollStick(value.includes("model"));
}, []);

return (
<div className="flex h-full flex-col">
<ScrollArea
className={cn("h-full", className)}
enableScrollStick={enableScrollStick ? "bottom" : undefined}
>
<AgentAlias agent={agentUpdates} onChange={partialSetAgent} />

<div className="m-4 p-4">
<AgentForm agent={agentUpdates} onChange={partialSetAgent} />
</div>

<div className="m-4 p-4">
<AgentIntroForm agent={agentUpdates} onChange={partialSetAgent} />
</div>

<div className="m-4 space-y-4 p-4">
<h4 className="flex items-center gap-2 border-b pb-2">
<BlocksIcon />
Capabilities
</h4>

<CardDescription>
Capabilities define how users can interact with this agent in the
chat interface. Each capability enables specific features that users
can access when using the agent.
</CardDescription>

<AgentCapabilityForm
entity={agentUpdates}
onChange={partialSetAgent}
/>
</div>

<div className="m-4 space-y-4 p-4">
<h4 className="flex items-center gap-2 border-b pb-2">
<WrenchIcon />
Tools
</h4>

<CardDescription>
Add tools that allow the agent to perform useful actions such as
searching the web, reading files, or interacting with other systems.
</CardDescription>

<ToolForm
agent={agentUpdates}
onChange={({ tools, oauthApps }) =>
partialSetAgent(convertTools(tools, oauthApps))
}
renderActions={renderActions}
/>
</div>

<div className="m-4 space-y-4 p-4">
<h4 className="flex items-center gap-2 border-b pb-2">
<LibraryIcon />
Knowledge
</h4>

<CardDescription>
Provide knowledge to the agent in the form of files, website, or
external links in order to give it context about various topics.
</CardDescription>

<AgentKnowledgePanel
agentId={agent.id}
agent={agent}
updateAgent={partialSetAgent}
addTool={(tool) => {
if (agent?.tools?.includes(tool)) return;

partialSetAgent({
tools: [...(agent.tools || []), tool],
});
}}
/>
</div>

<WorkspaceFilesSection entityId={agent.id} />

<Accordion
type="multiple"
className="m-4 p-4"
onValueChange={handleAccordionValueChange}
>
<AccordionItem value="model">
<AccordionTrigger className="border-b">
<h4 className="flex items-center gap-2">
<GearIcon className="size-5" />
Advanced
</h4>
</AccordionTrigger>

<AccordionContent className="space-y-8 py-4">
<div className="flex flex-col gap-4">
<h4>Model</h4>

<CardDescription>
The model to use for the agent.
</CardDescription>

<AgentModelSelect
entity={agentUpdates}
onChange={(updates) => partialSetAgent(updates)}
/>
</div>

<EnvironmentVariableSection
entity={agent}
onUpdate={partialSetAgent}
entityType="agent"
<div className="relative flex h-full flex-col">
<AgentAlias agent={agentUpdates} onChange={partialSetAgent} />

<div className="m-4 p-4">
<AgentForm agent={agentUpdates} onChange={partialSetAgent} />
</div>

<div className="m-4 p-4">
<AgentIntroForm agent={agentUpdates} onChange={partialSetAgent} />
</div>

<div className="m-4 space-y-4 p-4">
<h4 className="flex items-center gap-2 border-b pb-2">
<BlocksIcon />
Capabilities
</h4>

<CardDescription>
Capabilities define how users can interact with this agent in the chat
interface. Each capability enables specific features that users can
access when using the agent.
</CardDescription>

<AgentCapabilityForm entity={agentUpdates} onChange={partialSetAgent} />
</div>

<div className="m-4 space-y-4 p-4">
<h4 className="flex items-center gap-2 border-b pb-2">
<WrenchIcon />
Tools
</h4>

<CardDescription>
Add tools that allow the agent to perform useful actions such as
searching the web, reading files, or interacting with other systems.
</CardDescription>

<ToolForm
agent={agentUpdates}
onChange={({ tools, oauthApps }) =>
partialSetAgent(convertTools(tools, oauthApps))
}
renderActions={renderActions}
/>
</div>

<div className="m-4 space-y-4 p-4">
<h4 className="flex items-center gap-2 border-b pb-2">
<LibraryIcon />
Knowledge
</h4>

<CardDescription>
Provide knowledge to the agent in the form of files, website, or
external links in order to give it context about various topics.
</CardDescription>

<AgentKnowledgePanel
agentId={agent.id}
agent={agent}
updateAgent={partialSetAgent}
addTool={(tool) => {
if (agent?.tools?.includes(tool)) return;

partialSetAgent({
tools: [...(agent.tools || []), tool],
});
}}
/>
</div>

<WorkspaceFilesSection entityId={agent.id} />

<Accordion type="multiple" className="m-4 p-4">
<AccordionItem value="model">
<AccordionTrigger className="border-b">
<h4 className="flex items-center gap-2">
<GearIcon className="size-5" />
Advanced
</h4>
</AccordionTrigger>

<AccordionContent className="space-y-8 py-4">
<div className="flex flex-col gap-4">
<h4>Model</h4>

<CardDescription>The model to use for the agent.</CardDescription>

<AgentModelSelect
entity={agentUpdates}
onChange={(updates) => partialSetAgent(updates)}
/>
</AccordionContent>
</AccordionItem>
</Accordion>
</ScrollArea>

<footer className="flex items-center justify-between gap-4 px-8 py-4 shadow-inner">
<div className="text-muted-foreground">
{error ? (
<p>Error saving agent</p>
) : isUpdating ? (
<p>Saving...</p>
) : lastUpdated ? (
<p>Saved</p>
) : (
<div />
)}
</div>

<div className="flex gap-2">
<PastThreads
currentThreadId={currentThreadId}
agentId={agent.id}
onThreadSelect={handleThreadSelect}
/>

<Button
variant="outline"
className="flex gap-2"
onClick={() => {
onRefresh?.(null);
}}
>
<PlusIcon />
New Thread
</Button>
</div>
</footer>
</div>

<EnvironmentVariableSection
entity={agent}
onUpdate={partialSetAgent}
entityType="agent"
/>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
);

Expand Down
29 changes: 18 additions & 11 deletions ui/admin/app/components/agent/AgentAlias.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ArrowRightIcon } from "lucide-react";
import { useMemo } from "react";
import { Link, useNavigate } from "react-router";
import { useNavigate } from "react-router";
import { $path } from "safe-routes";
import useSWR from "swr";

Expand All @@ -13,6 +14,8 @@ import { Publish } from "~/components/agent/Publish";
import { CopyText } from "~/components/composed/CopyText";
import { WarningAlert } from "~/components/composed/WarningAlert";
import { LoadingSpinner } from "~/components/ui/LoadingSpinner";
import { AnimateResize } from "~/components/ui/animate/animate-resize";
import { Link } from "~/components/ui/link";

type AgentAliasProps = {
agent: Agent;
Expand Down Expand Up @@ -40,7 +43,7 @@ export function AgentAlias({ agent, onChange }: AgentAliasProps) {
);

return (
<div className="flex w-full flex-col gap-4 px-8 pt-4">
<div className="sticky top-0 z-10 flex h-16 w-full flex-col gap-4 border-b bg-background px-8 pt-4">
<div className="flex w-full justify-between gap-4">
<div className="flex flex-col gap-2">
{agent.aliasAssigned === undefined &&
Expand All @@ -57,24 +60,28 @@ export function AgentAlias({ agent, onChange }: AgentAliasProps) {
/>

<Link
as="button"
to={agentUrl}
target="_blank"
rel="noreferrer"
className="text-muted-foreground underline"
to={agentUrl}
className="group flex items-center gap-2"
>
{agentUrl}
<AnimateResize>
<span className="group-hover:hidden">Try it Out!</span>
<span className="hidden group-hover:block">{agentUrl}</span>
</AnimateResize>
<ArrowRightIcon />
</Link>

<Publish
alias={agent.alias}
id={agent.id}
onPublish={(alias) => onChange({ alias })}
/>
</div>
)}
</div>

<div className="flex gap-2">
<Publish
alias={agent.alias}
id={agent.id}
onPublish={(alias) => onChange({ alias })}
/>
<AgentAccessControl agent={agent} />
<DeleteAgent id={agent.id} onSuccess={() => navigate("/agents")} />
</div>
Expand Down
Loading

0 comments on commit 8414dee

Please sign in to comment.