Files
agent-chat-ui/src/components/thread/messages/ai.tsx

109 lines
3.3 KiB
TypeScript
Raw Normal View History

import { useStreamContext } from "@/providers/Stream";
2025-03-03 13:24:24 -08:00
import { Checkpoint, Message } from "@langchain/langgraph-sdk";
import { getContentString } from "../utils";
import { BranchSwitcher, CommandBar } from "./shared";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { MarkdownText } from "../markdown-text";
2025-03-03 12:40:24 -08:00
import { LoadExternalComponent } from "@langchain/langgraph-sdk/react-ui/client";
2025-03-04 14:37:39 +01:00
import { cn } from "@/lib/utils";
2025-03-03 12:40:24 -08:00
function CustomComponent({
message,
thread,
}: {
message: Message;
thread: ReturnType<typeof useStreamContext>;
}) {
const meta = thread.getMessagesMetadata(message);
const seenState = meta?.firstSeenState;
const customComponent = seenState?.values.ui
2025-03-04 19:29:31 +01:00
?.slice()
2025-03-03 12:40:24 -08:00
.reverse()
.find(
({ additional_kwargs }) =>
additional_kwargs.run_id === seenState.metadata?.run_id,
);
if (!customComponent) {
return null;
}
return (
<div key={message.id}>
{customComponent && (
<LoadExternalComponent
assistantId="agent"
stream={thread}
message={customComponent}
/>
)}
</div>
);
}
export function AssistantMessage({
message,
isLoading,
2025-03-03 13:24:24 -08:00
handleRegenerate,
}: {
message: Message;
isLoading: boolean;
2025-03-03 13:24:24 -08:00
handleRegenerate: (parentCheckpoint: Checkpoint | null | undefined) => void;
}) {
2025-03-03 17:53:00 -08:00
const contentString = getContentString(message.content);
const thread = useStreamContext();
const meta = thread.getMessagesMetadata(message);
const parentCheckpoint = meta?.firstSeenState?.parent_checkpoint;
return (
<div className="flex items-start mr-auto gap-2 group">
<Avatar>
<AvatarFallback>A</AvatarFallback>
</Avatar>
<div className="flex flex-col gap-2">
2025-03-03 12:40:24 -08:00
<CustomComponent message={message} thread={thread} />
{contentString.length > 0 && (
<div className="rounded-2xl bg-muted px-4 py-2">
<MarkdownText>{contentString}</MarkdownText>
</div>
)}
2025-03-04 14:37:39 +01:00
<div
className={cn(
"flex gap-2 items-center mr-auto transition-opacity",
"opacity-0 group-focus-within:opacity-100 group-hover:opacity-100",
)}
>
<BranchSwitcher
branch={meta?.branch}
branchOptions={meta?.branchOptions}
onSelect={(branch) => thread.setBranch(branch)}
isLoading={isLoading}
/>
<CommandBar
content={contentString}
isLoading={isLoading}
isAiMessage={true}
2025-03-03 13:24:24 -08:00
handleRegenerate={() => handleRegenerate(parentCheckpoint)}
/>
</div>
</div>
</div>
);
}
export function AssistantMessageLoading() {
return (
<div className="flex items-start mr-auto gap-2">
<Avatar>
<AvatarFallback>A</AvatarFallback>
</Avatar>
<div className="flex items-center gap-1 rounded-2xl bg-muted px-4 py-2 h-8">
<div className="w-1.5 h-1.5 rounded-full bg-foreground/50 animate-[pulse_1.5s_ease-in-out_infinite]"></div>
<div className="w-1.5 h-1.5 rounded-full bg-foreground/50 animate-[pulse_1.5s_ease-in-out_0.5s_infinite]"></div>
<div className="w-1.5 h-1.5 rounded-full bg-foreground/50 animate-[pulse_1.5s_ease-in-out_1s_infinite]"></div>
</div>
</div>
);
}