@brainfish-ai/components 0.25.4 → 0.26.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.
@@ -112,7 +112,7 @@ declare interface AnswerAttachment {
112
112
 
113
113
  declare type AnswerBlock = MarkdownTextBlock | ActionButtonsBlock | ActionInputFormBlock;
114
114
 
115
- declare type AnswerListAction = SetAnswers | ClearAll | AppendNewAnswer | SetSearchResults | AppendAnswerChunk | SetAnswerError | SetUserFeedback | ClearUserFeedback | SetFollowUpQuestions | AppendBlock | CompleteAnswer | MarkAsUncertain | InvokeAction | GetActionInputs | AddActionButtons | NoArticlesFound;
115
+ declare type AnswerListAction = SetAnswers | ClearAll | AppendNewAnswer | SetSearchResults | AppendAnswerChunk | SetAnswerError | SetUserFeedback | ClearUserFeedback | SetFollowUpQuestions | AppendBlock | CompleteAnswer | MarkAsUncertain | InvokeAction | GetActionInputs | AddActionButtons | NoArticlesFound | SetAwaitingKnowledgeGroup | SetKnowledgeGroupSelected;
116
116
 
117
117
  declare const AnswersActions: {
118
118
  readonly SET_ANSWERS: "answers/set_answers";
@@ -131,9 +131,11 @@ declare const AnswersActions: {
131
131
  readonly GET_ACTION_INPUTS: "answers/request_action_inputs";
132
132
  readonly INVOKE_ACTION: "answers/invoke_action";
133
133
  readonly ADD_ACTION_BUTTONS: "answers/add_action_buttons";
134
+ readonly SET_AWAITING_KNOWLEDGE_GROUP: "answers/set_awaiting_knowledge_group";
135
+ readonly SET_KNOWLEDGE_GROUP_SELECTED: "answers/set_knowledge_group_selected";
134
136
  };
135
137
 
136
- declare type AnswerState = 'fetching-search-results' | 'fetching-stream' | 'streaming' | 'getting-action-inputs' | 'completed';
138
+ declare type AnswerState = 'fetching-search-results' | 'fetching-stream' | 'streaming' | 'getting-action-inputs' | 'awaiting-knowledge-group' | 'completed';
137
139
 
138
140
  declare type AppendAnswerChunk = {
139
141
  type: typeof AnswersActions.APPEND_ANSWER_CHUNK;
@@ -255,6 +257,8 @@ export declare interface ChatSearchProps {
255
257
  isSearchWidget?: boolean;
256
258
  hideCitations?: boolean;
257
259
  conversationInlineSlot?: ReactNode;
260
+ liveHumanAgentChatSlot?: ReactNode;
261
+ knowledgeSourceGroups?: KnowledgeSourceGroup[];
258
262
  }
259
263
 
260
264
  export declare const ChatSearchProvider: default_2.FC<{
@@ -333,6 +337,12 @@ declare type InvokeAction = {
333
337
 
334
338
  declare type JSONSchema<T> = JSONSchemaType<T>;
335
339
 
340
+ export declare interface KnowledgeSourceGroup {
341
+ id: string;
342
+ name: string;
343
+ collectionIds: string[];
344
+ }
345
+
336
346
  declare type MarkAsUncertain = {
337
347
  type: typeof AnswersActions.MARK_AS_UNCERTAIN;
338
348
  payload: {
@@ -405,6 +415,13 @@ declare type SetAnswers = {
405
415
  };
406
416
  };
407
417
 
418
+ declare type SetAwaitingKnowledgeGroup = {
419
+ type: typeof AnswersActions.SET_AWAITING_KNOWLEDGE_GROUP;
420
+ payload: {
421
+ index?: number;
422
+ };
423
+ };
424
+
408
425
  declare type SetFollowUpQuestions = {
409
426
  type: typeof AnswersActions.SET_FOLLOW_UP_QUESTIONS;
410
427
  payload: {
@@ -413,6 +430,13 @@ declare type SetFollowUpQuestions = {
413
430
  };
414
431
  };
415
432
 
433
+ declare type SetKnowledgeGroupSelected = {
434
+ type: typeof AnswersActions.SET_KNOWLEDGE_GROUP_SELECTED;
435
+ payload: {
436
+ index?: number;
437
+ };
438
+ };
439
+
416
440
  declare type SetSearchResults = {
417
441
  type: typeof AnswersActions.SET_SEARCH_RESULTS;
418
442
  payload: {
@@ -29,7 +29,7 @@ import { C as Combobox } from './combobox.CJKym3Z1.js';
29
29
  import { T as TwoLevelCombobox } from './two-level-combobox.BXs2z9u5.js';
30
30
  import { u as useBooleanFlagValue, F as FeatureFlagProvider } from './feature-flags.DeDEcnd1.js';
31
31
  import { X as X$1 } from 'lucide-react';
32
- import { l as loadConversation, c as createConversationId, a as uploadFileToApi, b as useAutocomplete, s as sendFeedbackReason, d as sendFeedback, e as searchApi, f as fetchAnswerStream, g as fetchFollowUpQuestions } from './hooks.m-nIJmio.js';
32
+ import { l as loadConversation, c as createConversationId, a as uploadFileToApi, b as useAutocomplete, s as sendFeedbackReason, d as sendFeedback, e as searchApi, f as fetchAnswerStream, g as fetchFollowUpQuestions } from './hooks.BWVaVAT-.js';
33
33
  import { ScrollArea } from '../components/ui/scroll-area.js';
34
34
 
35
35
  import '../ChatSearch.css';function Suggestions({ suggestions, onQuestionClick, title = "Suggested questions" }) {
@@ -3509,7 +3509,10 @@ const AnswersActions = {
3509
3509
  // action flow
3510
3510
  GET_ACTION_INPUTS: "answers/request_action_inputs",
3511
3511
  INVOKE_ACTION: "answers/invoke_action",
3512
- ADD_ACTION_BUTTONS: "answers/add_action_buttons"
3512
+ ADD_ACTION_BUTTONS: "answers/add_action_buttons",
3513
+ // knowledge group clarification
3514
+ SET_AWAITING_KNOWLEDGE_GROUP: "answers/set_awaiting_knowledge_group",
3515
+ SET_KNOWLEDGE_GROUP_SELECTED: "answers/set_knowledge_group_selected"
3513
3516
  };
3514
3517
  const setAnswers = ({ answers }) => ({
3515
3518
  type: AnswersActions.SET_ANSWERS,
@@ -3599,6 +3602,18 @@ const noArticlesFound = ({ index } = {}) => ({
3599
3602
  index
3600
3603
  }
3601
3604
  });
3605
+ const setAwaitingKnowledgeGroup = ({ index } = {}) => ({
3606
+ type: AnswersActions.SET_AWAITING_KNOWLEDGE_GROUP,
3607
+ payload: {
3608
+ index
3609
+ }
3610
+ });
3611
+ const setKnowledgeGroupSelected = ({ index } = {}) => ({
3612
+ type: AnswersActions.SET_KNOWLEDGE_GROUP_SELECTED,
3613
+ payload: {
3614
+ index
3615
+ }
3616
+ });
3602
3617
  const getActionInputs = ({
3603
3618
  searchIntentId,
3604
3619
  actionId,
@@ -3789,6 +3804,20 @@ const reducer = (draft, action) => {
3789
3804
  }
3790
3805
  return;
3791
3806
  }
3807
+ case AnswersActions.SET_AWAITING_KNOWLEDGE_GROUP: {
3808
+ const answer = getTargetAnswer(draft, action.payload.index);
3809
+ if (answer) {
3810
+ answer.state = "awaiting-knowledge-group";
3811
+ }
3812
+ return;
3813
+ }
3814
+ case AnswersActions.SET_KNOWLEDGE_GROUP_SELECTED: {
3815
+ const answer = getTargetAnswer(draft, action.payload.index);
3816
+ if (answer) {
3817
+ answer.state = "fetching-search-results";
3818
+ }
3819
+ return;
3820
+ }
3792
3821
  default: {
3793
3822
  throw new Error(`Answer List Reducer: unknown action type '${action.type}'`);
3794
3823
  }
@@ -3975,6 +4004,24 @@ function AttachmentThumbnails({ items, size = "sm", onRemove, className }) {
3975
4004
  ))));
3976
4005
  }
3977
4006
 
4007
+ const MotionButton$2 = motion(Button);
4008
+ function KnowledgeGroupSelector({ groups, onGroupSelect }) {
4009
+ if (!groups.length) return null;
4010
+ return /* @__PURE__ */ React__default.createElement(motion.div, { initial: { opacity: 0, y: 20 }, animate: { opacity: 1, y: 0 }, className: "w-full px-4 py-3" }, /* @__PURE__ */ React__default.createElement("p", { className: "text-sm text-muted-foreground mb-2 text-right" }, "Please select an option"), /* @__PURE__ */ React__default.createElement("div", { className: "flex flex-wrap justify-end gap-2" }, groups.map((group, index) => /* @__PURE__ */ React__default.createElement(
4011
+ MotionButton$2,
4012
+ {
4013
+ key: group.id,
4014
+ initial: { opacity: 0, scale: 0.95 },
4015
+ animate: { opacity: 1, scale: 1 },
4016
+ transition: { delay: index * 0.05 },
4017
+ onClick: () => onGroupSelect(group),
4018
+ variant: "outline",
4019
+ className: "border-border font-normal h-auto rounded-full px-4 py-1.5 text-sm"
4020
+ },
4021
+ group.name
4022
+ ))));
4023
+ }
4024
+
3978
4025
  var ActionType = /* @__PURE__ */ ((ActionType2) => {
3979
4026
  ActionType2["CallPhone"] = "call_phone";
3980
4027
  ActionType2["OpenUrl"] = "open_url";
@@ -4748,15 +4795,18 @@ function Answer({
4748
4795
  nextBestActions,
4749
4796
  isLastAnswer,
4750
4797
  onFeedbackReasonSubmit,
4751
- hasError = false
4798
+ hasError = false,
4799
+ knowledgeSourceGroups,
4800
+ onKnowledgeGroupSelect
4752
4801
  }) {
4753
4802
  const [isCopied, setIsCopied] = useState(false);
4754
4803
  const [isExpanded, setIsExpanded] = useState(false);
4755
4804
  const answer = blocksToPlainText(blocks);
4805
+ const isAwaitingKnowledgeGroup = state === "awaiting-knowledge-group";
4756
4806
  const isFetching = state === "fetching-search-results" || state === "fetching-stream";
4757
4807
  const isFetchingInitialAnswer = isFetching && blocks.length === 1;
4758
4808
  const isFetchingSearchResults = state === "fetching-search-results";
4759
- const isIdle = state === "getting-action-inputs" || state === "completed";
4809
+ const isIdle = state === "getting-action-inputs" || state === "completed" || isAwaitingKnowledgeGroup;
4760
4810
  const isCompleted = state === "completed";
4761
4811
  const showError = hasError && isCompleted;
4762
4812
  const handleCopy = async () => {
@@ -4802,7 +4852,7 @@ function Answer({
4802
4852
  }
4803
4853
  )),
4804
4854
  isFetchingInitialAnswer && /* @__PURE__ */ React__default.createElement("div", { className: "px-4 pt-0 pb-2" }, /* @__PURE__ */ React__default.createElement("p", { className: "text-sm text-muted-foreground" }, isFetchingSearchResults ? textConfig.loadingSearchText : textConfig.loadingAnswerText)),
4805
- /* @__PURE__ */ React__default.createElement("div", { className: "flex flex-col" }, blocks?.map((block, i) => /* @__PURE__ */ React__default.createElement(
4855
+ isAwaitingKnowledgeGroup && knowledgeSourceGroups && onKnowledgeGroupSelect ? /* @__PURE__ */ React__default.createElement(KnowledgeGroupSelector, { groups: knowledgeSourceGroups, onGroupSelect: onKnowledgeGroupSelect }) : /* @__PURE__ */ React__default.createElement("div", { className: "flex flex-col" }, blocks?.map((block, i) => /* @__PURE__ */ React__default.createElement(
4806
4856
  "div",
4807
4857
  {
4808
4858
  key: i,
@@ -5421,7 +5471,8 @@ const FollowUpSearchBar = React__default.forwardRef(
5421
5471
  attachedFiles = [],
5422
5472
  isUploading = false,
5423
5473
  onAddFiles,
5424
- onRemoveFile
5474
+ onRemoveFile,
5475
+ disabled = false
5425
5476
  }, ref) => {
5426
5477
  const [localQuery, setLocalQuery] = useState(initialQuery);
5427
5478
  const [isDragOver, setIsDragOver] = useState(false);
@@ -5444,7 +5495,7 @@ const FollowUpSearchBar = React__default.forwardRef(
5444
5495
  useEffect(() => {
5445
5496
  setLocalQuery(initialQuery);
5446
5497
  }, [initialQuery]);
5447
- const isSendDisabled = isSearching || !localQuery.trim() || isUploading;
5498
+ const isSendDisabled = disabled || isSearching || !localQuery.trim() || isUploading;
5448
5499
  const handleKeyPress = (e) => {
5449
5500
  if (e.key === "Enter" && !e.shiftKey && !isSendDisabled) {
5450
5501
  e.preventDefault();
@@ -5551,7 +5602,7 @@ const FollowUpSearchBar = React__default.forwardRef(
5551
5602
  size: "icon",
5552
5603
  className: "rounded-md size-7 hover:bg-opacity-80 disabled:opacity-50 shrink-0",
5553
5604
  onClick: () => fileInputRef.current?.click(),
5554
- disabled: attachedFiles.length >= 5,
5605
+ disabled: disabled || attachedFiles.length >= 5,
5555
5606
  "aria-label": textConfig.attachImageText
5556
5607
  },
5557
5608
  /* @__PURE__ */ React__default.createElement(Image, { weight: "regular", className: "size-4 text-secondary-foreground" })
@@ -5564,7 +5615,8 @@ const FollowUpSearchBar = React__default.forwardRef(
5564
5615
  onBlur: handleBlur,
5565
5616
  onKeyDown: handleKeyPress,
5566
5617
  placeholder: textConfig.followUpPlaceholder,
5567
- className: "min-h-[44px] w-full resize-none border-0 shadow-none bg-transparent px-0 py-2.5 focus:outline-none focus:ring-0 focus-visible:ring-0",
5618
+ disabled,
5619
+ className: "min-h-11 w-full resize-none border-0 shadow-none bg-transparent px-0 py-2.5 focus:outline-none focus:ring-0 focus-visible:ring-0 disabled:opacity-50 disabled:cursor-not-allowed",
5568
5620
  rows: 1
5569
5621
  }
5570
5622
  ), /* @__PURE__ */ React__default.createElement(
@@ -5838,7 +5890,9 @@ const ChatSearchComponent = forwardRef(
5838
5890
  isSearchWidget = false,
5839
5891
  isAgentAssist = false,
5840
5892
  hideCitations = false,
5841
- conversationInlineSlot
5893
+ conversationInlineSlot,
5894
+ liveHumanAgentChatSlot,
5895
+ knowledgeSourceGroups
5842
5896
  }, ref) => {
5843
5897
  const {
5844
5898
  answers,
@@ -5880,6 +5934,8 @@ const ChatSearchComponent = forwardRef(
5880
5934
  const [currentCollectionId, setCurrentCollectionId] = useState(selectedCollectionId);
5881
5935
  const [currentRegion, setCurrentRegion] = useState(selectedRegion);
5882
5936
  const [showFollowUp, setShowFollowUp] = useState(true);
5937
+ const selectedKnowledgeGroupRef = useRef(null);
5938
+ const pendingSearchRef = useRef(null);
5883
5939
  const primaryTextareaRef = useRef(null);
5884
5940
  const answerRefs = useRef([]);
5885
5941
  const containerRef = useRef(null);
@@ -6164,12 +6220,14 @@ const ChatSearchComponent = forwardRef(
6164
6220
  conversationId,
6165
6221
  attributes,
6166
6222
  secretAttributes: secretAttributes2,
6167
- allowedRegions: allowedRegions2
6223
+ allowedRegions: allowedRegions2,
6224
+ knowledgeGroupCollectionIds
6168
6225
  }) => {
6226
+ const collectionId = knowledgeGroupCollectionIds ?? currentCollectionId;
6169
6227
  const searchResponse = await searchApi({
6170
6228
  endpoint: searchEndpoint,
6171
6229
  query: searchQuery,
6172
- collectionId: currentCollectionId,
6230
+ collectionId,
6173
6231
  headers,
6174
6232
  conversationId,
6175
6233
  attributes,
@@ -6208,14 +6266,31 @@ const ChatSearchComponent = forwardRef(
6208
6266
  await Promise.resolve();
6209
6267
  followUpSearchRef.current?.focus();
6210
6268
  const pageContext = await fetchPageContext(conversationId, searchQuery);
6269
+ const needsGroupSelection = knowledgeSourceGroups && knowledgeSourceGroups.length > 0 && !selectedKnowledgeGroupRef.current;
6270
+ if (needsGroupSelection) {
6271
+ pendingSearchRef.current = {
6272
+ searchQuery,
6273
+ conversationId,
6274
+ attributes: { ...userData },
6275
+ secretAttributes,
6276
+ allowedRegions,
6277
+ pageContext: pageContext ?? void 0,
6278
+ fileAttachments: fileAttachments.length > 0 ? fileAttachments : void 0
6279
+ };
6280
+ answerListDispatch(setAwaitingKnowledgeGroup());
6281
+ setIsSearching(false);
6282
+ return;
6283
+ }
6211
6284
  try {
6212
6285
  const attributes = { ...userData };
6286
+ const knowledgeGroupCollectionIds = selectedKnowledgeGroupRef.current?.collectionIds;
6213
6287
  const searchQueryId = await handleSearchApiCall({
6214
6288
  searchQuery,
6215
6289
  conversationId,
6216
6290
  attributes,
6217
6291
  secretAttributes,
6218
- allowedRegions
6292
+ allowedRegions,
6293
+ knowledgeGroupCollectionIds
6219
6294
  });
6220
6295
  if (searchQueryId) {
6221
6296
  event && trackEvent?.(event.name, {
@@ -6249,6 +6324,50 @@ const ChatSearchComponent = forwardRef(
6249
6324
  setIsSearching(false);
6250
6325
  }
6251
6326
  };
6327
+ const handleKnowledgeGroupSelect = async (group) => {
6328
+ selectedKnowledgeGroupRef.current = group;
6329
+ const pending = pendingSearchRef.current;
6330
+ if (!pending) return;
6331
+ pendingSearchRef.current = null;
6332
+ answerListDispatch(setKnowledgeGroupSelected());
6333
+ setIsSearching(true);
6334
+ try {
6335
+ const searchQueryId = await handleSearchApiCall({
6336
+ searchQuery: pending.searchQuery,
6337
+ conversationId: pending.conversationId,
6338
+ attributes: pending.attributes,
6339
+ secretAttributes: pending.secretAttributes,
6340
+ allowedRegions: pending.allowedRegions,
6341
+ knowledgeGroupCollectionIds: group.collectionIds
6342
+ });
6343
+ if (searchQueryId) {
6344
+ trackEvent?.("Knowledge Group Selected", {
6345
+ conversationId: pending.conversationId,
6346
+ searchQuery: pending.searchQuery,
6347
+ searchQueryId,
6348
+ knowledgeGroupId: group.id,
6349
+ knowledgeGroupName: group.name
6350
+ });
6351
+ await generateAnswerForQuery({
6352
+ searchQueryId,
6353
+ conversationId: pending.conversationId,
6354
+ context: pending.pageContext,
6355
+ attachments: pending.fileAttachments
6356
+ });
6357
+ } else {
6358
+ answerListDispatch(noArticlesFound());
6359
+ }
6360
+ } catch (error) {
6361
+ console.error("Error generating answer after group selection:", error);
6362
+ answerListDispatch(
6363
+ setAnswerError({
6364
+ error: mergedTextConfig.errorText || "Sorry, we were unable to process the request right now. Please try again in a moment."
6365
+ })
6366
+ );
6367
+ } finally {
6368
+ setIsSearching(false);
6369
+ }
6370
+ };
6252
6371
  const createNewThread = (firstQuery) => {
6253
6372
  if (currentReaderRef.current) {
6254
6373
  const reader = currentReaderRef.current;
@@ -6263,6 +6382,8 @@ const ChatSearchComponent = forwardRef(
6263
6382
  setActiveAnswerIndex(0);
6264
6383
  clearAttachedFiles();
6265
6384
  clearCurrentConversationId();
6385
+ selectedKnowledgeGroupRef.current = null;
6386
+ pendingSearchRef.current = null;
6266
6387
  trackEvent?.("New Thread Created", { oldConversationId: currentConversationId });
6267
6388
  if (primaryTextareaRef.current) {
6268
6389
  primaryTextareaRef.current.style.height = "auto";
@@ -6614,7 +6735,9 @@ const ChatSearchComponent = forwardRef(
6614
6735
  handleActionClick: handleNextBestActionClick,
6615
6736
  isLastAnswer: i === answers.length - 1,
6616
6737
  onFeedbackReasonSubmit: (reason) => void handleFeedbackReasonSubmit(reason, answer.searchQueryId),
6617
- hasError: answer.hasError
6738
+ hasError: answer.hasError,
6739
+ knowledgeSourceGroups,
6740
+ onKnowledgeGroupSelect: (group) => void handleKnowledgeGroupSelect(group)
6618
6741
  }
6619
6742
  ),
6620
6743
  i === answers.length - 1 && !disableFollowUpQuestions && answer.followUpQuestions && answer.state === "completed" && !answer.hasError && /* @__PURE__ */ React__default.createElement(
@@ -6662,22 +6785,27 @@ const ChatSearchComponent = forwardRef(
6662
6785
  conversationInlineSlot != null && /* @__PURE__ */ React__default.createElement("div", { className: "-mt-6 w-full max-w-[100vw] px-3 pb-0 pt-0 text-foreground md:px-0" }, conversationInlineSlot),
6663
6786
  /* @__PURE__ */ React__default.createElement("div", { ref: bottomRef })
6664
6787
  ),
6665
- showResults && showFollowUp && /* @__PURE__ */ React__default.createElement(
6666
- FollowUpSearchBar,
6667
- {
6668
- ref: followUpSearchRef,
6669
- initialQuery: followUpQuery,
6670
- onQueryChange: setFollowUpQuery,
6671
- isSearching,
6672
- handleFollowUpSearch,
6673
- textConfig: mergedTextConfig,
6674
- enableFileAttachment,
6675
- attachedFiles,
6676
- isUploading,
6677
- onAddFiles: addFiles,
6678
- onRemoveFile: removeFile
6679
- }
6680
- )
6788
+ showResults && showFollowUp && !liveHumanAgentChatSlot && (() => {
6789
+ const isAwaitingGroup = answers.some((a) => a.state === "awaiting-knowledge-group");
6790
+ return /* @__PURE__ */ React__default.createElement(
6791
+ FollowUpSearchBar,
6792
+ {
6793
+ ref: followUpSearchRef,
6794
+ initialQuery: followUpQuery,
6795
+ onQueryChange: setFollowUpQuery,
6796
+ isSearching,
6797
+ handleFollowUpSearch,
6798
+ textConfig: mergedTextConfig,
6799
+ enableFileAttachment,
6800
+ attachedFiles,
6801
+ isUploading,
6802
+ onAddFiles: addFiles,
6803
+ onRemoveFile: removeFile,
6804
+ disabled: isAwaitingGroup
6805
+ }
6806
+ );
6807
+ })(),
6808
+ liveHumanAgentChatSlot
6681
6809
  );
6682
6810
  }
6683
6811
  );
@@ -6694,4 +6822,4 @@ const ChatSearch = forwardRef(({ featureFlags, ...props }, ref) => /* @__PURE__
6694
6822
  ChatSearch.displayName = "ChatSearch";
6695
6823
 
6696
6824
  export { ChatSearch as C, ChatSearchProvider as a, useIsChatSearchDirty as b, useChatSearch as u };
6697
- //# sourceMappingURL=ChatSearch.CL2VnSod.js.map
6825
+ //# sourceMappingURL=ChatSearch.CeQrTOVx.js.map