llmasaservice-ui 0.12.39 → 0.13.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/README.md CHANGED
@@ -7,6 +7,7 @@ This library offers a pre-built panel for incorporating chat GPT style features
7
7
  Features
8
8
  - Multi-turn style chat interface
9
9
  - Open with an initial prompt (for example, after a click on a Summarize button in your application, set the initialPrompt to "Summarize the following text ..." and the panel will automatically show the summary)
10
+ - New Conversation button to reset chat history while preserving customer information
10
11
  - Light and dark theme
11
12
  - Abort functionality
12
13
  - Markdown response display
@@ -145,6 +146,13 @@ c) use prose as the class for the markdownStyle. This example is for dark mode
145
146
  ## Customization
146
147
  You can customize the chat panel by passing props to the ChatPanel component. Refer to the library documentation for more details on available props and customization options.
147
148
 
149
+ ### Button Controls
150
+ - `showSaveButton`: Show/hide the save conversation button (default: true)
151
+ - `showEmailButton`: Show/hide the email conversation button (default: true)
152
+ - `showNewConversationButton`: Show/hide the new conversation button (default: true)
153
+
154
+ The New Conversation button resets the chat history and state while preserving customer email and identification information. It requires two clicks for confirmation to prevent accidental resets - the first click changes the button to "Click to Confirm" and the second click performs the reset.
155
+
148
156
  See our storybook documentation showing how to theme, size and use the variaous features in this component:
149
157
 
150
158
  https://predictabilityatscale.github.io/llmasaservice-ui/?path=/docs/chatpanel--docs
package/dist/index.css CHANGED
@@ -333,6 +333,37 @@
333
333
  background-color: var(--button-background-color-hover);
334
334
  color: var(--button-text-color-hover);
335
335
  }
