mirror of https://github.com/kortix-ai/suna.git
Merge pull request #612 from escapade-mckv/tool-views-fix
fix(bug): fix double json dumps in response processor
This commit is contained in:
commit
35ef852ec4
|
@ -1747,7 +1747,7 @@ class ResponseProcessor:
|
||||||
return summary
|
return summary
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return json.dumps(structured_result_v1)
|
return structured_result_v1
|
||||||
|
|
||||||
def _format_xml_tool_result(self, tool_call: Dict[str, Any], result: ToolResult) -> str:
|
def _format_xml_tool_result(self, tool_call: Dict[str, Any], result: ToolResult) -> str:
|
||||||
"""Format a tool result wrapped in a <tool_result> tag.
|
"""Format a tool result wrapped in a <tool_result> tag.
|
||||||
|
|
|
@ -637,4 +637,20 @@
|
||||||
animation: var(--animate-shimmer);
|
animation: var(--animate-shimmer);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--animate-shiny-text: shiny-text 5s infinite;
|
||||||
|
|
||||||
|
@keyframes shiny-text {
|
||||||
|
0%,
|
||||||
|
90%,
|
||||||
|
100% {
|
||||||
|
background-position: calc(-100% - var(--shiny-width)) 0;
|
||||||
|
}
|
||||||
|
30%,
|
||||||
|
60% {
|
||||||
|
background-position: calc(100% + var(--shiny-width)) 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -421,7 +421,7 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
||||||
groupedMessages.push(currentGroup);
|
groupedMessages.push(currentGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle streaming content
|
// Handle streaming content - only add to existing group or create new one if needed
|
||||||
if (streamingTextContent) {
|
if (streamingTextContent) {
|
||||||
const lastGroup = groupedMessages.at(-1);
|
const lastGroup = groupedMessages.at(-1);
|
||||||
if (!lastGroup || lastGroup.type === 'user') {
|
if (!lastGroup || lastGroup.type === 'user') {
|
||||||
|
@ -443,7 +443,6 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
||||||
key: `assistant-group-${assistantGroupCounter}-streaming`
|
key: `assistant-group-${assistantGroupCounter}-streaming`
|
||||||
});
|
});
|
||||||
} else if (lastGroup.type === 'assistant_group') {
|
} else if (lastGroup.type === 'assistant_group') {
|
||||||
// Add to existing assistant group
|
|
||||||
lastGroup.messages.push({
|
lastGroup.messages.push({
|
||||||
content: streamingTextContent,
|
content: streamingTextContent,
|
||||||
type: 'assistant',
|
type: 'assistant',
|
||||||
|
@ -513,7 +512,7 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
||||||
return (
|
return (
|
||||||
<div key={group.key} ref={groupIndex === groupedMessages.length - 1 ? latestMessageRef : null}>
|
<div key={group.key} ref={groupIndex === groupedMessages.length - 1 ? latestMessageRef : null}>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{/* Logo positioned above the message content */}
|
{/* Logo positioned above the message content - ONLY ONCE PER GROUP */}
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="rounded-md flex items-center justify-center">
|
<div className="rounded-md flex items-center justify-center">
|
||||||
{agentAvatar}
|
{agentAvatar}
|
||||||
|
@ -521,8 +520,8 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
||||||
<p className='ml-2 text-sm text-muted-foreground'>{agentName ? agentName : 'Suna'}</p>
|
<p className='ml-2 text-sm text-muted-foreground'>{agentName ? agentName : 'Suna'}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Message content */}
|
{/* Message content - ALL messages in the group */}
|
||||||
<div className="flex max-w-[90%] rounded-lg text-sm break-words overflow-hidden">
|
<div className="flex max-w-[90%] rounded-lg text-sm break-words overflow-hidden">
|
||||||
<div className="space-y-2 min-w-0 flex-1">
|
<div className="space-y-2 min-w-0 flex-1">
|
||||||
{(() => {
|
{(() => {
|
||||||
// In debug mode, just show raw messages content
|
// In debug mode, just show raw messages content
|
||||||
|
@ -566,13 +565,13 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
||||||
|
|
||||||
const renderedToolResultIds = new Set<string>();
|
const renderedToolResultIds = new Set<string>();
|
||||||
const elements: React.ReactNode[] = [];
|
const elements: React.ReactNode[] = [];
|
||||||
let assistantMessageCount = 0; // Track assistant messages for spacing
|
|
||||||
|
|
||||||
group.messages.forEach((message, msgIndex) => {
|
group.messages.forEach((message, msgIndex) => {
|
||||||
if (message.type === 'assistant') {
|
if (message.type === 'assistant') {
|
||||||
const parsedContent = safeJsonParse<ParsedContent>(message.content, {});
|
const parsedContent = safeJsonParse<ParsedContent>(message.content, {});
|
||||||
const msgKey = message.message_id || `submsg-assistant-${msgIndex}`;
|
const msgKey = message.message_id || `submsg-assistant-${msgIndex}`;
|
||||||
|
let assistantMessageCount = 0;
|
||||||
|
|
||||||
if (!parsedContent.content) return;
|
if (!parsedContent.content) return;
|
||||||
|
|
||||||
const renderedContent = renderMarkdownContent(
|
const renderedContent = renderMarkdownContent(
|
||||||
|
@ -586,13 +585,12 @@ export const ThreadContent: React.FC<ThreadContentProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
elements.push(
|
elements.push(
|
||||||
<div key={msgKey} className={assistantMessageCount > 0 ? "mt-2" : ""}>
|
<div key={msgKey} className={assistantMessageCount > 0 ? "mt-4" : ""}>
|
||||||
<div className="prose prose-sm dark:prose-invert chat-markdown max-w-none [&>:first-child]:mt-0 prose-headings:mt-3 break-words overflow-hidden">
|
<div className="prose prose-sm dark:prose-invert chat-markdown max-w-none [&>:first-child]:mt-0 prose-headings:mt-3 break-words overflow-hidden">
|
||||||
{renderedContent}
|
{renderedContent}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
assistantMessageCount++;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
import { AnimatedShinyText } from '@/components/ui/animated-shiny-text';
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{ id: 1, content: "Initializing neural pathways..." },
|
{ id: 1, content: "Initializing neural pathways..." },
|
||||||
|
@ -33,10 +34,8 @@ export const AgentLoader = () => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 space-y-2 w-full h-16 bg-background">
|
<div className="flex py-2 items-center w-full">
|
||||||
<div className="max-w-[90%] animate-shimmer bg-transparent h-full p-0.5 text-sm border rounded-xl shadow-sm relative overflow-hidden">
|
<div>✨</div>
|
||||||
<div className="rounded-md bg-background flex px-5 items-start justify-start h-full relative z-10">
|
|
||||||
<div className="flex flex-col py-5 items-start w-full space-y-3">
|
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
<motion.div
|
<motion.div
|
||||||
key={items[index].id}
|
key={items[index].id}
|
||||||
|
@ -45,13 +44,11 @@ export const AgentLoader = () => {
|
||||||
exit={{ y: -20, opacity: 0, filter: "blur(8px)" }}
|
exit={{ y: -20, opacity: 0, filter: "blur(8px)" }}
|
||||||
transition={{ ease: "easeInOut" }}
|
transition={{ ease: "easeInOut" }}
|
||||||
style={{ position: "absolute" }}
|
style={{ position: "absolute" }}
|
||||||
|
className='ml-7'
|
||||||
>
|
>
|
||||||
{items[index].content}
|
<AnimatedShinyText>{items[index].content}</AnimatedShinyText>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { ComponentPropsWithoutRef, CSSProperties, FC } from "react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
export interface AnimatedShinyTextProps
|
||||||
|
extends ComponentPropsWithoutRef<"span"> {
|
||||||
|
shimmerWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AnimatedShinyText: FC<AnimatedShinyTextProps> = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
shimmerWidth = 100,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--shiny-width": `${shimmerWidth}px`,
|
||||||
|
} as CSSProperties
|
||||||
|
}
|
||||||
|
className={cn(
|
||||||
|
"mx-auto max-w-md text-neutral-600/70 dark:text-neutral-400/70",
|
||||||
|
"animate-shiny-text bg-clip-text bg-no-repeat [background-position:0_0] [background-size:var(--shiny-width)_100%] [transition:background-position_1s_cubic-bezier(.6,.6,0,1)_infinite]",
|
||||||
|
"bg-gradient-to-r from-transparent via-black/80 via-50% to-transparent dark:via-white/80",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue