@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/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 { send, response, idle, stop, lastCallId, setResponse } = useLLM({
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());