@smart-cloud/ai-kit-ui 1.3.7 → 1.3.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smart-cloud/ai-kit-ui",
3
- "version": "1.3.7",
3
+ "version": "1.3.8",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -140,6 +140,7 @@ const DocSearchBase: FC<Props> = (props) => {
140
140
  useAiRun<SearchResult>();
141
141
 
142
142
  const autoRunOnceRef = useRef(false);
143
+ const prevSelectedCategoriesRef = useRef<string[]>([]);
143
144
 
144
145
  const sessionId = result?.sessionId;
145
146
  const citationDocs = result?.citations?.docs ?? [];
@@ -188,6 +189,14 @@ const DocSearchBase: FC<Props> = (props) => {
188
189
  return Boolean((text && text.trim().length > 0) || audioBlob);
189
190
  }, [inputText, busy, audioBlob, featureOpen]);
190
191
 
192
+ const hasValidFilterOptions = useMemo(() => {
193
+ if (!metadataOptions) return false;
194
+ const hasCategories =
195
+ Object.keys(metadataOptions.allowedCategories).length > 0;
196
+ const hasTags = metadataOptions.allowedTags.length > 0;
197
+ return hasCategories || hasTags;
198
+ }, [metadataOptions]);
199
+
191
200
  const startRecording = useCallback(async () => {
192
201
  try {
193
202
  // Clear query input when starting audio recording
@@ -405,10 +414,11 @@ const DocSearchBase: FC<Props> = (props) => {
405
414
  ...(audioBlob && { audio: audioBlob }), // Pass Blob directly
406
415
  topK,
407
416
  // Include user-selected filters if enabled
408
- ...(enableUserFilters &&
409
- selectedCategories.length > 0 && {
410
- userSelectedCategories: selectedCategories,
411
- }),
417
+ // Always send userSelectedCategories array when enableUserFilters is true (even if empty)
418
+ // to prevent backend from applying its own kb-filter
419
+ ...(enableUserFilters && {
420
+ userSelectedCategories: selectedCategories,
421
+ }),
412
422
  ...(enableUserFilters &&
413
423
  selectedSubcategories.length > 0 && {
414
424
  userSelectedSubcategories: selectedSubcategories,
@@ -458,6 +468,24 @@ const DocSearchBase: FC<Props> = (props) => {
458
468
  }
459
469
  }, [canSearch]);
460
470
 
471
+ // Reset session when main categories change
472
+ useEffect(() => {
473
+ const prev = prevSelectedCategoriesRef.current;
474
+ const current = selectedCategories;
475
+
476
+ // Check if categories changed (different length or different items)
477
+ const categoriesChanged =
478
+ prev.length !== current.length ||
479
+ !current.every((cat) => prev.includes(cat));
480
+
481
+ if (categoriesChanged && prev.length > 0) {
482
+ // Reset session only if we had categories before (not on initial mount)
483
+ reset();
484
+ }
485
+
486
+ prevSelectedCategoriesRef.current = [...current];
487
+ }, [selectedCategories, reset]);
488
+
461
489
  const grouped = useMemo(() => groupChunksByDoc(result), [result]);
462
490
 
463
491
  const docNumberMap = useMemo(() => {
@@ -760,110 +788,66 @@ const DocSearchBase: FC<Props> = (props) => {
760
788
  ) : null}
761
789
 
762
790
  {/* User filter collapse */}
763
- {enableUserFilters && metadataOptions && (
764
- <Stack gap="xs">
765
- <Button
766
- variant="subtle"
767
- size="xs"
768
- onClick={() => setFiltersOpen(!filtersOpen)}
769
- leftSection={
770
- filtersOpen ? (
771
- <IconChevronDown size={14} />
772
- ) : (
773
- <IconChevronRight size={14} />
774
- )
775
- }
776
- style={{ alignSelf: "flex-start" }}
777
- >
778
- {I18n.get("Filters")}
779
- </Button>
780
-
781
- <Collapse in={filtersOpen}>
782
- <Stack gap="md">
783
- {/* Main categories as checkboxes */}
784
- {Object.keys(metadataOptions.allowedCategories)
785
- .length > 0 && (
786
- <div>
787
- <Text size="sm" fw={500} mb="xs">
788
- {I18n.get("Categories")}
789
- </Text>
790
- <Group gap="md">
791
- {Object.keys(
792
- metadataOptions.allowedCategories,
793
- ).map((category) => (
794
- <Checkbox
795
- key={category}
796
- label={category}
797
- checked={selectedCategories.includes(
798
- category,
799
- )}
800
- onChange={(e) => {
801
- if (e.currentTarget.checked) {
802
- setSelectedCategories([
803
- ...selectedCategories,
804
- category,
805
- ]);
806
- } else {
807
- setSelectedCategories(
808
- selectedCategories.filter(
809
- (c) => c !== category,
810
- ),
811
- );
812
- // Remove subcategories of unchecked category
813
- const subcatsToRemove =
814
- metadataOptions.allowedCategories[
815
- category
816
- ] || [];
817
- setSelectedSubcategories(
818
- selectedSubcategories.filter(
819
- (sc) =>
820
- !subcatsToRemove.includes(sc),
821
- ),
822
- );
823
- }
824
- }}
825
- disabled={busy || loadingMetadata}
826
- />
827
- ))}
828
- </Group>
829
- </div>
830
- )}
791
+ {enableUserFilters &&
792
+ metadataOptions &&
793
+ hasValidFilterOptions && (
794
+ <Stack gap="xs">
795
+ <Button
796
+ variant="subtle"
797
+ size="xs"
798
+ onClick={() => setFiltersOpen(!filtersOpen)}
799
+ leftSection={
800
+ filtersOpen ? (
801
+ <IconChevronDown size={14} />
802
+ ) : (
803
+ <IconChevronRight size={14} />
804
+ )
805
+ }
806
+ style={{ alignSelf: "flex-start" }}
807
+ >
808
+ {I18n.get("Filters")}
809
+ </Button>
831
810
 
832
- {/* Subcategories for selected categories */}
833
- {selectedCategories.length > 0 && (
834
- <div>
835
- <Text size="sm" fw={500} mb="xs">
836
- {I18n.get("Subcategories")}
837
- </Text>
838
- <Group gap="md">
839
- {selectedCategories
840
- .flatMap(
841
- (cat) =>
842
- metadataOptions.allowedCategories[
843
- cat
844
- ] || [],
845
- )
846
- .filter(
847
- (subcat, index, self) =>
848
- self.indexOf(subcat) === index,
849
- )
850
- .map((subcat) => (
811
+ <Collapse in={filtersOpen}>
812
+ <Stack gap="md">
813
+ {/* Main categories as checkboxes */}
814
+ {Object.keys(metadataOptions.allowedCategories)
815
+ .length > 0 && (
816
+ <div>
817
+ <Text size="sm" fw={500} mb="xs">
818
+ {I18n.get("Categories")}
819
+ </Text>
820
+ <Group gap="md">
821
+ {Object.keys(
822
+ metadataOptions.allowedCategories,
823
+ ).map((category) => (
851
824
  <Checkbox
852
- key={subcat}
853
- label={subcat}
854
- checked={selectedSubcategories.includes(
855
- subcat,
825
+ key={category}
826
+ label={I18n.get(category)}
827
+ checked={selectedCategories.includes(
828
+ category,
856
829
  )}
857
830
  onChange={(e) => {
858
831
  if (e.currentTarget.checked) {
859
- setSelectedSubcategories([
860
- ...selectedSubcategories,
861
- subcat,
832
+ setSelectedCategories([
833
+ ...selectedCategories,
834
+ category,
862
835
  ]);
863
836
  } else {
837
+ setSelectedCategories(
838
+ selectedCategories.filter(
839
+ (c) => c !== category,
840
+ ),
841
+ );
842
+ // Remove subcategories of unchecked category
843
+ const subcatsToRemove =
844
+ metadataOptions.allowedCategories[
845
+ category
846
+ ] || [];
864
847
  setSelectedSubcategories(
865
848
  selectedSubcategories.filter(
866
- (sc) => sc !== subcat,
849
+ (sc) =>
850
+ !subcatsToRemove.includes(sc),
867
851
  ),
868
852
  );
869
853
  }
@@ -871,31 +855,84 @@ const DocSearchBase: FC<Props> = (props) => {
871
855
  disabled={busy || loadingMetadata}
872
856
  />
873
857
  ))}
874
- </Group>
875
- </div>
876
- )}
877
-
878
- {/* Tags input */}
879
- {metadataOptions.allowedTags.length > 0 && (
880
- <MultiSelect
881
- label={I18n.get("Tags")}
882
- placeholder={I18n.get("Select or type tags...")}
883
- data={metadataOptions.allowedTags}
884
- value={selectedTags}
885
- onChange={setSelectedTags}
886
- searchValue={tagSearchValue}
887
- onSearchChange={setTagSearchValue}
888
- disabled={busy || loadingMetadata}
889
- searchable
890
- clearable
891
- maxDropdownHeight={200}
892
- limit={20}
893
- />
894
- )}
895
- </Stack>
896
- </Collapse>
897
- </Stack>
898
- )}
858
+ </Group>
859
+ </div>
860
+ )}
861
+
862
+ {/* Subcategories for selected categories */}
863
+ {selectedCategories.length > 0 && (
864
+ <div>
865
+ <Text size="sm" fw={500} mb="xs">
866
+ {I18n.get("Subcategories")}
867
+ </Text>
868
+ <Group gap="md">
869
+ {selectedCategories
870
+ .flatMap(
871
+ (cat) =>
872
+ metadataOptions.allowedCategories[
873
+ cat
874
+ ] || [],
875
+ )
876
+ .filter(
877
+ (subcat, index, self) =>
878
+ self.indexOf(subcat) === index,
879
+ )
880
+ .map((subcat) => (
881
+ <Checkbox
882
+ key={subcat}
883
+ label={I18n.get(subcat)}
884
+ checked={selectedSubcategories.includes(
885
+ subcat,
886
+ )}
887
+ onChange={(e) => {
888
+ if (e.currentTarget.checked) {
889
+ setSelectedSubcategories([
890
+ ...selectedSubcategories,
891
+ subcat,
892
+ ]);
893
+ } else {
894
+ setSelectedSubcategories(
895
+ selectedSubcategories.filter(
896
+ (sc) => sc !== subcat,
897
+ ),
898
+ );
899
+ }
900
+ }}
901
+ disabled={busy || loadingMetadata}
902
+ />
903
+ ))}
904
+ </Group>
905
+ </div>
906
+ )}
907
+
908
+ {/* Tags input */}
909
+ {metadataOptions.allowedTags.length > 0 && (
910
+ <MultiSelect
911
+ label={I18n.get("Tags")}
912
+ placeholder={I18n.get(
913
+ "Select or type tags...",
914
+ )}
915
+ data={metadataOptions.allowedTags.map(
916
+ (tag) => ({
917
+ value: tag,
918
+ label: I18n.get(tag),
919
+ }),
920
+ )}
921
+ value={selectedTags}
922
+ onChange={setSelectedTags}
923
+ searchValue={tagSearchValue}
924
+ onSearchChange={setTagSearchValue}
925
+ disabled={busy || loadingMetadata}
926
+ searchable
927
+ clearable
928
+ maxDropdownHeight={200}
929
+ limit={20}
930
+ />
931
+ )}
932
+ </Stack>
933
+ </Collapse>
934
+ </Stack>
935
+ )}
899
936
 
900
937
  {
901
938
  /* Audio level indicator when recording */ USE_AUDIO && (