mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-03 11:24:57 -05:00
Add Chat input
This commit is contained in:
@@ -26,81 +26,61 @@ export const ChatMessagesContainer = ({
|
||||
messages,
|
||||
status,
|
||||
error,
|
||||
handleSubmit,
|
||||
input,
|
||||
setInput,
|
||||
}: ChatMessagesContainerProps) => {
|
||||
return (
|
||||
<div className="flex h-full flex-1 flex-col">
|
||||
<Conversation className="flex-1">
|
||||
<ConversationContent>
|
||||
{messages.length === 0 ? (
|
||||
<ConversationEmptyState
|
||||
icon={<MessageSquareIcon className="size-12" />}
|
||||
title="Start a conversation"
|
||||
description="Type a message below to begin chatting"
|
||||
/>
|
||||
) : (
|
||||
messages.map((message) => (
|
||||
<Message from={message.role} key={message.id}>
|
||||
<MessageContent>
|
||||
{message.parts.map((part, i) => {
|
||||
switch (part.type) {
|
||||
case "text":
|
||||
return (
|
||||
<MessageResponse key={`${message.id}-${i}`}>
|
||||
{part.text}
|
||||
</MessageResponse>
|
||||
);
|
||||
case "tool-find_block":
|
||||
return (
|
||||
<FindBlocksTool
|
||||
key={`${message.id}-${i}`}
|
||||
part={part as ToolUIPart}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</MessageContent>
|
||||
</Message>
|
||||
))
|
||||
)}
|
||||
{status === "submitted" && (
|
||||
<Message from="assistant">
|
||||
<MessageContent>
|
||||
<p className="text-zinc-500">Thinking...</p>
|
||||
<Conversation className="flex-1">
|
||||
<ConversationContent>
|
||||
{messages.length === 0 ? (
|
||||
<ConversationEmptyState
|
||||
icon={<MessageSquareIcon className="size-12" />}
|
||||
title="Start a conversation"
|
||||
description="Type a message below to begin chatting"
|
||||
/>
|
||||
) : (
|
||||
messages.map((message) => (
|
||||
<Message from={message.role} key={message.id}>
|
||||
<MessageContent className={
|
||||
"border rounded-xl px-3 py-2 " +
|
||||
"group-[.is-user]:bg-purple-100 group-[.is-user]:rounded-2xl group-[.is-user]:border-purple-200 group-[.is-user]:text-slate-900 " +
|
||||
"group-[.is-assistant]:bg-slate-50/20 group-[.is-assistant]:border-none group-[.is-assistant]:text-slate-900"
|
||||
}>
|
||||
{message.parts.map((part, i) => {
|
||||
switch (part.type) {
|
||||
case "text":
|
||||
return (
|
||||
<MessageResponse key={`${message.id}-${i}`}>
|
||||
{part.text}
|
||||
</MessageResponse>
|
||||
);
|
||||
case "tool-find_block":
|
||||
return (
|
||||
<FindBlocksTool
|
||||
key={`${message.id}-${i}`}
|
||||
part={part as ToolUIPart}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</MessageContent>
|
||||
</Message>
|
||||
)}
|
||||
{error && (
|
||||
<div className="rounded-lg bg-red-50 p-3 text-red-600">
|
||||
Error: {error.message}
|
||||
</div>
|
||||
)}
|
||||
</ConversationContent>
|
||||
<ConversationScrollButton />
|
||||
</Conversation>
|
||||
|
||||
<form onSubmit={handleSubmit} className="border-t p-4">
|
||||
<div className="mx-auto flex max-w-2xl gap-2">
|
||||
<input
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
disabled={status !== "ready"}
|
||||
placeholder="Say something..."
|
||||
className="flex-1 rounded-md border border-zinc-300 px-4 py-2 focus:border-zinc-500 focus:outline-none"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={status !== "ready" || !input.trim()}
|
||||
className="rounded-md bg-zinc-900 px-4 py-2 text-white transition-colors hover:bg-zinc-800 disabled:opacity-50"
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
{status === "submitted" && (
|
||||
<Message from="assistant">
|
||||
<MessageContent>
|
||||
<p className="text-zinc-500">Thinking...</p>
|
||||
</MessageContent>
|
||||
</Message>
|
||||
)}
|
||||
{error && (
|
||||
<div className="rounded-lg bg-red-50 p-3 text-red-600">
|
||||
Error: {error.message}
|
||||
</div>
|
||||
)}
|
||||
</ConversationContent>
|
||||
<ConversationScrollButton />
|
||||
</Conversation>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ChatSidebar } from "./components/ChatSidebar/ChatSidebar";
|
||||
import { EmptySession } from "./components/EmptySession/EmptySession";
|
||||
import { ChatMessagesContainer } from "./components/ChatMessagesContainer/ChatMessagesContainer";
|
||||
import { postV2CreateSession } from "@/app/api/__generated__/endpoints/chat/chat";
|
||||
import { ChatInput } from "@/components/contextual/Chat/components/ChatInput/ChatInput";
|
||||
|
||||
export default function Page() {
|
||||
const [sessionId, setSessionId] = useQueryState("sessionId", parseAsString);
|
||||
@@ -62,22 +63,43 @@ export default function Page() {
|
||||
setInput("");
|
||||
}
|
||||
|
||||
function onSend(message: string) {
|
||||
sendMessage({ text: message });
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full">
|
||||
<ChatSidebar isCreating={isCreating} setIsCreating={setIsCreating} />
|
||||
|
||||
{sessionId ? (
|
||||
<ChatMessagesContainer
|
||||
messages={messages}
|
||||
status={status}
|
||||
error={error}
|
||||
handleSubmit={handleMessageSubmit}
|
||||
input={input}
|
||||
setInput={setInput}
|
||||
<div className="mx-auto h-[calc(100vh-60px)] max-w-3xl pb-6">
|
||||
<div className="flex h-full flex-col">
|
||||
{sessionId ? (
|
||||
<ChatMessagesContainer
|
||||
messages={messages}
|
||||
status={status}
|
||||
error={error}
|
||||
handleSubmit={handleMessageSubmit}
|
||||
input={input}
|
||||
setInput={setInput}
|
||||
/>
|
||||
) : (
|
||||
<EmptySession
|
||||
isCreating={isCreating}
|
||||
onCreateSession={createSession}
|
||||
/>
|
||||
)}
|
||||
<div className="relative px-3 pt-2">
|
||||
<div className="pointer-events-none absolute top-[-18px] z-10 h-6 w-full bg-gradient-to-b from-transparent to-[#f8f8f9]" />
|
||||
<ChatInput
|
||||
onSend={onSend}
|
||||
disabled={status === "streaming" || !sessionId}
|
||||
isStreaming={status === "streaming"}
|
||||
onStop={() => {}}
|
||||
placeholder="You can search or just ask"
|
||||
/>
|
||||
) : (
|
||||
<EmptySession isCreating={isCreating} onCreateSession={createSession} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,57 +1,56 @@
|
||||
import { ToolUIPart } from "ai";
|
||||
import { FindBlockInput, FindBlockOutput, FindBlockToolPart } from "./FindBlocks";
|
||||
import { CheckCircleIcon, CircleNotchIcon, XCircleIcon } from "@phosphor-icons/react";
|
||||
import {
|
||||
FindBlockInput,
|
||||
FindBlockOutput,
|
||||
FindBlockToolPart,
|
||||
} from "./FindBlocks";
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
CircleNotchIcon,
|
||||
XCircleIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
|
||||
export const getAnimationText = (part: FindBlockToolPart): string => {
|
||||
switch (part.state) {
|
||||
case "input-streaming":
|
||||
return "Searching blocks for you";
|
||||
|
||||
case "input-available": {
|
||||
const query = (part.input as FindBlockInput).query;
|
||||
return `Finding "${query}" blocks`;
|
||||
}
|
||||
|
||||
case "output-available": {
|
||||
const parsed = JSON.parse(part.output as string) as FindBlockOutput;
|
||||
if (parsed) {
|
||||
return `Found ${parsed.count} "${(part.input as FindBlockInput).query}" blocks`;
|
||||
}
|
||||
return "Found blocks";
|
||||
}
|
||||
|
||||
case "output-error":
|
||||
return "Error finding blocks";
|
||||
|
||||
default:
|
||||
return "Processing";
|
||||
switch (part.state) {
|
||||
case "input-streaming":
|
||||
return "Searching blocks for you";
|
||||
|
||||
case "input-available": {
|
||||
const query = (part.input as FindBlockInput).query;
|
||||
return `Finding "${query}" blocks`;
|
||||
}
|
||||
}
|
||||
|
||||
export const 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;
|
||||
|
||||
case "output-available": {
|
||||
const parsed = JSON.parse(part.output as string) as FindBlockOutput;
|
||||
if (parsed) {
|
||||
return `Found ${parsed.count} "${(part.input as FindBlockInput).query}" blocks`;
|
||||
}
|
||||
return "Found blocks";
|
||||
}
|
||||
|
||||
case "output-error":
|
||||
return "Error finding blocks";
|
||||
|
||||
default:
|
||||
return "Processing";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export const 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;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user