Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: admin ui - display information on agent when refName conflict #593

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apiclient/types/assitant.go
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote some Go! (please review)

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type Assistant struct {
Name string `json:"name"`
Description string `json:"description"`
Icons AgentIcons `json:"icons"`
EntityID string `json:"entityID"`
}

type AssistantList List[Assistant]
Expand Down
1 change: 1 addition & 0 deletions pkg/api/handlers/assistants.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ func convertAssistant(agent v1.Agent) types.Assistant {
Metadata: MetadataFrom(&agent),
Name: agent.Spec.Manifest.Name,
Description: agent.Spec.Manifest.Description,
EntityID: agent.ObjectMeta.Name,
Icons: icons,
}
assistant.ID = agent.Spec.Manifest.RefName
Expand Down
9 changes: 8 additions & 1 deletion pkg/storage/openapi/generated/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 7 additions & 23 deletions ui/admin/app/components/agent/Agent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import { cn } from "~/lib/utils";
import { TypographyH4, TypographyP } from "~/components/Typography";
import { useAgent } from "~/components/agent/AgentContext";
import { AgentForm } from "~/components/agent/AgentForm";
import { AgentPublishStatus } from "~/components/agent/AgentPublishStatus";
import { PastThreads } from "~/components/agent/PastThreads";
import { Publish } from "~/components/agent/Publish";
import { ToolForm } from "~/components/agent/ToolForm";
import { Unpublish } from "~/components/agent/Unpublish";
import { CopyText } from "~/components/composed/CopyText";
import { AgentKnowledgePanel } from "~/components/knowledge";
import { Button } from "~/components/ui/button";
import { Card } from "~/components/ui/card";
Expand Down Expand Up @@ -52,26 +50,11 @@ export function Agent({ className, onRefresh }: AgentProps) {
return (
<div className="h-full flex flex-col">
<ScrollArea className={cn("h-full", className)}>
<div className="flex w-full justify-between px-8 pt-4 items-center gap-4">
{agentUpdates.refName ? (
<CopyText
className="h-8 text-muted-foreground text-sm bg-background flex-row-reverse"
holdStatusDelay={10000}
text={`${window.location.protocol}//${window.location.host}/${agentUpdates.refName}`}
/>
) : (
<div />
)}

{agentUpdates.refName ? (
<Unpublish onChange={debouncedSetAgentInfo} />
) : (
<Publish
agent={agentUpdates}
onChange={debouncedSetAgentInfo}
/>
)}
</div>
<AgentPublishStatus
agent={agentUpdates}
onChange={partialSetAgent}
/>

<Card className="p-4 m-4 lg:mx-6 xl:mx-8">
<AgentForm
agent={agentUpdates}
Expand Down Expand Up @@ -146,6 +129,7 @@ export function Agent({ className, onRefresh }: AgentProps) {
</div>
);
}

function convertTools(
tools: { tool: string; variant: "fixed" | "default" | "available" }[]
) {
Expand Down
97 changes: 97 additions & 0 deletions ui/admin/app/components/agent/AgentPublishStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Link } from "@remix-run/react";
import { useMemo } from "react";
import { $path } from "remix-routes";
import useSWR from "swr";

import { AgentBase } from "~/lib/model/agents";
import { AssistantApiService } from "~/lib/service/api/assistantApiService";

import { TypographySmall } from "~/components/Typography";
import { Publish } from "~/components/agent/Publish";
import { Unpublish } from "~/components/agent/Unpublish";
import { CopyText } from "~/components/composed/CopyText";

type AgentPublishStatusProps = {
agent: AgentBase;
onChange: (agent: Partial<AgentBase>) => void;
};

export function AgentPublishStatus({
agent,
onChange,
}: AgentPublishStatusProps) {
const getAssistants = useSWR(
() =>
agent.refName && !agent.refNameAssigned
? AssistantApiService.getAssistants.key()
: null,
() => AssistantApiService.getAssistants()
);

const refAgent = useMemo(() => {
if (!getAssistants.data) return null;

return getAssistants.data.find(({ id }) => id === agent.refName);
}, [getAssistants.data, agent.refName]);

return (
<div className="flex w-full justify-between px-8 pt-4 items-center gap-4">
{renderAgentRef()}

{agent.refName ? (
<Unpublish onUnpublish={() => onChange({ refName: "" })} />
) : (
<Publish
refName={agent.refName}
onPublish={(refName) => onChange({ refName })}
/>
)}
</div>
);

function renderAgentRef() {
if (!agent.refName) return <div />;

if (refAgent) {
const route =
refAgent.type === "agent"
? $path("/agents/:agent", {
agent: refAgent.entityID,
})
: $path("/workflows/:workflow", {
workflow: refAgent.entityID,
});

return (
<div className="flex flex-col gap-1 h-full">
<div className="flex items-center gap-2">
<div className="size-2 bg-warning rounded-full" />
<TypographySmall>Unavailable</TypographySmall>
</div>

<TypographySmall className="pb-0 text-muted-foreground">
<span className="min-w-fit">
Ref name <b>{refAgent.id}</b> used by{" "}
</span>
<Link
className="text-accent-foreground underline"
to={route}
>
{refAgent.name}
</Link>
</TypographySmall>
</div>
);
}

if (!agent.refNameAssigned) return <div />;

return (
<CopyText
className="h-8 text-muted-foreground text-sm bg-background flex-row-reverse"
holdStatusDelay={10000}
text={`${window.location.protocol}//${window.location.host}/${agent.refName}`}
/>
);
}
}
21 changes: 9 additions & 12 deletions ui/admin/app/components/agent/Publish.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Eye } from "lucide-react";
import { useState } from "react";