336
+ .llm-panel .new-conversation-button {
337
+ background-color: var(--prompt-background-color);
338
+ border-color: var(--button-border-color);
339
+ font-weight: 500;
340
+ }
341
+ .llm-panel .new-conversation-button:hover {
342
+ background-color: var(--button-background-color-hover);
343
+ color: var(--button-text-color-hover);
344
+ transform: translateY(-1px);
345
+ }
346
+ .llm-panel .new-conversation-button.confirm-state {
347
+ background-color: #ff6b35;
348
+ color: white;
349
+ border-color: #e55a2b;
350
+ animation: pulse-confirm 1s infinite;
351
+ }
352
+ .llm-panel .new-conversation-button.confirm-state:hover {
353
+ background-color: #e55a2b;
354
+ color: white;
355
+ }
356
+ @keyframes pulse-confirm {
357
+ 0% {
358
+ opacity: 1;
359
+ }
360
+ 50% {
361
+ opacity: 0.7;
362
+ }
363
+ 100% {
364
+ opacity: 1;
365
+ }
366
+ }
336
367
  .llm-panel .suggestion-button {
337
368
  background-color: var(--button-background-color);
338
369
  color: var(--button-text-color);
package/dist/index.d.mts CHANGED
@@ -47,6 +47,7 @@ interface ChatPanelProps {
47
47
  }[];
48
48
  showSaveButton?: boolean;
49
49
  showEmailButton?: boolean;
50
+ showNewConversationButton?: boolean;
50
51
  followOnQuestions?: string[];
51
52
  clearFollowOnQuestionsNextPrompt?: boolean;
52
53
  followOnPrompt?: string;
package/dist/index.d.ts CHANGED
@@ -47,6 +47,7 @@ interface ChatPanelProps {
47
47
  }[];
48
48
  showSaveButton?: boolean;
49
49
  showEmailButton?: boolean;
50
+ showNewConversationButton?: boolean;
50
51
  followOnQuestions?: string[];
51
52
  clearFollowOnQuestionsNextPrompt?: boolean;
52
53
  followOnPrompt?: string;
package/dist/index.js CHANGED
@@ -189,6 +189,7 @@ var ChatPanel = ({
189
189
  actions,
190
190
  showSaveButton = true,
191
191
  showEmailButton = true,
192
+ showNewConversationButton = true,
192
193
  followOnQuestions = [],
193
194
  clearFollowOnQuestionsNextPrompt = false,
194
195
  followOnPrompt = "",
@@ -247,6 +248,8 @@ var ChatPanel = ({
247
248
  const [currentCustomer, setCurrentCustomer] = (0, import_react3.useState)(
248
249
  customer
249
250
  );
251
+ const [justReset, setJustReset] = (0, import_react3.useState)(false);
252
+ const [newConversationConfirm, setNewConversationConfirm] = (0, import_react3.useState)(false);
250
253
  const [allActions, setAllActions] = (0, import_react3.useState)([]);
251
254
  const [pendingToolRequests, setPendingToolRequests] = (0, import_react3.useState)([]);
252
255
  const [followOnQuestionsState, setFollowOnQuestionsState] = (0, import_react3.useState)(followOnQuestions);
@@ -566,7 +569,7 @@ var ChatPanel = ({
566
569
  });
567
570
  fetchAndSetTools();
568
571
  }, [mcpServers, publicAPIUrl]);
569
- const { send, response, idle, stop, lastCallId } = (0, import_llmasaservice_client.useLLM)({
572
+ const { send, response, idle, stop, lastCallId, setResponse } = (0, import_llmasaservice_client.useLLM)({
570
573
  project_id,
571
574
  customer: currentCustomer,
572
575
  url,
@@ -2004,7 +2007,7 @@ var ChatPanel = ({
2004
2007
  var _a2, _b;
2005
2008
  const isLastEntry = index === Object.keys(history).length - 1;
2006
2009
  const hasToolData = !!((((_a2 = historyEntry == null ? void 0 : historyEntry.toolCalls) == null ? void 0 : _a2.length) || 0) > 0 || (((_b = historyEntry == null ? void 0 : historyEntry.toolResponses) == null ? void 0 : _b.length) || 0) > 0);
2007
- return /* @__PURE__ */ import_react3.default.createElement("div", { className: "history-entry", key: index }, hideInitialPrompt && index === 0 ? null : /* @__PURE__ */ import_react3.default.createElement("div", { className: "prompt" }, formatPromptForDisplay(prompt)), /* @__PURE__ */ import_react3.default.createElement("div", { className: "response" }, index === Object.keys(history).length - 1 && (isLoading || !idle) ? /* @__PURE__ */ import_react3.default.createElement("div", { className: "streaming-response" }, (() => {
2010
+ return /* @__PURE__ */ import_react3.default.createElement("div", { className: "history-entry", key: index }, hideInitialPrompt && index === 0 ? null : /* @__PURE__ */ import_react3.default.createElement("div", { className: "prompt" }, formatPromptForDisplay(prompt)), /* @__PURE__ */ import_react3.default.createElement("div", { className: "response" }, index === Object.keys(history).length - 1 && (isLoading || !idle) && !justReset ? /* @__PURE__ */ import_react3.default.createElement("div", { className: "streaming-response" }, (() => {
2008
2011
  const { cleanedText } = processThinkingTags(
2009
2012
  response || ""
2010
2013
  );
@@ -2249,7 +2252,61 @@ var ChatPanel = ({
2249
2252
  },
2250
2253
  "\u270E"
2251
2254
  ))),
2252
- /* @__PURE__ */ import_react3.default.createElement("div", { className: "button-container-actions" }, showSaveButton && /* @__PURE__ */ import_react3.default.createElement(
2255
+ /* @__PURE__ */ import_react3.default.createElement("div", { className: "button-container-actions" }, showNewConversationButton && /* @__PURE__ */ import_react3.default.createElement(
2256
+ "button",
2257
+ {
2258
+ className: `save-button new-conversation-button ${newConversationConfirm ? "confirm-state" : ""}`,
2259
+ onClick: () => {
2260
+ if (!newConversationConfirm) {
2261
+ setNewConversationConfirm(true);
2262
+ setTimeout(() => {
2263
+ setNewConversationConfirm(false);
2264
+ }, 3e3);
2265
+ } else {
2266
+ setNewConversationConfirm(false);
2267
+ if (!idle) {
2268
+ stop(lastController);
2269
+ }
2270
+ setResponse("");
2271
+ setHistory({});
2272
+ setLastMessages([]);
2273
+ setNextPrompt("");
2274
+ setLastPrompt(null);
2275
+ setLastKey(null);
2276
+ setIsLoading(false);
2277
+ setCurrentConversation(null);
2278
+ setCallToActionSent(false);
2279
+ setEmailSent(false);
2280
+ setCTAClickedButNoEmail(false);
2281
+ setEmailClickedButNoEmail(false);
2282
+ setFollowOnQuestionsState(followOnQuestions);
2283
+ setSessionApprovedTools([]);
2284
+ setThinkingBlocks([]);
2285
+ setCurrentThinkingIndex(0);
2286
+ setPendingButtonAttachments([]);
2287
+ setPendingToolRequests([]);
2288
+ setIsEmailModalOpen(false);
2289
+ setIsToolInfoModalOpen(false);
2290
+ setToolInfoData(null);
2291
+ setJustReset(true);
2292
+ setLastController(new AbortController());
2293
+ buttonActionRegistry.current.clear();
2294
+ setTimeout(() => {
2295
+ setJustReset(false);
2296
+ const responseArea = responseAreaRef.current;
2297
+ if (responseArea) {
2298
+ responseArea.scrollTo({
2299
+ top: 0,
2300
+ behavior: "smooth"
2301
+ });
2302
+ }
2303
+ }, 100);
2304
+ }
2305
+ },
2306
+ title: newConversationConfirm ? "Click again to confirm reset" : "Start a new conversation"
2307
+ },
2308
+ newConversationConfirm ? "Click to Confirm" : "New Conversation"
2309
+ ), showSaveButton && /* @__PURE__ */ import_react3.default.createElement(
2253
2310
  "button",
2254
2311
  {
2255
2312
  className: "save-button",
package/dist/index.mjs CHANGED
@@ -161,6 +161,7 @@ var ChatPanel = ({
161
161
  actions,
162
162
  showSaveButton = true,
163
163
  showEmailButton = true,
164
+ showNewConversationButton = true,
164
165
  followOnQuestions = [],
165
166
  clearFollowOnQuestionsNextPrompt = false,
166
167
  followOnPrompt = "",
@@ -219,6 +220,8 @@ var ChatPanel = ({
219
220
  const [currentCustomer, setCurrentCustomer] = useState2(
220
221
  customer
221
222
  );
223
+ const [justReset, setJustReset] = useState2(false);
224
+ const [newConversationConfirm, setNewConversationConfirm] = useState2(false);
222
225
  const [allActions, setAllActions] = useState2([]);
223
226
  const [pendingToolRequests, setPendingToolRequests] = useState2([]);
224
227
  const [followOnQuestionsState, setFollowOnQuestionsState] = useState2(followOnQuestions);
@@ -538,7 +541,7 @@ var ChatPanel = ({
538
541
  });
539
542
  fetchAndSetTools();
540
543
  }, [mcpServers, publicAPIUrl]);
541
- const { send, response, idle, stop, lastCallId } = useLLM({
544
+ const { send, response, idle, stop, lastCallId, setResponse } = useLLM({
542
545
  project_id,
543
546
  customer: currentCustomer,
544
547
  url,
@@ -1976,7 +1979,7 @@ var ChatPanel = ({
1976
1979
  var _a2, _b;
1977
1980
  const isLastEntry = index === Object.keys(history).length - 1;
1978
1981
  const hasToolData = !!((((_a2 = historyEntry == null ? void 0 : historyEntry.toolCalls) == null ? void 0 : _a2.length) || 0) > 0 || (((_b = historyEntry == null ? void 0 : historyEntry.toolResponses) == null ? void 0 : _b.length) || 0) > 0);
1979
- return /* @__PURE__ */ React3.createElement("div", { className: "history-entry", key: index }, hideInitialPrompt && index === 0 ? null : /* @__PURE__ */ React3.createElement("div", { className: "prompt" }, formatPromptForDisplay(prompt)), /* @__PURE__ */ React3.createElement("div", { className: "response" }, index === Object.keys(history).length - 1 && (isLoading || !idle) ? /* @__PURE__ */ React3.createElement("div", { className: "streaming-response" }, (() => {
1982
+ return /* @__PURE__ */ React3.createElement("div", { className: "history-entry", key: index }, hideInitialPrompt && index === 0 ? null : /* @__PURE__ */ React3.createElement("div", { className: "prompt" }, formatPromptForDisplay(prompt)), /* @__PURE__ */ React3.createElement("div", { className: "response" }, index === Object.keys(history).length - 1 && (isLoading || !idle) && !justReset ? /* @__PURE__ */ React3.createElement("div", { className: "streaming-response" }, (() => {
1980
1983
  const { cleanedText } = processThinkingTags(
1981
1984
  response || ""
1982
1985
  );
@@ -2221,7 +2224,61 @@ var ChatPanel = ({
2221
2224
  },
2222
2225
  "\u270E"
2223
2226
  ))),
2224
- /* @__PURE__ */ React3.createElement("div", { className: "button-container-actions" }, showSaveButton && /* @__PURE__ */ React3.createElement(
2227
+ /* @__PURE__ */ React3.createElement("div", { className: "button-container-actions" }, showNewConversationButton && /* @__PURE__ */ React3.createElement(
2228
+ "button",
2229
+ {
2230
+ className: `save-button new-conversation-button ${newConversationConfirm ? "confirm-state" : ""}`,
2231
+ onClick: () => {
2232
+ if (!newConversationConfirm) {
2233
+ setNewConversationConfirm(true);
2234
+ setTimeout(() => {
2235
+ setNewConversationConfirm(false);
2236
+ }, 3e3);
2237
+ } else {
2238
+ setNewConversationConfirm(false);
2239
+ if (!idle) {
2240
+ stop(lastController);
2241
+ }
2242
+ setResponse("");
2243
+ setHistory({});
2244
+ setLastMessages([]);
2245
+ setNextPrompt("");
2246
+ setLastPrompt(null);
2247
+ setLastKey(null);
2248
+ setIsLoading(false);
2249
+ setCurrentConversation(null);
2250
+ setCallToActionSent(false);
2251
+ setEmailSent(false);
2252
+ setCTAClickedButNoEmail(false);
2253
+ setEmailClickedButNoEmail(false);
2254
+ setFollowOnQuestionsState(followOnQuestions);
2255
+ setSessionApprovedTools([]);
2256
+ setThinkingBlocks([]);
2257
+ setCurrentThinkingIndex(0);
2258
+ setPendingButtonAttachments([]);
2259
+ setPendingToolRequests([]);
2260
+ setIsEmailModalOpen(false);
2261
+ setIsToolInfoModalOpen(false);
2262
+ setToolInfoData(null);
2263
+ setJustReset(true);
2264
+ setLastController(new AbortController());
2265
+ buttonActionRegistry.current.clear();
2266
+ setTimeout(() => {
2267
+ setJustReset(false);
2268
+ const responseArea = responseAreaRef.current;
2269
+ if (responseArea) {
2270
+ responseArea.scrollTo({
2271
+ top: 0,
2272
+ behavior: "smooth"
2273
+ });
2274
+ }
2275
+ }, 100);
2276
+ }
2277
+ },
2278
+ title: newConversationConfirm ? "Click again to confirm reset" : "Start a new conversation"
2279
+ },
2280
+ newConversationConfirm ? "Click to Confirm" : "New Conversation"
2281
+ ), showSaveButton && /* @__PURE__ */ React3.createElement(
2225
2282
  "button",
2226
2283
  {
2227
2284
  className: "save-button",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llmasaservice-ui",
3
- "version": "0.12.39",
3
+ "version": "0.13.0",
4
4
  "description": "Prebuilt UI components for LLMAsAService.io",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -47,7 +47,7 @@
47
47
  "dependencies": {
48
48
  "@modelcontextprotocol/sdk": "^1.10.2",
49
49
  "dotenv": "^16.4.7",
50
- "llmasaservice-client": "^0.10.0",
50
+ "llmasaservice-client": "^0.10.1",
51
51
  "process": "^0.11.10",
52
52
  "react-markdown": "^9.0.1",
53
53
  "react-syntax-highlighter": "^15.5.0",
package/src/ChatPanel.css CHANGED
@@ -412,6 +412,38 @@
412
412
  color: var(--button-text-color-hover);
413
413
  }
414
414
 
415
+ /* New Conversation Button - distinct styling */
416
+ .llm-panel .new-conversation-button {
417
+ background-color: var(--prompt-background-color);
418
+ border-color: var(--button-border-color);
419
+ font-weight: 500;
420
+ }
421
+
422
+ .llm-panel .new-conversation-button:hover {
423
+ background-color: var(--button-background-color-hover);
424
+ color: var(--button-text-color-hover);
425
+ transform: translateY(-1px);
426
+ }
427
+
428
+ /* Confirmation state styling */
429
+ .llm-panel .new-conversation-button.confirm-state {
430
+ background-color: #ff6b35;
431
+ color: white;
432
+ border-color: #e55a2b;
433
+ animation: pulse-confirm 1s infinite;
434
+ }
435
+
436
+ .llm-panel .new-conversation-button.confirm-state:hover {
437
+ background-color: #e55a2b;
438
+ color: white;
439
+ }
440
+
441
+ @keyframes pulse-confirm {
442
+ 0% { opacity: 1; }
443
+ 50% { opacity: 0.7; }
444
+ 100% { opacity: 1; }
445
+ }
446
+
415
447
  /* Suggestion Buttons */
416
448
  .llm-panel .suggestion-button {
417
449
  background-color: var(--button-background-color);
package/src/ChatPanel.tsx CHANGED
@@ -59,6 +59,7 @@ export interface ChatPanelProps {
59
59
  }[];
60
60
  showSaveButton?: boolean;
61
61
  showEmailButton?: boolean;
62
+ showNewConversationButton?: boolean;
62
63
  followOnQuestions?: string[];
63
64
  clearFollowOnQuestionsNextPrompt?: boolean;
64
65
  followOnPrompt?: string;
@@ -117,6 +118,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
117
118
  actions,
118
119
  showSaveButton = true,
119
120
  showEmailButton = true,
121
+ showNewConversationButton = true,
120
122
  followOnQuestions = [],
121
123
  clearFollowOnQuestionsNextPrompt = false,
122
124
  followOnPrompt = "",
@@ -179,6 +181,8 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
179
181
  const [currentCustomer, setCurrentCustomer] = useState<LLMAsAServiceCustomer>(
180
182
  customer as LLMAsAServiceCustomer
181
183
  );
184
+ const [justReset, setJustReset] = useState(false);
185
+ const [newConversationConfirm, setNewConversationConfirm] = useState(false);
182
186
  const [allActions, setAllActions] = useState<
183
187
  {
184
188
  pattern: string;
@@ -658,7 +662,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
658
662
  fetchAndSetTools();
659
663
  }, [mcpServers, publicAPIUrl]);
660
664
 
661
- const { send, response, idle, stop, lastCallId } = useLLM({
665
+ const { send, response, idle, stop, lastCallId, setResponse } = useLLM({
662
666
  project_id: project_id,
663
667
  customer: currentCustomer,
664
668
  url: url,
@@ -2485,7 +2489,7 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
2485
2489
  <div className="response">
2486
2490
  {/* Show streaming response with thinking blocks displayed separately */}
2487
2491
  {index === Object.keys(history).length - 1 &&
2488
- (isLoading || !idle) ? (
2492
+ (isLoading || !idle) && !justReset ? (
2489
2493
  <div className="streaming-response">
2490
2494
  {/* Display current thinking block or thinking message */}
2491
2495
  {(() => {
@@ -2850,6 +2854,78 @@ const ChatPanel: React.FC<ChatPanelProps & ExtraProps> = ({
2850
2854
  </>
2851
2855
  )}
2852
2856
  <div className="button-container-actions">
2857
+ {showNewConversationButton && (
2858
+ <button
2859
+ className={`save-button new-conversation-button ${newConversationConfirm ? 'confirm-state' : ''}`}
2860
+ onClick={() => {
2861
+ if (!newConversationConfirm) {
2862
+ // First click - show confirmation state
2863
+ setNewConversationConfirm(true);
2864
+
2865
+ // Auto-reset confirmation after 3 seconds
2866
+ setTimeout(() => {
2867
+ setNewConversationConfirm(false);
2868
+ }, 3000);
2869
+ } else {
2870
+ // Second click - proceed with reset
2871
+ setNewConversationConfirm(false);
2872
+
2873
+ // Stop any current request first
2874
+ if (!idle) {
2875
+ stop(lastController);
2876
+ }
2877
+
2878
+ // Clear the current response from useLLM hook
2879
+ setResponse("");
2880
+
2881
+ // Reset conversation state while preserving customer info
2882
+ setHistory({});
2883
+ setLastMessages([]);
2884
+ setNextPrompt("");
2885
+ setLastPrompt(null);
2886
+ setLastKey(null);
2887
+ setIsLoading(false);
2888
+ setCurrentConversation(null);
2889
+ setCallToActionSent(false);
2890
+ setEmailSent(false);
2891
+ setCTAClickedButNoEmail(false);
2892
+ setEmailClickedButNoEmail(false);
2893
+ setFollowOnQuestionsState(followOnQuestions);
2894
+ setSessionApprovedTools([]);
2895
+ setThinkingBlocks([]);
2896
+ setCurrentThinkingIndex(0);
2897
+ setPendingButtonAttachments([]);
2898
+ setPendingToolRequests([]);
2899
+ setIsEmailModalOpen(false);
2900
+ setIsToolInfoModalOpen(false);
2901
+ setToolInfoData(null);
2902
+ setJustReset(true);
2903
+
2904
+ // Create a new AbortController for future requests
2905
+ setLastController(new AbortController());
2906
+
2907
+ // Clear button registry for new conversation
2908
+ buttonActionRegistry.current.clear();
2909
+
2910
+ // Force a small delay to ensure all state updates are processed
2911
+ setTimeout(() => {
2912
+ setJustReset(false);
2913
+ // Scroll to top after state updates
2914
+ const responseArea = responseAreaRef.current as any;
2915
+ if (responseArea) {
2916
+ responseArea.scrollTo({
2917
+ top: 0,
2918
+ behavior: "smooth",
2919
+ });
2920
+ }
2921
+ }, 100);
2922
+ }
2923
+ }}
2924
+ title={newConversationConfirm ? "Click again to confirm reset" : "Start a new conversation"}
2925
+ >
2926
+ {newConversationConfirm ? "Click to Confirm" : "New Conversation"}
2927
+ </button>
2928
+ )}
2853
2929
  {showSaveButton && (
2854
2930
  <button
2855
2931
  className="save-button"
@@ -29,6 +29,34 @@ export const ChatPanelStory: Story = {
29
29
  },
30
30
  };
31
31
 
32
+ export const ChatPanelWithNewConversationButton: Story = {
33
+ args: {
34
+ title: "Chat Panel with New Conversation Button",
35
+ project_id: "[get this from your control panel]",
36
+ initialPrompt: "Hello! How can I help you today?",
37
+ hideInitialPrompt: false,
38
+ placeholder: "Type your message here...",
39
+ theme: "light",
40
+ messages: [
41
+ { role: "assistant", content: "Welcome! This conversation has some history." },
42
+ { role: "user", content: "Tell me about the weather" },
43
+ { role: "assistant", content: "I'd be happy to help with weather information. However, I don't have access to real-time weather data. You might want to check a weather service like Weather.com or use a weather app on your device." }
44
+ ],
45
+ width: "400px",
46
+ height: "600px",
47
+ showNewConversationButton: true,
48
+ showSaveButton: true,
49
+ showEmailButton: true,
50
+ initialMessage: "Try clicking the 'New Conversation' button - it requires two clicks to confirm the reset and prevent accidental conversation clearing!",
51
+ initialHistory: {
52
+ "Hello! How can I help you today?": {
53
+ content: "Welcome! This is a test conversation with some existing history. The 'New Conversation' button now requires double-click confirmation to prevent accidental resets.",
54
+ callId: "test-call-1"
55
+ }
56
+ }
57
+ },
58
+ };
59
+
32
60
  export const ChatPanelThumbsStory: Story = {
33
61
  args: {
34
62
  title: "Chat",