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: enhance chat functionality with new ChatHelpers component and optional tool deletion #857

Merged
2 changes: 1 addition & 1 deletion pkg/api/handlers/threads.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func (a *ThreadHandler) Update(req api.Context) error {
return err
}
for _, newTool := range newThread.Tools {
if !slices.Contains(agent.Spec.Manifest.AvailableThreadTools, newTool) {
if !slices.Contains(agent.Spec.Manifest.AvailableThreadTools, newTool) && !slices.Contains(agent.Spec.Manifest.DefaultThreadTools, newTool) {
return types.NewErrBadRequest("tool %s is not available for agent %s", newTool, agent.Name)
}
}
Expand Down
22 changes: 12 additions & 10 deletions ui/admin/app/components/agent/ToolEntry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function ToolEntry({
actions,
}: {
tool: string;
onDelete: () => void;
onDelete?: () => void;
actions?: React.ReactNode;
}) {
const { data: toolReference, isLoading } = useSWR(
Expand All @@ -26,7 +26,7 @@ export function ToolEntry({

return (
<div className="flex items-center space-x-2 justify-between mt-1">
<div className="text-sm px-3 shadow-sm rounded-md p-2 w-full flex items-center justify-between gap-2">
<div className="text-sm px-3 rounded-md p-2 w-full flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
{isLoading ? (
<LoadingSpinner className="w-5 h-5" />
Expand All @@ -44,14 +44,16 @@ export function ToolEntry({
<div className="flex items-center gap-2">
{actions}

<Button
type="button"
variant="ghost"
size="icon"
onClick={() => onDelete()}
>
<TrashIcon className="w-5 h-5" />
</Button>
{onDelete && (
<Button
type="button"
variant="ghost"
size="icon"
onClick={() => onDelete()}
>
<TrashIcon className="w-5 h-5" />
</Button>
)}
</div>
</div>
</div>
Expand Down
32 changes: 17 additions & 15 deletions ui/admin/app/components/agent/ToolForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,21 +166,23 @@ export function ToolForm({
onDelete={() => removeTools([field.tool])}
actions={
<Tooltip>
<TooltipTrigger>
<Switch
checked={
field.variant ===
ToolVariant.DEFAULT
}
onCheckedChange={(checked) =>
updateVariant(
field.tool,
checked
? ToolVariant.DEFAULT
: ToolVariant.AVAILABLE
)
}
/>
<TooltipTrigger asChild>
<div>
<Switch
checked={
field.variant ===
ToolVariant.DEFAULT
}
onCheckedChange={(checked) =>
updateVariant(
field.tool,
checked
? ToolVariant.DEFAULT
: ToolVariant.AVAILABLE
)
}
/>
</div>
</TooltipTrigger>

<TooltipContent>
Expand Down
6 changes: 2 additions & 4 deletions ui/admin/app/components/chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function Chat({ className }: ChatProps) {
const showStartButtonPane = mode === "workflow" && !readOnly;

return (
<div className={`flex flex-col h-full ${className}`}>
<div className={`flex flex-col h-full pb-5 ${className}`}>
{showMessagePane && (
<div className="flex-grow overflow-hidden">
<MessagePane
Expand All @@ -55,9 +55,7 @@ export function Chat({ className }: ChatProps) {
setRunTriggered(true);
invoke(params && JSON.stringify(params));
}}
className={cn({
"w-full": threadId,
})}
className={cn({ "w-full": threadId })}
popoverContentProps={{
className: cn({ "translate-y-[-50%]": !threadId }),
}}
Expand Down
30 changes: 30 additions & 0 deletions ui/admin/app/components/chat/ChatActions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { cn } from "~/lib/utils";

import { useChat } from "~/components/chat/ChatContext";
import { ToolsInfo } from "~/components/chat/chat-actions/ToolsInfo";
import {
useOptimisticThread,
useThreadAgents as useThreadAgent,
} from "~/components/chat/thread-helpers";

export function ChatActions({ className }: { className?: string }) {
const { threadId } = useChat();

const { data: agent } = useThreadAgent(threadId);
const { thread, updateThread } = useOptimisticThread(threadId);

const tools = thread?.tools;

return (
<div className={cn("w-full flex items-center", className)}>
<div className="flex items-center gap-2">
<ToolsInfo
tools={tools ?? []}
onChange={(tools) => updateThread({ tools })}
agent={agent}
disabled={!thread}
/>
</div>
</div>
);
}
34 changes: 23 additions & 11 deletions ui/admin/app/components/chat/Chatbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useState } from "react";

import { cn } from "~/lib/utils";

import { ChatActions } from "~/components/chat/ChatActions";
import { useChat } from "~/components/chat/ChatContext";
import { LoadingSpinner } from "~/components/ui/LoadingSpinner";
import { Button } from "~/components/ui/button";
Expand Down Expand Up @@ -30,11 +31,12 @@ export function Chatbar({ className }: ChatbarProps) {
return (
<form
onSubmit={handleSubmit}
className={cn("flex items-end gap-2 pb-10", className)}
className={cn("flex items-end gap-2", className)}
>
<div className="relative flex-grow">
<AutosizeTextarea
className="resize-none rounded-xl h-[2.5rem] line-height-[1.25rem] min-h-[2.5rem] bg-background"
className="rounded-3xl p-2"
variant="flat"
value={input}
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
Expand All @@ -46,17 +48,27 @@ export function Chatbar({ className }: ChatbarProps) {
minHeight={0}
onChange={(e) => setInput(e.target.value)}
placeholder="Type your message..."
bottomContent={
<div className="flex flex-row-reverse items-center justify-between">
<Button
size="icon-sm"
className="m-2"
color="primary"
type="submit"
disabled={!input || isRunning || isInvoking}
>
{isInvoking ? (
<LoadingSpinner />
) : (
<ArrowUpIcon />
)}
</Button>

<ChatActions className="p-2" />
</div>
}
/>
</div>

<Button
size="icon"
className="rounded-full"
type="submit"
disabled={!input || isRunning || isInvoking}
>
{isInvoking ? <LoadingSpinner /> : <ArrowUpIcon />}
</Button>
</form>
);
}
28 changes: 28 additions & 0 deletions ui/admin/app/components/chat/chat-actions/FilesInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { PaperclipIcon } from "lucide-react";

import { Button } from "~/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "~/components/ui/tooltip";

export function FilesInfo() {
return (
<Tooltip>
<TooltipContent>Files (Coming Soon)</TooltipContent>

<TooltipTrigger asChild>
<div>
<Button
size="icon-sm"
variant="outline"
className="gap-2"
startContent={<PaperclipIcon />}
disabled
/>
</div>
</TooltipTrigger>
</Tooltip>
);
}
63 changes: 63 additions & 0 deletions ui/admin/app/components/chat/chat-actions/KnowledgeInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { LibraryIcon } from "lucide-react";

import { KnowledgeFile } from "~/lib/model/knowledge";
import { cn } from "~/lib/utils";

import { TypographyMuted } from "~/components/Typography";
import { Button } from "~/components/ui/button";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "~/components/ui/popover";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "~/components/ui/tooltip";

export function KnowledgeInfo({
knowledge,
className,
disabled,
}: {
knowledge: KnowledgeFile[];
className?: string;
disabled?: boolean;
}) {
return (
<Tooltip>
<TooltipContent>Knowledge</TooltipContent>

<Popover>
<TooltipTrigger asChild>
<PopoverTrigger asChild>
<Button
size="icon-sm"
variant="outline"
className={cn("gap-2", className)}
startContent={<LibraryIcon />}
disabled={disabled}
/>
</PopoverTrigger>
</TooltipTrigger>

<PopoverContent>
{knowledge.length > 0 ? (
<div className="space-y-2">
{knowledge.map((file) => (
<TypographyMuted key={file.id}>
{file.fileName}
</TypographyMuted>
))}
</div>
) : (
<TypographyMuted>
No knowledge available
</TypographyMuted>
)}
</PopoverContent>
</Popover>
</Tooltip>
);
}
28 changes: 28 additions & 0 deletions ui/admin/app/components/chat/chat-actions/TablesInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { TableIcon } from "lucide-react";

import { Button } from "~/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "~/components/ui/tooltip";

export function TablesInfo() {
return (
<Tooltip>
<TooltipContent>Tables (Coming Soon)</TooltipContent>

<TooltipTrigger asChild>
<div>
<Button
size="icon-sm"
variant="outline"
className="gap-2"
startContent={<TableIcon />}
disabled
/>
</div>
</TooltipTrigger>
</Tooltip>
);
}
Loading