Skip to content

Commit 4a355da

Browse files
authored
refactor(DATAGO-126701): Enhance error handling and messaging in PromptBuilderChat components (#1275)
* refactor: Enhance error handling and messaging in PromptBuilderChat and related components * feat: Add is_error handling to PromptBuilderResponse and enhance LLM exception tests * feat: Add PromptBuilderChat stories and unit tests for error handling * feat: Update model configuration messaging for clarity and user guidance * feat: Implement ModelWarningBanner component and integrate it into AppLayout for model configuration warnings * feat: Add comprehensive tests for PromptBuilderChat component including rendering, message sending, and error handling * fix: Update API spy type casting to use 'any' for compatibility * feat: Enhance PromptBuilderChat stories with message sending and error handling scenarios * refactor: Clean up comments and improve code readability in PromptBuilderChat stories and tests
1 parent c2f9a45 commit 4a355da

File tree

14 files changed

+1414
-71
lines changed

14 files changed

+1414
-71
lines changed

client/webui/frontend/src/AppLayout.tsx

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ import { useEffect, useState, useCallback } from "react";
22
import { Outlet, useLocation, useNavigate } from "react-router-dom";
33

44
import { NavigationSidebar, CollapsibleNavigationSidebar, ToastContainer, bottomNavigationItems, getTopNavigationItems, EmptyState } from "@/lib/components";
5-
import { MessageBanner } from "@/lib/components/common/MessageBanner";
65
import { SelectionContextMenu, useTextSelection } from "@/lib/components/chat/selection";
76
import { MoveSessionDialog } from "@/lib/components/chat/MoveSessionDialog";
87
import { ModelSetupDialog } from "@/lib/components/models/ModelSetupDialog";
8+
import { ModelWarningBanner } from "@/lib/components/models/ModelWarningBanner";
99
import { SettingsDialog } from "@/lib/components/settings/SettingsDialog";
10-
import { Button } from "@/lib/components/ui";
1110
import { ChatProvider } from "@/lib/providers";
1211
import { useBooleanFlagDetails } from "@openfeature/react-sdk";
1312
import { useAuthContext, useBeforeUnload, useConfigContext, useChatContext, useNavigationItems, useLocalStorage } from "@/lib/hooks";
@@ -177,25 +176,7 @@ function AppLayoutContent() {
177176
<NavigationSidebar items={topNavItems} bottomItems={bottomNavigationItems} activeItem={getActiveItem()} onItemChange={handleNavItemChange} onHeaderClick={handleHeaderClick} />
178177
)}
179178
<main className="h-full w-full flex-1 overflow-auto">
180-
{showModelWarning && (
181-
<MessageBanner
182-
variant="warning"
183-
style={{ alignItems: "center" }}
184-
message={
185-
<div className="flex w-full items-center justify-between gap-3">
186-
<span>
187-
No model has been set up. Some features may not work as intended without a configured model.
188-
{!hasModelConfigWrite && " Contact your administrator for assistance."}
189-
</span>
190-
{hasModelConfigWrite && (
191-
<Button variant="outline" size="sm" className="shrink-0" onClick={() => navigate("/agents?tab=models")}>
192-
Go to Models
193-
</Button>
194-
)}
195-
</div>
196-
}
197-
/>
198-
)}
179+
<ModelWarningBanner showWarning={!!showModelWarning} hasModelConfigWrite={hasModelConfigWrite} />
199180
<Outlet />
200181
</main>
201182
<ToastContainer />
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { useNavigate } from "react-router-dom";
2+
3+
import { MessageBanner } from "@/lib/components/common/MessageBanner";
4+
import { Button } from "@/lib/components/ui";
5+
6+
interface ModelWarningBannerProps {
7+
showWarning: boolean;
8+
hasModelConfigWrite: boolean;
9+
}
10+
11+
export function ModelWarningBanner({ showWarning, hasModelConfigWrite }: ModelWarningBannerProps) {
12+
const navigate = useNavigate();
13+
14+
if (!showWarning) return null;
15+
16+
return (
17+
<MessageBanner
18+
variant="warning"
19+
style={{ alignItems: "center" }}
20+
message={
21+
<div className="flex w-full items-center justify-between gap-3">
22+
<span>
23+
No model has been set up. Some features may not work as intended without a configured model.
24+
{hasModelConfigWrite ? " Go to Agent Mesh to configure your models." : " Ask your administrator to configure models in Agent Mesh."}
25+
</span>
26+
{hasModelConfigWrite && (
27+
<Button variant="outline" size="sm" className="shrink-0" onClick={() => navigate("/agents?tab=models")}>
28+
Go to Models
29+
</Button>
30+
)}
31+
</div>
32+
}
33+
/>
34+
);
35+
}

client/webui/frontend/src/lib/components/models/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ export { ModelDetailsPage } from "./ModelDetailsPage";
33
export { ModelDeleteDialog } from "./ModelDeleteDialog";
44
export { ModelEditPage } from "./ModelEditPage";
55
export { ModelSetupDialog } from "./ModelSetupDialog";
6+
export { ModelWarningBanner } from "./ModelWarningBanner";
67
export { ModelProviderIcon } from "./ModelProviderIcon";
78
export { PROVIDER_DISPLAY_NAMES, DEFAULT_MODEL_ALIASES } from "./common";

