mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-03 12:22:38 +02:00
Add fix ctas at message levelg
This commit is contained in:
parent
a3bf3046e3
commit
9d3eccbf87
7 changed files with 261 additions and 31 deletions
|
|
@ -163,3 +163,12 @@ body {
|
|||
.animate-float {
|
||||
animation: float 5s ease-in-out infinite, pulse-mascot 4s infinite;
|
||||
}
|
||||
|
||||
/* Feedback modal textarea overrides */
|
||||
.feedback-modal textarea,
|
||||
.feedback-modal textarea:focus {
|
||||
font-size: 0.75rem !important; /* Tailwind's text-xs */
|
||||
box-shadow: none !important;
|
||||
outline: none !important;
|
||||
border-color: #d1d5db !important; /* Tailwind's gray-300 */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export function App({
|
|||
isInitialState = false,
|
||||
onPanelClick,
|
||||
projectTools,
|
||||
triggerCopilotChat,
|
||||
}: {
|
||||
hidden?: boolean;
|
||||
projectId: string;
|
||||
|
|
@ -36,6 +37,7 @@ export function App({
|
|||
isInitialState?: boolean;
|
||||
onPanelClick?: () => void;
|
||||
projectTools: z.infer<typeof WorkflowTool>[];
|
||||
triggerCopilotChat?: (message: string) => void;
|
||||
}) {
|
||||
const [counter, setCounter] = useState<number>(0);
|
||||
const [testProfile, setTestProfile] = useState<WithStringId<z.infer<typeof TestProfile>> | null>(null);
|
||||
|
|
@ -187,6 +189,7 @@ export function App({
|
|||
onCopyClick={(fn) => { getCopyContentRef.current = fn; }}
|
||||
showDebugMessages={showDebugMessages}
|
||||
projectTools={projectTools}
|
||||
triggerCopilotChat={triggerCopilotChat}
|
||||
/>
|
||||
</div>
|
||||
</Panel>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import { ProfileContextBox } from "./profile-context-box";
|
|||
import { USE_TESTING_FEATURE } from "@/app/lib/feature_flags";
|
||||
import { BillingUpgradeModal } from "@/components/common/billing-upgrade-modal";
|
||||
import { ChevronDownIcon } from "@heroicons/react/24/outline";
|
||||
import { FeedbackModal } from "./feedback-modal";
|
||||
import { FIX_WORKFLOW_PROMPT, FIX_WORKFLOW_PROMPT_WITH_FEEDBACK } from "../copilot-prompts";
|
||||
|
||||
export function Chat({
|
||||
chat,
|
||||
|
|
@ -29,6 +31,7 @@ export function Chat({
|
|||
showDebugMessages = true,
|
||||
showJsonMode = false,
|
||||
projectTools,
|
||||
triggerCopilotChat,
|
||||
}: {
|
||||
chat: z.infer<typeof PlaygroundChat>;
|
||||
projectId: string;
|
||||
|
|
@ -44,6 +47,7 @@ export function Chat({
|
|||
showDebugMessages?: boolean;
|
||||
showJsonMode?: boolean;
|
||||
projectTools: z.infer<typeof WorkflowTool>[];
|
||||
triggerCopilotChat?: (message: string) => void;
|
||||
}) {
|
||||
const [messages, setMessages] = useState<z.infer<typeof Message>[]>(chat.messages);
|
||||
const [loadingAssistantResponse, setLoadingAssistantResponse] = useState<boolean>(false);
|
||||
|
|
@ -53,6 +57,9 @@ export function Chat({
|
|||
const [lastAgenticResponse, setLastAgenticResponse] = useState<unknown | null>(null);
|
||||
const [optimisticMessages, setOptimisticMessages] = useState<z.infer<typeof Message>[]>(chat.messages);
|
||||
const [isLastInteracted, setIsLastInteracted] = useState(false);
|
||||
const [showFeedbackModal, setShowFeedbackModal] = useState(false);
|
||||
const [pendingFixMessage, setPendingFixMessage] = useState<string | null>(null);
|
||||
const [showSuccessMessage, setShowSuccessMessage] = useState(false);
|
||||
|
||||
// --- Scroll/auto-scroll/unread bubble logic ---
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
|
@ -101,6 +108,37 @@ export function Chat({
|
|||
setOptimisticMessages(messages);
|
||||
}, [messages]);
|
||||
|
||||
// Handle fix functionality
|
||||
const handleFix = useCallback((message: string) => {
|
||||
setPendingFixMessage(message);
|
||||
setShowFeedbackModal(true);
|
||||
}, []);
|
||||
|
||||
const handleFeedbackSubmit = useCallback((feedback: string) => {
|
||||
if (!pendingFixMessage) return;
|
||||
|
||||
// Create the copilot prompt
|
||||
const prompt = feedback.trim()
|
||||
? FIX_WORKFLOW_PROMPT_WITH_FEEDBACK
|
||||
.replace('{chat_turn}', pendingFixMessage)
|
||||
.replace('{feedback}', feedback)
|
||||
: FIX_WORKFLOW_PROMPT
|
||||
.replace('{chat_turn}', pendingFixMessage);
|
||||
|
||||
// Use the triggerCopilotChat function if available, otherwise fall back to localStorage
|
||||
if (triggerCopilotChat) {
|
||||
triggerCopilotChat(prompt);
|
||||
// Show a subtle success indication
|
||||
setShowSuccessMessage(true);
|
||||
setTimeout(() => setShowSuccessMessage(false), 3000);
|
||||
} else {
|
||||
// Fallback for standalone playground
|
||||
localStorage.setItem(`project_prompt_${projectId}`, prompt);
|
||||
alert('Fix request submitted! Redirecting to workflow editor...');
|
||||
window.location.href = `/projects/${projectId}/workflow`;
|
||||
}
|
||||
}, [pendingFixMessage, projectId, triggerCopilotChat]);
|
||||
|
||||
// collect published tool call results
|
||||
const toolCallResults: Record<string, z.infer<typeof ToolMessage>> = {};
|
||||
optimisticMessages
|
||||
|
|
@ -302,6 +340,7 @@ export function Chat({
|
|||
showSystemMessage={false}
|
||||
showDebugMessages={showDebugMessages}
|
||||
showJsonMode={showJsonMode}
|
||||
onFix={handleFix}
|
||||
/>
|
||||
{showUnreadBubble && (
|
||||
<button
|
||||
|
|
@ -322,6 +361,19 @@ export function Chat({
|
|||
</div>
|
||||
|
||||
<div className="sticky bottom-0 bg-white dark:bg-zinc-900 pt-4 pb-2">
|
||||
{showSuccessMessage && (
|
||||
<div className="mb-4 p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800
|
||||
rounded-lg flex gap-2 justify-between items-center">
|
||||
<p className="text-green-600 dark:text-green-400 text-sm">Skipper will suggest fixes for you now.</p>
|
||||
<Button
|
||||
size="sm"
|
||||
color="success"
|
||||
onPress={() => setShowSuccessMessage(false)}
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{fetchResponseError && (
|
||||
<div className="mb-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800
|
||||
rounded-lg flex gap-2 justify-between items-center">
|
||||
|
|
@ -354,5 +406,11 @@ export function Chat({
|
|||
onClose={() => setBillingError(null)}
|
||||
errorMessage={billingError || ''}
|
||||
/>
|
||||
<FeedbackModal
|
||||
isOpen={showFeedbackModal}
|
||||
onClose={() => setShowFeedbackModal(false)}
|
||||
onSubmit={handleFeedbackSubmit}
|
||||
title="Fix Assistant"
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
'use client';
|
||||
import { useState } from "react";
|
||||
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button, Textarea } from "@heroui/react";
|
||||
|
||||
interface FeedbackModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit: (feedback: string) => void;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export function FeedbackModal({ isOpen, onClose, onSubmit, title = "Provide Feedback" }: FeedbackModalProps) {
|
||||
const [feedback, setFeedback] = useState("");
|
||||
|
||||
const handleSubmit = () => {
|
||||
onSubmit(feedback);
|
||||
setFeedback("");
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setFeedback("");
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={handleCancel} size="md">
|
||||
<ModalContent className="feedback-modal">
|
||||
<ModalHeader className="flex flex-col gap-1">
|
||||
{title}
|
||||
</ModalHeader>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400 px-6 pt-1 pb-0">
|
||||
Tell Skipper what needs to be fixed
|
||||
</p>
|
||||
<ModalBody>
|
||||
<div className="space-y-3">
|
||||
<Textarea
|
||||
placeholder="Describe the issue..."
|
||||
value={feedback}
|
||||
onChange={(e) => setFeedback(e.target.value)}
|
||||
minRows={3}
|
||||
maxRows={6}
|
||||
className="w-full !text-xs focus:ring-0 focus:shadow-none focus:border-gray-300"
|
||||
/>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant="bordered" onPress={handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button color="primary" onPress={handleSubmit}>
|
||||
Submit
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import z from "zod";
|
|||
import { Workflow } from "@/app/lib/types/workflow_types";
|
||||
import { WorkflowTool } from "@/app/lib/types/workflow_types";
|
||||
import MarkdownContent from "@/app/lib/components/markdown-content";
|
||||
import { ChevronRightIcon, ChevronDownIcon, ChevronUpIcon, CodeIcon, CheckCircleIcon, FileTextIcon, EyeIcon, EyeOffIcon, WrapTextIcon, ArrowRightFromLineIcon, BracesIcon, TextIcon } from "lucide-react";
|
||||
import { ChevronRightIcon, ChevronDownIcon, ChevronUpIcon, CodeIcon, CheckCircleIcon, FileTextIcon, EyeIcon, EyeOffIcon, WrapTextIcon, ArrowRightFromLineIcon, BracesIcon, TextIcon, FlagIcon } from "lucide-react";
|
||||
import { TestProfile } from "@/app/lib/types/testing_types";
|
||||
import { ProfileContextBox } from "./profile-context-box";
|
||||
import { Message, ToolMessage, AssistantMessageWithToolCalls } from "@/app/lib/types/types";
|
||||
|
|
@ -30,7 +30,7 @@ function UserMessage({ content }: { content: string }) {
|
|||
);
|
||||
}
|
||||
|
||||
function InternalAssistantMessage({ content, sender, latency, delta, showJsonMode = false }: { content: string, sender: string | null | undefined, latency: number, delta: number, showJsonMode?: boolean }) {
|
||||
function InternalAssistantMessage({ content, sender, latency, delta, showJsonMode = false, onFix, showDebugMessages, isFirstAssistant }: { content: string, sender: string | null | undefined, latency: number, delta: number, showJsonMode?: boolean, onFix?: (message: string) => void, showDebugMessages?: boolean, isFirstAssistant?: boolean }) {
|
||||
const isJsonContent = useMemo(() => {
|
||||
try {
|
||||
JSON.parse(content);
|
||||
|
|
@ -84,10 +84,20 @@ function InternalAssistantMessage({ content, sender, latency, delta, showJsonMod
|
|||
|
||||
return (
|
||||
<div className="self-start flex flex-col gap-1 my-5">
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1">
|
||||
{sender ?? 'Assistant'}
|
||||
</div>
|
||||
<div className="max-w-[85%] inline-block">
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1 flex justify-between items-center mb-2">
|
||||
<span>{sender ?? 'Assistant'}</span>
|
||||
{showDebugMessages && onFix && !isFirstAssistant && (
|
||||
<button
|
||||
onClick={() => onFix(content)}
|
||||
className="flex items-center gap-1 text-xs text-orange-700 dark:text-orange-400 hover:text-orange-800 dark:hover:text-orange-300 hover:underline"
|
||||
title="Fix this response"
|
||||
>
|
||||
<FlagIcon size={12} />
|
||||
Fix
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="bg-gray-50 dark:bg-zinc-800 px-4 py-2.5 rounded-2xl rounded-bl-lg text-sm leading-relaxed text-gray-700 dark:text-gray-200 border-none shadow-sm animate-slideUpAndFade flex flex-col items-stretch">
|
||||
<div className="text-left mb-2">
|
||||
{isJsonContent && hasResponseKey && (
|
||||
|
|
@ -134,13 +144,37 @@ function InternalAssistantMessage({ content, sender, latency, delta, showJsonMod
|
|||
);
|
||||
}
|
||||
|
||||
function AssistantMessage({ content, sender, latency }: { content: string, sender: string | null | undefined, latency: number }) {
|
||||
function AssistantMessage({
|
||||
content,
|
||||
sender,
|
||||
latency,
|
||||
onFix,
|
||||
showDebugMessages,
|
||||
isFirstAssistant
|
||||
}: {
|
||||
content: string,
|
||||
sender: string | null | undefined,
|
||||
latency: number,
|
||||
onFix?: (message: string) => void,
|
||||
showDebugMessages?: boolean,
|
||||
isFirstAssistant?: boolean
|
||||
}) {
|
||||
return (
|
||||
<div className="self-start flex flex-col gap-1 my-5">
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1">
|
||||
{sender ?? 'Assistant'}
|
||||
</div>
|
||||
<div className="max-w-[85%] inline-block">
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1 flex justify-between items-center mb-2">
|
||||
<span>{sender ?? 'Assistant'}</span>
|
||||
{showDebugMessages && onFix && !isFirstAssistant && (
|
||||
<button
|
||||
onClick={() => onFix(content)}
|
||||
className="flex items-center gap-1 text-xs text-orange-700 dark:text-orange-400 hover:text-orange-800 dark:hover:text-orange-300 hover:underline"
|
||||
title="Fix this response"
|
||||
>
|
||||
<FlagIcon size={12} />
|
||||
Fix
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="bg-purple-50 dark:bg-purple-900/30 px-4 py-2.5
|
||||
rounded-2xl rounded-bl-lg text-sm leading-relaxed
|
||||
text-gray-800 dark:text-purple-100
|
||||
|
|
@ -182,7 +216,10 @@ function ToolCalls({
|
|||
workflow,
|
||||
testProfile = null,
|
||||
systemMessage,
|
||||
delta
|
||||
delta,
|
||||
onFix,
|
||||
showDebugMessages,
|
||||
isFirstAssistant
|
||||
}: {
|
||||
toolCalls: z.infer<typeof AssistantMessageWithToolCalls>['toolCalls'];
|
||||
results: Record<string, z.infer<typeof ToolMessage>>;
|
||||
|
|
@ -193,9 +230,12 @@ function ToolCalls({
|
|||
testProfile: z.infer<typeof TestProfile> | null;
|
||||
systemMessage: string | undefined;
|
||||
delta: number;
|
||||
onFix?: (message: string) => void;
|
||||
showDebugMessages?: boolean;
|
||||
isFirstAssistant?: boolean;
|
||||
}) {
|
||||
return <div className="flex flex-col gap-4">
|
||||
{toolCalls.map(toolCall => {
|
||||
{toolCalls.map((toolCall, idx) => {
|
||||
return <ToolCall
|
||||
key={toolCall.id}
|
||||
toolCall={toolCall}
|
||||
|
|
@ -203,6 +243,9 @@ function ToolCalls({
|
|||
sender={sender}
|
||||
workflow={workflow}
|
||||
delta={delta}
|
||||
onFix={onFix}
|
||||
showDebugMessages={showDebugMessages}
|
||||
isFirstAssistant={isFirstAssistant && idx === 0}
|
||||
/>
|
||||
})}
|
||||
</div>;
|
||||
|
|
@ -213,13 +256,19 @@ function ToolCall({
|
|||
result,
|
||||
sender,
|
||||
workflow,
|
||||
delta
|
||||
delta,
|
||||
onFix,
|
||||
showDebugMessages,
|
||||
isFirstAssistant
|
||||
}: {
|
||||
toolCall: z.infer<typeof AssistantMessageWithToolCalls>['toolCalls'][number];
|
||||
result: z.infer<typeof ToolMessage> | undefined;
|
||||
sender: string | null | undefined;
|
||||
workflow: z.infer<typeof Workflow>;
|
||||
delta: number;
|
||||
onFix?: (message: string) => void;
|
||||
showDebugMessages?: boolean;
|
||||
isFirstAssistant?: boolean;
|
||||
}) {
|
||||
let matchingWorkflowTool: z.infer<typeof WorkflowTool> | undefined;
|
||||
for (const tool of workflow.tools) {
|
||||
|
|
@ -242,6 +291,8 @@ function ToolCall({
|
|||
sender={sender ?? ''}
|
||||
workflow={workflow}
|
||||
delta={delta}
|
||||
onFix={onFix}
|
||||
showDebugMessages={showDebugMessages}
|
||||
/>;
|
||||
}
|
||||
|
||||
|
|
@ -280,13 +331,17 @@ function ClientToolCall({
|
|||
result: availableResult,
|
||||
sender,
|
||||
workflow,
|
||||
delta
|
||||
delta,
|
||||
onFix,
|
||||
showDebugMessages
|
||||
}: {
|
||||
toolCall: z.infer<typeof AssistantMessageWithToolCalls>['toolCalls'][number];
|
||||
result: z.infer<typeof ToolMessage> | undefined;
|
||||
sender: string | null | undefined;
|
||||
workflow: z.infer<typeof Workflow>;
|
||||
delta: number;
|
||||
onFix?: (message: string) => void;
|
||||
showDebugMessages?: boolean;
|
||||
}) {
|
||||
const [wrapText, setWrapText] = useState(true);
|
||||
const [paramsExpanded, setParamsExpanded] = useState(false);
|
||||
|
|
@ -299,8 +354,18 @@ function ClientToolCall({
|
|||
return (
|
||||
<div className="self-start flex flex-col gap-1 my-5">
|
||||
{sender && (
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1">
|
||||
{sender}
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1 flex justify-between items-center">
|
||||
<span>{sender}</span>
|
||||
{showDebugMessages && onFix && (
|
||||
<button
|
||||
onClick={() => onFix(`Tool call: ${toolCall.function.name}`)}
|
||||
className="flex items-center gap-1 text-xs text-orange-700 dark:text-orange-400 hover:text-orange-800 dark:hover:text-orange-300 hover:underline"
|
||||
title="Fix this tool call"
|
||||
>
|
||||
<FlagIcon size={12} />
|
||||
Fix
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="min-w-[85%]">
|
||||
|
|
@ -309,13 +374,15 @@ function ClientToolCall({
|
|||
bg-gray-50 dark:bg-gray-800 shadow-sm dark:shadow-gray-950/20">
|
||||
<div className="flex flex-col gap-1 min-w-0">
|
||||
<div className="shrink-0 flex gap-2 items-center flex-nowrap">
|
||||
{!availableResult && <Spinner size="sm" />}
|
||||
{availableResult && <CheckCircleIcon size={16} className="text-green-500" />}
|
||||
<div className="flex items-center font-medium text-xs gap-2 min-w-0 flex-nowrap">
|
||||
<span>Invoked Tool:</span>
|
||||
<span className="px-2 py-0.5 rounded-full bg-purple-50 text-purple-800 dark:bg-purple-900/30 dark:text-purple-100 text-xs align-middle whitespace-nowrap">
|
||||
{toolCall.function.name}
|
||||
</span>
|
||||
<div className="flex items-center gap-2 min-w-0 flex-nowrap">
|
||||
{!availableResult && <Spinner size="sm" />}
|
||||
{availableResult && <CheckCircleIcon size={16} className="text-green-500" />}
|
||||
<div className="flex items-center font-medium text-xs gap-2 min-w-0 flex-nowrap">
|
||||
<span>Invoked Tool:</span>
|
||||
<span className="px-2 py-0.5 rounded-full bg-purple-50 text-purple-800 dark:bg-purple-900/30 dark:text-purple-100 text-xs align-middle whitespace-nowrap">
|
||||
{toolCall.function.name}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{hasExpandedContent && (
|
||||
|
|
@ -362,8 +429,18 @@ function ClientToolCall({
|
|||
return (
|
||||
<div className="self-start flex flex-col gap-1 my-5">
|
||||
{sender && (
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1">
|
||||
{sender}
|
||||
<div className="text-gray-500 dark:text-gray-400 text-xs pl-1 flex justify-between items-center">
|
||||
<span>{sender}</span>
|
||||
{showDebugMessages && onFix && (
|
||||
<button
|
||||
onClick={() => onFix(`Tool call: ${toolCall.function.name}`)}
|
||||
className="flex items-center gap-1 text-xs text-orange-700 dark:text-orange-400 hover:text-orange-800 dark:hover:text-orange-300 hover:underline"
|
||||
title="Fix this tool call"
|
||||
>
|
||||
<FlagIcon size={12} />
|
||||
Fix
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full">
|
||||
|
|
@ -372,13 +449,15 @@ function ClientToolCall({
|
|||
bg-gray-50 dark:bg-gray-800 shadow-sm dark:shadow-gray-950/20 w-full">
|
||||
<div className="flex flex-col gap-1 w-full">
|
||||
<div className="shrink-0 flex gap-2 items-center w-full flex-nowrap">
|
||||
{!availableResult && <Spinner size="sm" />}
|
||||
{availableResult && <CheckCircleIcon size={16} className="text-green-500" />}
|
||||
<div className="flex items-center font-medium text-xs gap-2 w-full min-w-0 flex-nowrap">
|
||||
<span>Invoked Tool:</span>
|
||||
<span className="px-2 py-0.5 rounded-full bg-purple-50 text-purple-800 dark:bg-purple-900/30 dark:text-purple-100 text-xs align-middle truncate min-w-0 max-w-full">
|
||||
{toolCall.function.name}
|
||||
</span>
|
||||
<div className="flex items-center gap-2 min-w-0 flex-nowrap">
|
||||
{!availableResult && <Spinner size="sm" />}
|
||||
{availableResult && <CheckCircleIcon size={16} className="text-green-500" />}
|
||||
<div className="flex items-center font-medium text-xs gap-2 min-w-0 flex-nowrap">
|
||||
<span>Invoked Tool:</span>
|
||||
<span className="px-2 py-0.5 rounded-full bg-purple-50 text-purple-800 dark:bg-purple-900/30 dark:text-purple-100 text-xs align-middle truncate min-w-0 max-w-full">
|
||||
{toolCall.function.name}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{hasExpandedContent && (
|
||||
|
|
@ -503,6 +582,7 @@ export function Messages({
|
|||
showSystemMessage,
|
||||
showDebugMessages = true,
|
||||
showJsonMode = false,
|
||||
onFix,
|
||||
}: {
|
||||
projectId: string;
|
||||
messages: z.infer<typeof Message>[];
|
||||
|
|
@ -515,6 +595,7 @@ export function Messages({
|
|||
showSystemMessage: boolean;
|
||||
showDebugMessages?: boolean;
|
||||
showJsonMode?: boolean;
|
||||
onFix?: (message: string) => void;
|
||||
}) {
|
||||
// Remove scroll/auto-scroll state and logic
|
||||
// const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
|
@ -522,7 +603,11 @@ export function Messages({
|
|||
// const [showUnreadBubble, setShowUnreadBubble] = useState(false);
|
||||
// Remove handleScroll and useEffect for scroll
|
||||
|
||||
// Find the index of the first assistant message
|
||||
const firstAssistantIdx = messages.findIndex(m => m.role === 'assistant');
|
||||
|
||||
const renderMessage = (message: z.infer<typeof Message>, index: number) => {
|
||||
const isFirstAssistant = message.role === 'assistant' && index === firstAssistantIdx;
|
||||
if (message.role === 'assistant') {
|
||||
// TODO: add latency support
|
||||
// let latency = new Date(message.createdAt).getTime() - lastUserMessageTimestamp;
|
||||
|
|
@ -548,6 +633,9 @@ export function Messages({
|
|||
testProfile={testProfile}
|
||||
systemMessage={systemMessage}
|
||||
delta={latency}
|
||||
onFix={onFix}
|
||||
showDebugMessages={showDebugMessages}
|
||||
isFirstAssistant={isFirstAssistant}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -565,6 +653,9 @@ export function Messages({
|
|||
latency={latency}
|
||||
delta={latency}
|
||||
showJsonMode={showJsonMode}
|
||||
onFix={onFix}
|
||||
showDebugMessages={showDebugMessages}
|
||||
isFirstAssistant={isFirstAssistant}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -575,6 +666,9 @@ export function Messages({
|
|||
content={message.content ?? ''}
|
||||
sender={message.agentName ?? ''}
|
||||
latency={latency}
|
||||
onFix={onFix}
|
||||
showDebugMessages={showDebugMessages}
|
||||
isFirstAssistant={isFirstAssistant}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
export const FIX_WORKFLOW_PROMPT = `There is an issue with this turn of chat: "{chat_turn}"
|
||||
|
||||
Fix the issue by updating necessary agents and tools.`;
|
||||
|
||||
export const FIX_WORKFLOW_PROMPT_WITH_FEEDBACK = `${FIX_WORKFLOW_PROMPT}
|
||||
|
||||
Here are more details: {feedback}`;
|
||||
|
|
@ -1028,6 +1028,7 @@ export function WorkflowEditor({
|
|||
isInitialState={isInitialState}
|
||||
onPanelClick={handlePlaygroundClick}
|
||||
projectTools={projectTools}
|
||||
triggerCopilotChat={triggerCopilotChat}
|
||||
/>
|
||||
{state.present.selection?.type === "agent" && <AgentConfig
|
||||
key={`agent-${state.present.workflow.agents.findIndex(agent => agent.name === state.present.selection!.name)}`}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue