@hef2024/llmasaservice-ui 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AICHATPANEL-PORT-INVENTORY.md +830 -0
- package/DEBUG-ERROR-HANDLING.md +130 -0
- package/FIX-APPLIED.md +234 -0
- package/IMPLEMENTATION-COMPLETE.md +246 -0
- package/README.md +1 -0
- package/dist/index.css +190 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +242 -56
- package/dist/index.mjs +242 -56
- package/docs/CHANGELOG-ERROR-HANDLING.md +247 -0
- package/docs/ERROR-HANDLING-413.md +253 -0
- package/docs/ERROR-HANDLING-SUMMARY.md +131 -0
- package/package.json +1 -1
- package/src/AIChatPanel.css +120 -1
- package/src/AIChatPanel.tsx +282 -40
- package/src/ChatPanel.css +111 -0
- package/src/ChatPanel.tsx +105 -4
package/src/ChatPanel.css
CHANGED
|
@@ -1090,4 +1090,115 @@ button[data-pending="true"]::after {
|
|
|
1090
1090
|
|
|
1091
1091
|
.searching-section {
|
|
1092
1092
|
border-left: 3px solid #7b68ee;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/* ============================================================================
|
|
1096
|
+
Error Banner
|
|
1097
|
+
============================================================================ */
|
|
1098
|
+
|
|
1099
|
+
.error-banner {
|
|
1100
|
+
display: flex;
|
|
1101
|
+
align-items: flex-start;
|
|
1102
|
+
gap: 12px;
|
|
1103
|
+
padding: 12px 16px;
|
|
1104
|
+
margin: 12px 16px;
|
|
1105
|
+
background: #fef2f2;
|
|
1106
|
+
border: 1px solid #fecaca;
|
|
1107
|
+
border-radius: 8px;
|
|
1108
|
+
animation: slideDown 0.2s ease-out;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
.dark-theme .error-banner {
|
|
1112
|
+
background: #7f1d1d;
|
|
1113
|
+
border-color: #991b1b;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
.error-banner__icon {
|
|
1117
|
+
flex-shrink: 0;
|
|
1118
|
+
color: #dc2626;
|
|
1119
|
+
margin-top: 2px;
|
|
1120
|
+
width: 20px;
|
|
1121
|
+
height: 20px;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
.error-banner__icon svg {
|
|
1125
|
+
width: 100%;
|
|
1126
|
+
height: 100%;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
.dark-theme .error-banner__icon {
|
|
1130
|
+
color: #fca5a5;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
.error-banner__content {
|
|
1134
|
+
flex: 1;
|
|
1135
|
+
display: flex;
|
|
1136
|
+
flex-direction: column;
|
|
1137
|
+
gap: 4px;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
.error-banner__message {
|
|
1141
|
+
color: #991b1b;
|
|
1142
|
+
font-size: 14px;
|
|
1143
|
+
font-weight: 500;
|
|
1144
|
+
line-height: 1.4;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
.dark-theme .error-banner__message {
|
|
1148
|
+
color: #fecaca;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
.error-banner__hint {
|
|
1152
|
+
color: #b91c1c;
|
|
1153
|
+
font-size: 13px;
|
|
1154
|
+
line-height: 1.4;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
.dark-theme .error-banner__hint {
|
|
1158
|
+
color: #fca5a5;
|
|
1159
|
+
opacity: 0.8;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
.error-banner__close {
|
|
1163
|
+
flex-shrink: 0;
|
|
1164
|
+
background: none;
|
|
1165
|
+
border: none;
|
|
1166
|
+
padding: 4px;
|
|
1167
|
+
cursor: pointer;
|
|
1168
|
+
color: #991b1b;
|
|
1169
|
+
border-radius: 4px;
|
|
1170
|
+
transition: background-color 0.15s;
|
|
1171
|
+
width: 24px;
|
|
1172
|
+
height: 24px;
|
|
1173
|
+
display: flex;
|
|
1174
|
+
align-items: center;
|
|
1175
|
+
justify-content: center;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
.error-banner__close svg {
|
|
1179
|
+
width: 16px;
|
|
1180
|
+
height: 16px;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
.dark-theme .error-banner__close {
|
|
1184
|
+
color: #fca5a5;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
.error-banner__close:hover {
|
|
1188
|
+
background: rgba(220, 38, 38, 0.1);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
.dark-theme .error-banner__close:hover {
|
|
1192
|
+
background: rgba(252, 165, 165, 0.1);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
@keyframes slideDown {
|
|
1196
|
+
from {
|
|
1197
|
+
opacity: 0;
|
|
1198
|
+
transform: translateY(-8px);
|
|
1199
|
+
}
|
|
1200
|
+
to {
|
|
1201
|
+
opacity: 1;
|
|
1202
|
+
transform: translateY(0);
|
|
1203
|
+
}
|
|
1093
1204
|
}
|
package/src/ChatPanel.tsx
CHANGED
|
@@ -222,6 +222,9 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
222
222
|
>([]);
|
|
223
223
|
const [currentThinkingIndex, setCurrentThinkingIndex] = useState(0);
|
|
224
224
|
|
|
225
|
+
// State for error handling
|
|
226
|
+
const [error, setError] = useState<{ message: string; code?: string } | null>(null);
|
|
227
|
+
|
|
225
228
|
// State for tracking user-resized textarea height
|
|
226
229
|
const [userResizedHeight, setUserResizedHeight] = useState<number | null>(null);
|
|
227
230
|
|
|
@@ -680,7 +683,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
680
683
|
fetchAndSetTools();
|
|
681
684
|
}, [mcpServers, publicAPIUrl]);
|
|
682
685
|
|
|
683
|
-
const
|
|
686
|
+
const llmResult = useLLM({
|
|
684
687
|
project_id: project_id,
|
|
685
688
|
customer: currentCustomer,
|
|
686
689
|
url: url,
|
|
@@ -694,6 +697,57 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
694
697
|
};
|
|
695
698
|
}) as [],
|
|
696
699
|
});
|
|
700
|
+
|
|
701
|
+
const { send, response, idle, stop, lastCallId, setResponse, error: llmError } = llmResult;
|
|
702
|
+
|
|
703
|
+
// Error handler function - reusable for both error state and callbacks
|
|
704
|
+
const handleError = useCallback((errorMessage: string, historyKey?: string) => {
|
|
705
|
+
console.log('[ChatPanel] Error detected:', errorMessage);
|
|
706
|
+
|
|
707
|
+
// Detect 413 Content Too Large error
|
|
708
|
+
if (errorMessage.includes('413') || errorMessage.toLowerCase().includes('content too large')) {
|
|
709
|
+
setError({
|
|
710
|
+
message: 'The context is too large to process. Please start a new conversation or reduce the amount of context.',
|
|
711
|
+
code: '413',
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
// Detect other network errors
|
|
715
|
+
else if (errorMessage.toLowerCase().includes('network error') || errorMessage.toLowerCase().includes('fetch')) {
|
|
716
|
+
setError({
|
|
717
|
+
message: 'Network error. Please check your connection and try again.',
|
|
718
|
+
code: 'NETWORK_ERROR',
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
// Generic error
|
|
722
|
+
else {
|
|
723
|
+
setError({
|
|
724
|
+
message: errorMessage,
|
|
725
|
+
code: 'UNKNOWN_ERROR',
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Reset loading state
|
|
730
|
+
setIsLoading(false);
|
|
731
|
+
|
|
732
|
+
// Update history to show error
|
|
733
|
+
const keyToUse = historyKey || lastKey;
|
|
734
|
+
if (keyToUse) {
|
|
735
|
+
setHistory((prev) => ({
|
|
736
|
+
...prev,
|
|
737
|
+
[keyToUse]: {
|
|
738
|
+
content: `Error: ${errorMessage}`,
|
|
739
|
+
callId: lastCallId || '',
|
|
740
|
+
},
|
|
741
|
+
}));
|
|
742
|
+
}
|
|
743
|
+
}, [lastKey, lastCallId]);
|
|
744
|
+
|
|
745
|
+
// Monitor for errors from useLLM hook (for errors not caught by callback)
|
|
746
|
+
useEffect(() => {
|
|
747
|
+
if (llmError && llmError.trim()) {
|
|
748
|
+
handleError(llmError);
|
|
749
|
+
}
|
|
750
|
+
}, [llmError, handleError]);
|
|
697
751
|
|
|
698
752
|
// Centralized action processing function to eliminate duplication
|
|
699
753
|
const processActionsOnContent = useCallback((
|
|
@@ -1459,7 +1513,9 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1459
1513
|
true,
|
|
1460
1514
|
service,
|
|
1461
1515
|
currentConversation,
|
|
1462
|
-
lastController
|
|
1516
|
+
lastController,
|
|
1517
|
+
undefined, // onComplete
|
|
1518
|
+
(errorMsg: string) => handleError(errorMsg) // onError
|
|
1463
1519
|
);
|
|
1464
1520
|
} catch (error) {
|
|
1465
1521
|
console.error("Error in processing all tools:", error);
|
|
@@ -1941,7 +1997,9 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
1941
1997
|
true,
|
|
1942
1998
|
service,
|
|
1943
1999
|
convId,
|
|
1944
|
-
controller
|
|
2000
|
+
controller,
|
|
2001
|
+
undefined, // onComplete
|
|
2002
|
+
(errorMsg: string) => handleError(errorMsg) // onError
|
|
1945
2003
|
);
|
|
1946
2004
|
|
|
1947
2005
|
// Store the context in component state
|
|
@@ -2153,6 +2211,14 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
2153
2211
|
// Clear thinking blocks for new response
|
|
2154
2212
|
setThinkingBlocks([]);
|
|
2155
2213
|
setCurrentThinkingIndex(0);
|
|
2214
|
+
|
|
2215
|
+
// Clear any previous errors
|
|
2216
|
+
setError(null);
|
|
2217
|
+
|
|
2218
|
+
// IMPORTANT: Clear the response BEFORE setting new lastKey
|
|
2219
|
+
// This prevents the old response from being written to the new history entry
|
|
2220
|
+
// when the history update effect runs
|
|
2221
|
+
setResponse("");
|
|
2156
2222
|
|
|
2157
2223
|
// Auto-set email if valid before proceeding
|
|
2158
2224
|
if (emailInput && isEmailAddress(emailInput) && !emailInputSet) {
|
|
@@ -2297,7 +2363,9 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
2297
2363
|
true,
|
|
2298
2364
|
service,
|
|
2299
2365
|
convId,
|
|
2300
|
-
controller
|
|
2366
|
+
controller,
|
|
2367
|
+
undefined, // onComplete
|
|
2368
|
+
(errorMsg: string) => handleError(errorMsg) // onError
|
|
2301
2369
|
);
|
|
2302
2370
|
|
|
2303
2371
|
setLastPrompt(nextPromptToSend);
|
|
@@ -2692,6 +2760,38 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
2692
2760
|
className={"llm-panel" + (theme === "light" ? "" : " dark-theme")}
|
|
2693
2761
|
>
|
|
2694
2762
|
{title && title !== "" ? <div className="title">{title}</div> : null}
|
|
2763
|
+
|
|
2764
|
+
{/* Error Banner */}
|
|
2765
|
+
{error && (
|
|
2766
|
+
<div className="error-banner">
|
|
2767
|
+
<div className="error-banner__icon">
|
|
2768
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
2769
|
+
<circle cx="12" cy="12" r="10" />
|
|
2770
|
+
<line x1="12" x2="12" y1="8" y2="12" />
|
|
2771
|
+
<line x1="12" x2="12.01" y1="16" y2="16" />
|
|
2772
|
+
</svg>
|
|
2773
|
+
</div>
|
|
2774
|
+
<div className="error-banner__content">
|
|
2775
|
+
<div className="error-banner__message">{error.message}</div>
|
|
2776
|
+
{error.code === '413' && (
|
|
2777
|
+
<div className="error-banner__hint">
|
|
2778
|
+
Try starting a new conversation or reducing the amount of information being sent.
|
|
2779
|
+
</div>
|
|
2780
|
+
)}
|
|
2781
|
+
</div>
|
|
2782
|
+
<button
|
|
2783
|
+
className="error-banner__close"
|
|
2784
|
+
onClick={() => setError(null)}
|
|
2785
|
+
aria-label="Dismiss error"
|
|
2786
|
+
>
|
|
2787
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
2788
|
+
<line x1="18" x2="6" y1="6" y2="18" />
|
|
2789
|
+
<line x1="6" x2="18" y1="6" y2="18" />
|
|
2790
|
+
</svg>
|
|
2791
|
+
</button>
|
|
2792
|
+
</div>
|
|
2793
|
+
)}
|
|
2794
|
+
|
|
2695
2795
|
<div className="responseArea" ref={responseAreaRef}>
|
|
2696
2796
|
{initialMessage && initialMessage !== "" ? (
|
|
2697
2797
|
<div className="history-entry">
|
|
@@ -3153,6 +3253,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
|
|
|
3153
3253
|
setIsToolInfoModalOpen(false);
|
|
3154
3254
|
setToolInfoData(null);
|
|
3155
3255
|
setJustReset(true);
|
|
3256
|
+
setError(null); // Clear any errors
|
|
3156
3257
|
|
|
3157
3258
|
// Create a new AbortController for future requests
|
|
3158
3259
|
setLastController(new AbortController());
|