mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-19 20:18:22 -05:00
chore: refactors
This commit is contained in:
@@ -73,9 +73,7 @@ export function CopilotShell({ children }: CopilotShellProps) {
|
||||
onClick={() => handleSelectSession(session.id)}
|
||||
className={cn(
|
||||
"w-full rounded-lg px-3 py-2.5 text-left transition-colors",
|
||||
isActive
|
||||
? "bg-zinc-100"
|
||||
: "hover:bg-zinc-50",
|
||||
isActive ? "bg-zinc-100" : "hover:bg-zinc-50",
|
||||
)}
|
||||
>
|
||||
<Text
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { postV2CreateSession, useGetV2GetSession, useGetV2ListSessions } from "@/app/api/__generated__/endpoints/chat/chat";
|
||||
import {
|
||||
postV2CreateSession,
|
||||
useGetV2GetSession,
|
||||
useGetV2ListSessions,
|
||||
} from "@/app/api/__generated__/endpoints/chat/chat";
|
||||
import type { SessionDetailResponse } from "@/app/api/__generated__/models/sessionDetailResponse";
|
||||
import type { SessionSummaryResponse } from "@/app/api/__generated__/models/sessionSummaryResponse";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
@@ -30,7 +34,9 @@ export function useCopilotShell() {
|
||||
breakpoint === "base" || breakpoint === "sm" || breakpoint === "md";
|
||||
|
||||
const [offset, setOffset] = useState(0);
|
||||
const [accumulatedSessions, setAccumulatedSessions] = useState<SessionSummaryResponse[]>([]);
|
||||
const [accumulatedSessions, setAccumulatedSessions] = useState<
|
||||
SessionSummaryResponse[]
|
||||
>([]);
|
||||
const [totalCount, setTotalCount] = useState<number | null>(null);
|
||||
const [hasAutoSelectedSession, setHasAutoSelectedSession] = useState(false);
|
||||
const hasCreatedSessionRef = useRef(false);
|
||||
@@ -51,7 +57,7 @@ export function useCopilotShell() {
|
||||
const newSessions = responseData.sessions;
|
||||
const total = responseData.total;
|
||||
setTotalCount(total);
|
||||
|
||||
|
||||
if (offset === 0) {
|
||||
setAccumulatedSessions(newSessions);
|
||||
} else {
|
||||
@@ -67,7 +73,9 @@ export function useCopilotShell() {
|
||||
|
||||
const areAllSessionsLoaded = useMemo(() => {
|
||||
if (totalCount === null) return false;
|
||||
return accumulatedSessions.length >= totalCount && !isFetching && !isLoading;
|
||||
return (
|
||||
accumulatedSessions.length >= totalCount && !isFetching && !isLoading
|
||||
);
|
||||
}, [accumulatedSessions.length, totalCount, isFetching, isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -105,33 +113,35 @@ export function useCopilotShell() {
|
||||
[searchParams],
|
||||
);
|
||||
|
||||
const { data: currentSessionData, isLoading: isCurrentSessionLoading } = useGetV2GetSession(
|
||||
currentSessionId || "",
|
||||
{
|
||||
const { data: currentSessionData, isLoading: isCurrentSessionLoading } =
|
||||
useGetV2GetSession(currentSessionId || "", {
|
||||
query: {
|
||||
enabled: !!currentSessionId && (!isMobile || isDrawerOpen),
|
||||
select: okData,
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const sessions = useMemo(
|
||||
function getSessions() {
|
||||
const filteredSessions: SessionSummaryResponse[] = [];
|
||||
|
||||
|
||||
if (accumulatedSessions.length > 0) {
|
||||
const visibleSessions = filterVisibleSessions(accumulatedSessions);
|
||||
|
||||
|
||||
if (currentSessionId) {
|
||||
const currentInAll = accumulatedSessions.find((s) => s.id === currentSessionId);
|
||||
const currentInAll = accumulatedSessions.find(
|
||||
(s) => s.id === currentSessionId,
|
||||
);
|
||||
if (currentInAll) {
|
||||
const isInVisible = visibleSessions.some((s) => s.id === currentSessionId);
|
||||
const isInVisible = visibleSessions.some(
|
||||
(s) => s.id === currentSessionId,
|
||||
);
|
||||
if (!isInVisible) {
|
||||
filteredSessions.push(currentInAll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
filteredSessions.push(...visibleSessions);
|
||||
}
|
||||
|
||||
@@ -140,7 +150,8 @@ export function useCopilotShell() {
|
||||
(s) => s.id === currentSessionId,
|
||||
);
|
||||
if (!isCurrentInList) {
|
||||
const summarySession = convertSessionDetailToSummary(currentSessionData);
|
||||
const summarySession =
|
||||
convertSessionDetailToSummary(currentSessionData);
|
||||
// Add new session at the beginning to match API order (most recent first)
|
||||
filteredSessions.unshift(summarySession);
|
||||
}
|
||||
@@ -189,9 +200,9 @@ export function useCopilotShell() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!areAllSessionsLoaded || hasAutoSelectedSession) return;
|
||||
|
||||
|
||||
const visibleSessions = filterVisibleSessions(accumulatedSessions);
|
||||
|
||||
|
||||
if (paramSessionId) {
|
||||
setHasAutoSelectedSession(true);
|
||||
return;
|
||||
@@ -201,7 +212,12 @@ export function useCopilotShell() {
|
||||
const lastSession = visibleSessions[0];
|
||||
setHasAutoSelectedSession(true);
|
||||
router.push(`/copilot/chat?sessionId=${lastSession.id}`);
|
||||
} else if (accumulatedSessions.length === 0 && !isLoading && totalCount === 0 && !hasCreatedSessionRef.current) {
|
||||
} else if (
|
||||
accumulatedSessions.length === 0 &&
|
||||
!isLoading &&
|
||||
totalCount === 0 &&
|
||||
!hasCreatedSessionRef.current
|
||||
) {
|
||||
hasCreatedSessionRef.current = true;
|
||||
postV2CreateSession({ body: JSON.stringify({}) })
|
||||
.then((response) => {
|
||||
@@ -216,7 +232,15 @@ export function useCopilotShell() {
|
||||
} else if (totalCount === 0) {
|
||||
setHasAutoSelectedSession(true);
|
||||
}
|
||||
}, [areAllSessionsLoaded, accumulatedSessions, paramSessionId, hasAutoSelectedSession, router, isLoading, totalCount]);
|
||||
}, [
|
||||
areAllSessionsLoaded,
|
||||
accumulatedSessions,
|
||||
paramSessionId,
|
||||
hasAutoSelectedSession,
|
||||
router,
|
||||
isLoading,
|
||||
totalCount,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (paramSessionId) {
|
||||
@@ -228,13 +252,24 @@ export function useCopilotShell() {
|
||||
if (!areAllSessionsLoaded) return false;
|
||||
|
||||
if (paramSessionId) {
|
||||
const sessionFound = accumulatedSessions.some((s) => s.id === paramSessionId);
|
||||
const sessionFound = accumulatedSessions.some(
|
||||
(s) => s.id === paramSessionId,
|
||||
);
|
||||
const sessionLoading = isCurrentSessionLoading;
|
||||
return sessionFound || (!sessionLoading && currentSessionData !== undefined);
|
||||
return (
|
||||
sessionFound || (!sessionLoading && currentSessionData !== undefined)
|
||||
);
|
||||
}
|
||||
|
||||
return hasAutoSelectedSession;
|
||||
}, [areAllSessionsLoaded, accumulatedSessions, paramSessionId, isCurrentSessionLoading, currentSessionData, hasAutoSelectedSession]);
|
||||
}, [
|
||||
areAllSessionsLoaded,
|
||||
accumulatedSessions,
|
||||
paramSessionId,
|
||||
isCurrentSessionLoading,
|
||||
currentSessionData,
|
||||
hasAutoSelectedSession,
|
||||
]);
|
||||
|
||||
return {
|
||||
isMobile,
|
||||
|
||||
@@ -50,7 +50,7 @@ export function Chat({
|
||||
<div className={cn("flex h-full flex-col", className)}>
|
||||
{/* Header */}
|
||||
{showHeader && (
|
||||
<header className="shrink-0 bg-[#f8f8f9] p-3">
|
||||
<header className="shrink-0 bg-[#f8f8f9] p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
{showSessionInfo && sessionId && (
|
||||
|
||||
@@ -8,12 +8,7 @@ export interface AIChatBubbleProps {
|
||||
|
||||
export function AIChatBubble({ children, className }: AIChatBubbleProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"text-left text-sm leading-relaxed",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className={cn("text-left text-sm leading-relaxed", className)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -50,24 +50,27 @@ export function ChatContainer({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn("flex h-full min-h-0 flex-col max-w-3xl mx-auto bg-[#f8f8f9]", className)}
|
||||
className={cn(
|
||||
"mx-auto flex h-full min-h-0 max-w-3xl flex-col bg-[#f8f8f9]",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{/* Messages or Welcome Screen - Scrollable */}
|
||||
<div className="flex min-h-0 flex-1 flex-col overflow-y-auto relative">
|
||||
<div className="relative flex min-h-0 flex-1 flex-col overflow-y-auto">
|
||||
<div className="flex min-h-full flex-col justify-end">
|
||||
<MessageList
|
||||
messages={messages}
|
||||
streamingChunks={streamingChunks}
|
||||
isStreaming={isStreaming}
|
||||
onSendMessage={sendMessageWithContext}
|
||||
className="flex-1"
|
||||
/>
|
||||
<MessageList
|
||||
messages={messages}
|
||||
streamingChunks={streamingChunks}
|
||||
isStreaming={isStreaming}
|
||||
onSendMessage={sendMessageWithContext}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Input - Fixed at bottom */}
|
||||
<div className="relative pb-4 pt-2">
|
||||
<div className="absolute w-full top-[-18px] h-6 pointer-events-none bg-gradient-to-b from-transparent to-[#f8f8f9] z-10" />
|
||||
<div className="pointer-events-none absolute top-[-18px] z-10 h-6 w-full bg-gradient-to-b from-transparent to-[#f8f8f9]" />
|
||||
<ChatInput
|
||||
onSend={sendMessageWithContext}
|
||||
disabled={isStreaming || !sessionId}
|
||||
|
||||
@@ -37,10 +37,11 @@ export function handleTextEnded(
|
||||
const lastMessage = prev[prev.length - 1];
|
||||
console.log("[Text Ended] Previous message:", {
|
||||
type: lastMessage?.type,
|
||||
toolName: lastMessage?.type === "tool_call" ? lastMessage.toolName : undefined,
|
||||
toolName:
|
||||
lastMessage?.type === "tool_call" ? lastMessage.toolName : undefined,
|
||||
content: completedText.substring(0, 200),
|
||||
});
|
||||
|
||||
|
||||
const assistantMessage: ChatMessageData = {
|
||||
type: "message",
|
||||
role: "assistant",
|
||||
|
||||
@@ -7,5 +7,7 @@
|
||||
animation: l1 1s infinite;
|
||||
}
|
||||
@keyframes l1 {
|
||||
100% {box-shadow: 0 0 0 30px #0000}
|
||||
100% {
|
||||
box-shadow: 0 0 0 30px #0000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,16 +245,15 @@ export function ChatMessage({
|
||||
) : (
|
||||
<AIChatBubble>
|
||||
<MarkdownContent content={message.content} />
|
||||
{agentOutput &&
|
||||
agentOutput.type === "tool_response" && (
|
||||
<div className="mt-4">
|
||||
<ToolResponseMessage
|
||||
toolId={agentOutput.toolId}
|
||||
toolName={agentOutput.toolName || "Agent Output"}
|
||||
result={agentOutput.result}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{agentOutput && agentOutput.type === "tool_response" && (
|
||||
<div className="mt-4">
|
||||
<ToolResponseMessage
|
||||
toolId={agentOutput.toolId}
|
||||
toolName={agentOutput.toolName || "Agent Output"}
|
||||
result={agentOutput.result}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</AIChatBubble>
|
||||
)}
|
||||
<div
|
||||
|
||||
@@ -39,9 +39,7 @@ export function MessageBubble({
|
||||
)}
|
||||
>
|
||||
{/* Gradient flare background */}
|
||||
<div
|
||||
className={cn("absolute inset-0 bg-gradient-to-br")}
|
||||
/>
|
||||
<div className={cn("absolute inset-0 bg-gradient-to-br")} />
|
||||
<div
|
||||
className={cn(
|
||||
"relative z-10 transition-all duration-500 ease-in-out",
|
||||
|
||||
@@ -40,7 +40,7 @@ export function MessageList({
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="mx-auto flex flex-col py-4 min-w-0 break-words hyphens-auto">
|
||||
<div className="mx-auto flex min-w-0 flex-col hyphens-auto break-words py-4">
|
||||
{/* Render all persisted messages */}
|
||||
{(() => {
|
||||
let lastAssistantMessageIndex = -1;
|
||||
@@ -61,165 +61,177 @@ export function MessageList({
|
||||
}
|
||||
}
|
||||
|
||||
return messages
|
||||
.map((message, index) => {
|
||||
// Log message for debugging
|
||||
if (message.type === "message" && message.role === "assistant") {
|
||||
const prevMessage = messages[index - 1];
|
||||
const prevMessageToolName = prevMessage?.type === "tool_call" ? prevMessage.toolName : undefined;
|
||||
console.log("[MessageList] Assistant message:", {
|
||||
index,
|
||||
content: message.content.substring(0, 200),
|
||||
fullContent: message.content,
|
||||
prevMessageType: prevMessage?.type,
|
||||
prevMessageToolName,
|
||||
});
|
||||
}
|
||||
|
||||
// Check if current message is an agent_output tool_response
|
||||
// and if previous message is an assistant message
|
||||
let agentOutput: ChatMessageData | undefined;
|
||||
let messageToRender: ChatMessageData = message;
|
||||
|
||||
if (message.type === "tool_response" && message.result) {
|
||||
let parsedResult: Record<string, unknown> | null = null;
|
||||
try {
|
||||
parsedResult =
|
||||
typeof message.result === "string"
|
||||
? JSON.parse(message.result)
|
||||
: (message.result as Record<string, unknown>);
|
||||
} catch {
|
||||
parsedResult = null;
|
||||
}
|
||||
if (parsedResult?.type === "agent_output") {
|
||||
return messages.map((message, index) => {
|
||||
// Log message for debugging
|
||||
if (message.type === "message" && message.role === "assistant") {
|
||||
const prevMessage = messages[index - 1];
|
||||
if (
|
||||
prevMessage &&
|
||||
prevMessage.type === "message" &&
|
||||
prevMessage.role === "assistant"
|
||||
) {
|
||||
// This agent output will be rendered inside the previous assistant message
|
||||
// Skip rendering this message separately
|
||||
return null;
|
||||
}
|
||||
const prevMessageToolName =
|
||||
prevMessage?.type === "tool_call"
|
||||
? prevMessage.toolName
|
||||
: undefined;
|
||||
console.log("[MessageList] Assistant message:", {
|
||||
index,
|
||||
content: message.content.substring(0, 200),
|
||||
fullContent: message.content,
|
||||
prevMessageType: prevMessage?.type,
|
||||
prevMessageToolName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check if assistant message follows a tool_call and looks like a tool output
|
||||
if (message.type === "message" && message.role === "assistant") {
|
||||
const prevMessage = messages[index - 1];
|
||||
|
||||
// Check if next message is an agent_output tool_response to include in current assistant message
|
||||
const nextMessage = messages[index + 1];
|
||||
if (
|
||||
nextMessage &&
|
||||
nextMessage.type === "tool_response" &&
|
||||
nextMessage.result
|
||||
) {
|
||||
// Check if current message is an agent_output tool_response
|
||||
// and if previous message is an assistant message
|
||||
let agentOutput: ChatMessageData | undefined;
|
||||
let messageToRender: ChatMessageData = message;
|
||||
|
||||
if (message.type === "tool_response" && message.result) {
|
||||
let parsedResult: Record<string, unknown> | null = null;
|
||||
try {
|
||||
parsedResult =
|
||||
typeof nextMessage.result === "string"
|
||||
? JSON.parse(nextMessage.result)
|
||||
: (nextMessage.result as Record<string, unknown>);
|
||||
typeof message.result === "string"
|
||||
? JSON.parse(message.result)
|
||||
: (message.result as Record<string, unknown>);
|
||||
} catch {
|
||||
parsedResult = null;
|
||||
}
|
||||
if (parsedResult?.type === "agent_output") {
|
||||
agentOutput = nextMessage;
|
||||
const prevMessage = messages[index - 1];
|
||||
if (
|
||||
prevMessage &&
|
||||
prevMessage.type === "message" &&
|
||||
prevMessage.role === "assistant"
|
||||
) {
|
||||
// This agent output will be rendered inside the previous assistant message
|
||||
// Skip rendering this message separately
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only convert to tool_response if it follows a tool_call AND looks like a tool output
|
||||
if (prevMessage && prevMessage.type === "tool_call") {
|
||||
const content = message.content.toLowerCase().trim();
|
||||
// Patterns that indicate this is a tool output result, not an agent response
|
||||
const isToolOutputPattern =
|
||||
content.startsWith("no agents found") ||
|
||||
content.startsWith("no results found") ||
|
||||
content.includes("no agents found matching") ||
|
||||
content.match(/^no \w+ found/i) ||
|
||||
(content.length < 150 && content.includes("try different")) ||
|
||||
(content.length < 200 && !content.includes("i'll") && !content.includes("let me") && !content.includes("i can") && !content.includes("i will"));
|
||||
// Check if assistant message follows a tool_call and looks like a tool output
|
||||
if (message.type === "message" && message.role === "assistant") {
|
||||
const prevMessage = messages[index - 1];
|
||||
|
||||
console.log("[MessageList] Checking if assistant message is tool output:", {
|
||||
content: message.content.substring(0, 100),
|
||||
isToolOutputPattern,
|
||||
prevToolName: prevMessage.toolName,
|
||||
});
|
||||
|
||||
if (isToolOutputPattern) {
|
||||
// Convert this message to a tool_response format for rendering
|
||||
messageToRender = {
|
||||
type: "tool_response",
|
||||
toolId: prevMessage.toolId,
|
||||
toolName: prevMessage.toolName,
|
||||
result: message.content,
|
||||
success: true,
|
||||
timestamp: message.timestamp,
|
||||
} as ChatMessageData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isFinalMessage =
|
||||
messageToRender.type !== "message" ||
|
||||
messageToRender.role !== "assistant" ||
|
||||
index === lastAssistantMessageIndex;
|
||||
|
||||
// Render last tool_response as AIChatBubble (but skip agent_output that's rendered inside assistant message)
|
||||
// Check if next message is an agent_output tool_response to include in current assistant message
|
||||
const nextMessage = messages[index + 1];
|
||||
if (
|
||||
messageToRender.type === "tool_response" &&
|
||||
message.type === "tool_response" &&
|
||||
index === lastToolResponseIndex
|
||||
nextMessage &&
|
||||
nextMessage.type === "tool_response" &&
|
||||
nextMessage.result
|
||||
) {
|
||||
// Check if this is an agent_output that should be rendered inside assistant message
|
||||
let parsedResult: Record<string, unknown> | null = null;
|
||||
try {
|
||||
parsedResult =
|
||||
typeof messageToRender.result === "string"
|
||||
? JSON.parse(messageToRender.result)
|
||||
: (messageToRender.result as Record<string, unknown>);
|
||||
} catch {
|
||||
parsedResult = null;
|
||||
}
|
||||
|
||||
const isAgentOutput = parsedResult?.type === "agent_output";
|
||||
const prevMessage = messages[index - 1];
|
||||
const shouldSkip =
|
||||
isAgentOutput &&
|
||||
prevMessage &&
|
||||
prevMessage.type === "message" &&
|
||||
prevMessage.role === "assistant";
|
||||
|
||||
if (shouldSkip) return null;
|
||||
|
||||
const resultValue =
|
||||
typeof messageToRender.result === "string"
|
||||
? messageToRender.result
|
||||
: messageToRender.result
|
||||
? JSON.stringify(messageToRender.result, null, 2)
|
||||
: "";
|
||||
|
||||
return (
|
||||
<div key={index} className="px-4 py-2 min-w-0 overflow-x-hidden break-words hyphens-auto">
|
||||
<AIChatBubble>
|
||||
<MarkdownContent content={resultValue} />
|
||||
</AIChatBubble>
|
||||
</div>
|
||||
);
|
||||
let parsedResult: Record<string, unknown> | null = null;
|
||||
try {
|
||||
parsedResult =
|
||||
typeof nextMessage.result === "string"
|
||||
? JSON.parse(nextMessage.result)
|
||||
: (nextMessage.result as Record<string, unknown>);
|
||||
} catch {
|
||||
parsedResult = null;
|
||||
}
|
||||
if (parsedResult?.type === "agent_output") {
|
||||
agentOutput = nextMessage;
|
||||
}
|
||||
}
|
||||
|
||||
// Only convert to tool_response if it follows a tool_call AND looks like a tool output
|
||||
if (prevMessage && prevMessage.type === "tool_call") {
|
||||
const content = message.content.toLowerCase().trim();
|
||||
// Patterns that indicate this is a tool output result, not an agent response
|
||||
const isToolOutputPattern =
|
||||
content.startsWith("no agents found") ||
|
||||
content.startsWith("no results found") ||
|
||||
content.includes("no agents found matching") ||
|
||||
content.match(/^no \w+ found/i) ||
|
||||
(content.length < 150 && content.includes("try different")) ||
|
||||
(content.length < 200 &&
|
||||
!content.includes("i'll") &&
|
||||
!content.includes("let me") &&
|
||||
!content.includes("i can") &&
|
||||
!content.includes("i will"));
|
||||
|
||||
console.log(
|
||||
"[MessageList] Checking if assistant message is tool output:",
|
||||
{
|
||||
content: message.content.substring(0, 100),
|
||||
isToolOutputPattern,
|
||||
prevToolName: prevMessage.toolName,
|
||||
},
|
||||
);
|
||||
|
||||
if (isToolOutputPattern) {
|
||||
// Convert this message to a tool_response format for rendering
|
||||
messageToRender = {
|
||||
type: "tool_response",
|
||||
toolId: prevMessage.toolId,
|
||||
toolName: prevMessage.toolName,
|
||||
result: message.content,
|
||||
success: true,
|
||||
timestamp: message.timestamp,
|
||||
} as ChatMessageData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isFinalMessage =
|
||||
messageToRender.type !== "message" ||
|
||||
messageToRender.role !== "assistant" ||
|
||||
index === lastAssistantMessageIndex;
|
||||
|
||||
// Render last tool_response as AIChatBubble (but skip agent_output that's rendered inside assistant message)
|
||||
if (
|
||||
messageToRender.type === "tool_response" &&
|
||||
message.type === "tool_response" &&
|
||||
index === lastToolResponseIndex
|
||||
) {
|
||||
// Check if this is an agent_output that should be rendered inside assistant message
|
||||
let parsedResult: Record<string, unknown> | null = null;
|
||||
try {
|
||||
parsedResult =
|
||||
typeof messageToRender.result === "string"
|
||||
? JSON.parse(messageToRender.result)
|
||||
: (messageToRender.result as Record<string, unknown>);
|
||||
} catch {
|
||||
parsedResult = null;
|
||||
}
|
||||
|
||||
const isAgentOutput = parsedResult?.type === "agent_output";
|
||||
const prevMessage = messages[index - 1];
|
||||
const shouldSkip =
|
||||
isAgentOutput &&
|
||||
prevMessage &&
|
||||
prevMessage.type === "message" &&
|
||||
prevMessage.role === "assistant";
|
||||
|
||||
if (shouldSkip) return null;
|
||||
|
||||
const resultValue =
|
||||
typeof messageToRender.result === "string"
|
||||
? messageToRender.result
|
||||
: messageToRender.result
|
||||
? JSON.stringify(messageToRender.result, null, 2)
|
||||
: "";
|
||||
|
||||
return (
|
||||
<ChatMessage
|
||||
<div
|
||||
key={index}
|
||||
message={messageToRender}
|
||||
onSendMessage={onSendMessage}
|
||||
agentOutput={agentOutput}
|
||||
isFinalMessage={isFinalMessage}
|
||||
/>
|
||||
className="min-w-0 overflow-x-hidden hyphens-auto break-words px-4 py-2"
|
||||
>
|
||||
<AIChatBubble>
|
||||
<MarkdownContent content={resultValue} />
|
||||
</AIChatBubble>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<ChatMessage
|
||||
key={index}
|
||||
message={messageToRender}
|
||||
onSendMessage={onSendMessage}
|
||||
agentOutput={agentOutput}
|
||||
isFinalMessage={isFinalMessage}
|
||||
/>
|
||||
);
|
||||
});
|
||||
})()}
|
||||
|
||||
{/* Render thinking message when streaming but no chunks yet */}
|
||||
|
||||
@@ -20,8 +20,8 @@ export function ToolCallMessage({
|
||||
const displayData = toolArguments
|
||||
? JSON.stringify(toolArguments)
|
||||
: "No arguments";
|
||||
|
||||
const displayText = `${displayKey}: ${displayData}`;
|
||||
|
||||
const displayText = `${displayKey}: ${displayData}`;
|
||||
|
||||
return (
|
||||
<AIChatBubble className={className}>
|
||||
|
||||
@@ -6,10 +6,7 @@ export interface UserChatBubbleProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function UserChatBubble({
|
||||
children,
|
||||
className,
|
||||
}: UserChatBubbleProps) {
|
||||
export function UserChatBubble({ children, className }: UserChatBubbleProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@@ -66,7 +66,6 @@ export function useChat({ urlSessionId }: UseChatArgs = {}) {
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading || isCreating) {
|
||||
const timer = setTimeout(() => {
|
||||
|
||||
@@ -75,17 +75,17 @@ export function NavbarLink({ name, href }: Props) {
|
||||
{href === "/library" &&
|
||||
(isChatEnabled ? (
|
||||
<ListChecksIcon
|
||||
className={cn(
|
||||
iconWidthClass,
|
||||
isActive && "text-white dark:text-black",
|
||||
)}
|
||||
/>
|
||||
className={cn(
|
||||
iconWidthClass,
|
||||
isActive && "text-white dark:text-black",
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<HouseIcon
|
||||
className={cn(
|
||||
iconWidthClass,
|
||||
isActive && "text-white dark:text-black",
|
||||
)}
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
<Text
|
||||
|
||||
Reference in New Issue
Block a user