import { Agent } from "~/lib/model/agents";

import {
TypographyMuted,
TypographyMutedAccent,
Expand All @@ -20,19 +18,18 @@ import { Input } from "~/components/ui/input";

type PublishProps = {
className?: string;
agent: Agent;
onChange: (agent: Agent) => void;
refName: string;
onPublish: (refName: string) => void;
};

export function Publish({ className, agent, onChange }: PublishProps) {
const [refName, setRefName] = useState(agent.refName);
export function Publish({
className,
refName: _refName,
onPublish,
}: PublishProps) {
const [refName, setRefName] = useState(_refName);

const handlePublish = () => {
onChange({
...agent,
refName,
});
};
const handlePublish = () => onPublish(refName);

return (
<Dialog>
Expand Down
2 changes: 1 addition & 1 deletion ui/admin/app/components/agent/ToolForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export function ToolForm({
onDelete={() => removeTools([field.tool])}
actions={
<Tooltip>
<TooltipTrigger>
<TooltipTrigger asChild>
<Switch
checked={
field.variant ===
Expand Down
18 changes: 4 additions & 14 deletions ui/admin/app/components/agent/Unpublish.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,19 @@
import { EyeOff } from "lucide-react";

import { Agent } from "~/lib/model/agents";

import { useAgent } from "~/components/agent/AgentContext";
import { ConfirmationDialog } from "~/components/composed/ConfirmationDialog";
import { Button } from "~/components/ui/button";

type PublishProps = {
type UnpublishProps = {
className?: string;
onChange: (agent: Agent) => void;
onUnpublish: () => void;
};

export function Unpublish({ onChange }: PublishProps) {
const { agent } = useAgent();

export function Unpublish({ onUnpublish }: UnpublishProps) {
return (
<ConfirmationDialog
title="Unpublish Agent"
description="Are you sure you want to unpublish this agent? This action will disrupt every user currently using this reference."
onConfirm={() => {
onChange({
...agent,
refName: "",
});
}}
onConfirm={() => onUnpublish()}
confirmProps={{
variant: "destructive",
children: "Unpublish",
Expand Down
10 changes: 8 additions & 2 deletions ui/admin/app/lib/model/agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type AgentBase = {
temperature?: number;
cache?: boolean;
refName: string;
refNameAssigned?: boolean;
prompt: string;
agents?: string[];
workflows?: string[];
Expand All @@ -28,10 +29,15 @@ export type AgentOAuthStatus = {

export type Agent = EntityMeta &
AgentBase & {
slugAssigned: boolean;
} & {
authStatus?: Record<string, AgentOAuthStatus>;
};

export type CreateAgent = AgentBase;
export type UpdateAgent = AgentBase;

export type AgentIcons = {
icon: string;
iconDark: string;
collapsed: string;
collapsedDark: string;
};
10 changes: 10 additions & 0 deletions ui/admin/app/lib/model/assistants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { AgentIcons } from "~/lib/model/agents";
import { EntityMeta } from "~/lib/model/primitives";

export type Assistant = EntityMeta & {
name: string;
entityID: string;
description: string;
icons: AgentIcons;
type: "agent" | "workflow";
};
30 changes: 30 additions & 0 deletions ui/admin/app/lib/routers/apiRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,36 @@ const buildUrl = (path: string, params?: object) => {
};

export const ApiRoutes = {
assistants: {
base: () => buildUrl("/assistants"),
getAssistants: () => buildUrl("/assistants"),
getCredentials: (assistantId: string) =>
buildUrl(`/assistants/${assistantId}/credentials`),
deleteCredential: (assistantId: string, credentialId: string) =>
buildUrl(`/assistants/${assistantId}/credentials/${credentialId}`),
getEvents: (assistantId: string) =>
buildUrl(`/assistants/${assistantId}/events`),
invoke: (assistantId: string) =>
buildUrl(`/assistants/${assistantId}/invoke`),
getTools: (assistantId: string) =>
buildUrl(`/assistants/${assistantId}/tools`),
deleteTool: (assistantId: string, toolId: string) =>
buildUrl(`/assistants/${assistantId}/tools/${toolId}`),
getFiles: (assistantId: string) =>
buildUrl(`/assistants/${assistantId}/files`),
getFileById: (assistantId: string, fileId: string) =>
buildUrl(`/assistants/${assistantId}/files/${fileId}`),
uploadFile: (assistantId: string) =>
buildUrl(`/assistants/${assistantId}/files`),
deleteFile: (assistantId: string, fileId: string) =>
buildUrl(`/assistants/${assistantId}/files/${fileId}`),
getKnowledge: (assistantId: string) =>
buildUrl(`/assistants/${assistantId}/knowledge`),
addKnowledge: (assistantId: string, fileName: string) =>
buildUrl(`/assistants/${assistantId}/knowledge/${fileName}`),
deleteKnowledge: (assistantId: string, fileName: string) =>
buildUrl(`/assistants/${assistantId}/knowledge/${fileName}`),
},
agents: {
base: () => buildUrl("/agents"),
getById: (agentId: string) => buildUrl(`/agents/${agentId}`),
Expand Down
16 changes: 16 additions & 0 deletions ui/admin/app/lib/service/api/assistantApiService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Assistant } from "~/lib/model/assistants";
import { ApiRoutes } from "~/lib/routers/apiRoutes";
import { request } from "~/lib/service/api/primitives";

async function getAssistants() {
const { data } = await request<{ items: Assistant[] }>({
url: ApiRoutes.assistants.getAssistants().url,
});

return data.items ?? [];
}
getAssistants.key = () => ({ url: ApiRoutes.assistants.getAssistants().path });

export const AssistantApiService = {
getAssistants,
};
6 changes: 5 additions & 1 deletion ui/admin/app/routes/_auth.agents.$agent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ export default function ChatAgent() {
className="flex-auto"
>
<ResizablePanel className="">
<Agent agent={agent} onRefresh={updateThreadId} />
<Agent
agent={agent}
onRefresh={updateThreadId}
key={agent.id}
/>
</ResizablePanel>
<ResizableHandle withHandle />
<ResizablePanel>
Expand Down