client/webui/frontend/src/lib/components/prompts/PromptBuilderChat.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
import React, { useState, useEffect, useRef, useCallback } from "react";
2-
import { Send, Loader2, Sparkles } from "lucide-react";
2+
import { AlertCircle, Send, Loader2, Sparkles } from "lucide-react";
33

44
import { AudioRecorder, Button, MessageBanner, Textarea } from "@/lib/components";
55
import { useAudioSettings, useConfigContext } from "@/lib/hooks";
6+
import { cn } from "@/lib/utils";
67
import type { TemplateConfig } from "@/lib/types";
78
import { api } from "@/lib/api";
89

910
interface Message {
1011
role: "user" | "assistant";
1112
content: string;
1213
timestamp: Date;
14+
isError?: boolean;
1315
}
1416

1517
interface ChatResponse {
1618
message: string;
1719
template_updates: Record<string, unknown>;
1820
confidence: number;
1921
ready_to_save: boolean;
22+
is_error?: boolean;
2023
}
2124

2225
interface PromptBuilderChatProps {
@@ -110,14 +113,15 @@ export const PromptBuilderChat: React.FC<PromptBuilderChatProps> = ({ onConfigUp
110113
role: "assistant",
111114
content: chatData.message,
112115
timestamp: new Date(),
116+
isError: chatData.is_error,
113117
};
114118
setMessages(prev => [...prev, assistantMessage]);
115119

116-
if (Object.keys(chatData.template_updates).length > 0) {
120+
if (!chatData.is_error && Object.keys(chatData.template_updates).length > 0) {
117121
onConfigUpdate(chatData.template_updates);
118122
}
119123

120-
onReadyToSave(chatData.ready_to_save);
124+
if (!chatData.is_error) onReadyToSave(chatData.ready_to_save);
121125

122126
// Scroll to bottom after AI response
123127
setTimeout(() => scrollToBottom(), 100);
@@ -129,6 +133,7 @@ export const PromptBuilderChat: React.FC<PromptBuilderChatProps> = ({ onConfigUp
129133
role: "assistant",
130134
content: "The conversation history is too long for automatic processing. Please describe your task manually, and I'll help you create a template.",
131135
timestamp: new Date(),
136+
isError: true,
132137
};
133138
setMessages(prev => [...prev, errorMessage]);
134139
}
@@ -138,6 +143,7 @@ export const PromptBuilderChat: React.FC<PromptBuilderChatProps> = ({ onConfigUp
138143
role: "assistant",
139144
content: "I encountered an error processing your request. Please try describing your task manually.",
140145
timestamp: new Date(),
146+
isError: true,
141147
};
142148
setMessages(prev => [...prev, errorMessage]);
143149
} finally {
@@ -238,22 +244,24 @@ export const PromptBuilderChat: React.FC<PromptBuilderChatProps> = ({ onConfigUp
238244
role: "assistant",
239245
content: data.message,
240246
timestamp: new Date(),
247+
isError: data.is_error,
241248
};
242249
setMessages(prev => [...prev, assistantMessage]);
243250

244-
// Update config if there are updates
245-
if (Object.keys(data.template_updates).length > 0) {
251+
// Update config if there are updates (skip on error responses)
252+
if (!data.is_error && Object.keys(data.template_updates).length > 0) {
246253
onConfigUpdate(data.template_updates);
247254
}
248255

249256
// Notify parent if ready to save
250-
onReadyToSave(data.ready_to_save);
257+
if (!data.is_error) onReadyToSave(data.ready_to_save);
251258
} catch (error) {
252259
console.error("Error sending message:", error);
253260
const errorMessage: Message = {
254261
role: "assistant",
255262
content: "I encountered an error. Could you please try again?",
256263
timestamp: new Date(),
264+
isError: true,
257265
};
258266
setMessages(prev => [...prev, errorMessage]);
259267
} finally {
@@ -294,8 +302,11 @@ export const PromptBuilderChat: React.FC<PromptBuilderChatProps> = ({ onConfigUp
294302
<div className="flex-1 space-y-4 overflow-y-auto p-4">
295303
{messages.map((message, index) => (
296304
<div key={index} className={`flex ${message.role === "user" ? "justify-end" : "justify-start"}`}>
297-
<div className={`max-w-[80%] rounded-2xl px-4 py-3 ${message.role === "user" ? "bg-(--secondary-w20)" : ""}`}>
298-
<div className="text-sm leading-relaxed whitespace-pre-wrap">{message.content}</div>
305+
<div className={cn("max-w-[80%] rounded-2xl px-4 py-3", message.role === "user" && "bg-(--secondary-w20)", message.isError && "border border-(--error-wMain) bg-(--error-w10)")}>
306+
<div className={cn("text-sm leading-relaxed whitespace-pre-wrap", message.isError && "text-(--error-wMain)")}>
307+
{message.isError && <AlertCircle className="mr-1.5 -mt-0.5 inline-block h-4 w-4" />}
308+
{message.content}
309+
</div>
299310
</div>
300311
</div>
301312
))}

0 commit comments

Comments
 (0)