mnm-matin commited on
Commit
eac3a0d
·
verified ·
1 Parent(s): bd1fb8c

Fix DeepFashion demo sample panel flow

Browse files
.hyperview/extensions/fashion-search-readout/panel.js CHANGED
@@ -6,7 +6,6 @@ if (!sdk) throw new Error("HyperViewPanelSDK is not available on window.");
6
  const { React, components, hooks } = sdk;
7
  const { Panel, PanelToolbar, PanelToolbarButton } = components;
8
  const {
9
- usePanelClient,
10
  usePanelSelection,
11
  usePanelSamples,
12
  usePanelCommands,
@@ -607,7 +606,6 @@ function BenchmarkDetails({ typedMetricRows, imageMetricRows }) {
607
  }
608
 
609
  export default function FashionSearchComparisonPanel() {
610
- const client = usePanelClient();
611
  const selection = usePanelSelection();
612
  const samplesState = usePanelSamples();
613
  const commands = usePanelCommands();
@@ -619,8 +617,9 @@ export default function FashionSearchComparisonPanel() {
619
  );
620
  const [contentWidth, setContentWidth] = React.useState(0);
621
  const resultsContentRef = React.useRef(null);
622
- const didApplyDemoLayout = React.useRef(false);
623
  const didPrimeEvidence = React.useRef(false);
 
 
624
  const mode = panelProps.mode === "summary" ? "summary" : panelProps.mode === "results" ? "results" : "full";
625
 
626
  const models = React.useMemo(() => normalizeModels(panelProps.models), [panelProps.models]);
@@ -646,16 +645,6 @@ export default function FashionSearchComparisonPanel() {
646
  { label: "Image-to-image mAP", hyper3: metrics.imageRetrievalMapHyper3, clip: metrics.imageRetrievalMapClip },
647
  ];
648
 
649
- React.useEffect(() => {
650
- const handleExampleChange = (event) => {
651
- if (typeof event.detail === "string") {
652
- setSelectedExampleId(event.detail);
653
- }
654
- };
655
- window.addEventListener("fashion-search-example-change", handleExampleChange);
656
- return () => window.removeEventListener("fashion-search-example-change", handleExampleChange);
657
- }, []);
658
-
659
  React.useEffect(() => {
660
  const node = resultsContentRef.current;
661
  if (!node) return undefined;
@@ -693,13 +682,21 @@ export default function FashionSearchComparisonPanel() {
693
  [commands],
694
  );
695
 
696
- const showExampleEvidence = React.useCallback(
697
- async (item, model, source = "fashion-demo:query") => {
698
- if (!item || !model?.layoutKey) return;
 
 
 
 
 
 
699
  try {
700
  await commands.setActiveLayout(model.layoutKey, { persist: false });
 
 
701
  await commands.showSimilar({
702
- sampleId: item.queryId,
703
  layoutKey: model.layoutKey,
704
  spaceKey: model.spaceKey,
705
  k: 10,
@@ -707,19 +704,43 @@ export default function FashionSearchComparisonPanel() {
707
  focus: "samples",
708
  persist: false,
709
  });
710
- await selectSimilarityAnchor(item.queryId);
 
 
 
 
 
 
 
 
 
 
 
 
711
  } catch (error) {
712
  const message = error instanceof Error ? error.message : String(error);
713
- setPanelError(`Could not load the sample evidence panel: ${message}`);
714
  }
715
  },
716
  [commands, selectSimilarityAnchor],
717
  );
718
 
 
 
 
 
 
 
 
 
 
 
 
 
 
719
  const chooseExample = React.useCallback(
720
  (exampleId) => {
721
  setSelectedExampleId(exampleId);
722
- window.dispatchEvent(new CustomEvent("fashion-search-example-change", { detail: exampleId }));
723
  const item = examples.find((entry) => entry.id === exampleId);
724
  const evidenceModel =
725
  models.find((model) => model.key === "clip" && model.layoutKey) ||
@@ -731,28 +752,6 @@ export default function FashionSearchComparisonPanel() {
731
  [examples, models, showExampleEvidence],
732
  );
733
 
734
- React.useEffect(() => {
735
- if (mode === "summary") return;
736
- if (didApplyDemoLayout.current) return;
737
- didApplyDemoLayout.current = true;
738
-
739
- let attempts = 0;
740
- const timer = window.setInterval(() => {
741
- attempts += 1;
742
- ["explorer", "labels"].forEach((panelId) => {
743
- try {
744
- commands.closePanel?.(panelId);
745
- } catch (_error) {
746
- // Older HyperView builds may not have every panel id; ignore missing panes.
747
- }
748
- });
749
- commands.focusPanel?.("fashion-ranked-results");
750
- if (attempts >= 8) window.clearInterval(timer);
751
- }, 250);
752
-
753
- return () => window.clearInterval(timer);
754
- }, [commands, mode]);
755
-
756
  React.useEffect(() => {
757
  if (mode === "summary") return;
758
  if (didPrimeEvidence.current) return;
@@ -773,8 +772,7 @@ export default function FashionSearchComparisonPanel() {
773
  const clearSelection = async () => {
774
  if (commands.setLabelFilter) commands.setLabelFilter(null);
775
  setPanelError(null);
776
- await client.clearSimilarityQuery();
777
- await commands.clearSelection();
778
  };
779
 
780
  const selectModelQuery = async (item, model) => {
@@ -787,17 +785,12 @@ export default function FashionSearchComparisonPanel() {
787
  }
788
  setLoadingKey(key);
789
  try {
790
- await commands.setActiveLayout(model.layoutKey, { persist: false });
791
- await commands.showSimilar({
792
  sampleId: item.queryId,
793
- layoutKey: model.layoutKey,
794
- spaceKey: model.spaceKey,
795
- k: 10,
796
  source: `fashion-demo:${model.key}`,
797
- focus: "samples",
798
- persist: false,
799
  });
800
- await selectSimilarityAnchor(item.queryId);
801
  } catch (error) {
802
  const message = error instanceof Error ? error.message : String(error);
803
  setPanelError(`Could not select target item: ${message}`);
@@ -819,17 +812,12 @@ export default function FashionSearchComparisonPanel() {
819
  }
820
  setLoadingKey(key);
821
  try {
822
- await commands.setActiveLayout(model.layoutKey, { persist: false });
823
- await commands.showSimilar({
824
  sampleId: result.sampleId,
825
- layoutKey: model.layoutKey,
826
- spaceKey: model.spaceKey,
827
- k: 10,
828
  source: `fashion-ranked-result:${model.key}`,
829
- focus: "samples",
830
- persist: false,
831
  });
832
- await selectSimilarityAnchor(result.sampleId);
833
  } catch (error) {
834
  const message = error instanceof Error ? error.message : String(error);
835
  setPanelError(`Could not select ranked result: ${message}`);
@@ -939,6 +927,24 @@ export default function FashionSearchComparisonPanel() {
939
  { style: { color: colors.bodyText, fontSize: 12, lineHeight: 1.45 } },
940
  "Demo examples are not configured.",
941
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
942
  selectedExample && models.length
943
  ? React.createElement(SearchResultComparison, {
944
  item: selectedExample,
@@ -950,24 +956,6 @@ export default function FashionSearchComparisonPanel() {
950
  })
951
  : null,
952
  ),
953
- selectedExample && inspectableModels.length
954
- ? React.createElement(
955
- Section,
956
- { title: "Inspect Exact Target" },
957
- React.createElement(
958
- TextBlock,
959
- null,
960
- `Exact-target summary for: ${selectedExample?.targetTitle || "target product"}.`,
961
- ),
962
- React.createElement(ExactTargetActions, {
963
- item: selectedExample,
964
- models: inspectableModels,
965
- loadingKey,
966
- onSelectQuery: selectModelQuery,
967
- compact: compactLayout,
968
- }),
969
- )
970
- : null,
971
  warnings.length
972
  ? React.createElement(
973
  "div",
 
6
  const { React, components, hooks } = sdk;
7
  const { Panel, PanelToolbar, PanelToolbarButton } = components;
8
  const {
 
9
  usePanelSelection,
10
  usePanelSamples,
11
  usePanelCommands,
 
606
  }
607
 
608
  export default function FashionSearchComparisonPanel() {
 
609
  const selection = usePanelSelection();
610
  const samplesState = usePanelSamples();
611
  const commands = usePanelCommands();
 
617
  );
618
  const [contentWidth, setContentWidth] = React.useState(0);
619
  const resultsContentRef = React.useRef(null);
 
620
  const didPrimeEvidence = React.useRef(false);
621
+ const evidenceRequestSeq = React.useRef(0);
622
+ const latestEvidenceRequest = React.useRef(null);
623
  const mode = panelProps.mode === "summary" ? "summary" : panelProps.mode === "results" ? "results" : "full";
624
 
625
  const models = React.useMemo(() => normalizeModels(panelProps.models), [panelProps.models]);
 
645
  { label: "Image-to-image mAP", hyper3: metrics.imageRetrievalMapHyper3, clip: metrics.imageRetrievalMapClip },
646
  ];
647
 
 
 
 
 
 
 
 
 
 
 
648
  React.useEffect(() => {
649
  const node = resultsContentRef.current;
650
  if (!node) return undefined;
 
682
  [commands],
683
  );
684
 
685
+ const loadEvidenceForSample = React.useCallback(
686
+ async ({ sampleId, model, source = "fashion-demo:evidence", errorPrefix = "Could not load the sample evidence panel" }) => {
687
+ if (!sampleId || !model?.layoutKey) return;
688
+ const requestKey = `${sampleId}:${model.layoutKey}:${model.spaceKey || ""}`;
689
+ const request = { sampleId, model, source, errorPrefix, requestKey };
690
+ latestEvidenceRequest.current = request;
691
+ const requestSeq = evidenceRequestSeq.current + 1;
692
+ evidenceRequestSeq.current = requestSeq;
693
+
694
  try {
695
  await commands.setActiveLayout(model.layoutKey, { persist: false });
696
+ if (requestSeq !== evidenceRequestSeq.current) return;
697
+
698
  await commands.showSimilar({
699
+ sampleId,
700
  layoutKey: model.layoutKey,
701
  spaceKey: model.spaceKey,
702
  k: 10,
 
704
  focus: "samples",
705
  persist: false,
706
  });
707
+ if (requestSeq !== evidenceRequestSeq.current) {
708
+ const latest = latestEvidenceRequest.current;
709
+ if (latest && latest.requestKey !== requestKey) {
710
+ window.setTimeout(() => {
711
+ if (latestEvidenceRequest.current?.requestKey === latest.requestKey) {
712
+ void loadEvidenceForSample(latest);
713
+ }
714
+ }, 0);
715
+ }
716
+ return;
717
+ }
718
+
719
+ await selectSimilarityAnchor(sampleId);
720
  } catch (error) {
721
  const message = error instanceof Error ? error.message : String(error);
722
+ setPanelError(`${errorPrefix}: ${message}`);
723
  }
724
  },
725
  [commands, selectSimilarityAnchor],
726
  );
727
 
728
+ const showExampleEvidence = React.useCallback(
729
+ async (item, model, source = "fashion-demo:query") => {
730
+ if (!item || !model?.layoutKey) return;
731
+ await loadEvidenceForSample({
732
+ sampleId: item.queryId,
733
+ model,
734
+ source,
735
+ errorPrefix: "Could not load the sample evidence panel",
736
+ });
737
+ },
738
+ [loadEvidenceForSample],
739
+ );
740
+
741
  const chooseExample = React.useCallback(
742
  (exampleId) => {
743
  setSelectedExampleId(exampleId);
 
744
  const item = examples.find((entry) => entry.id === exampleId);
745
  const evidenceModel =
746
  models.find((model) => model.key === "clip" && model.layoutKey) ||
 
752
  [examples, models, showExampleEvidence],
753
  );
754
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
755
  React.useEffect(() => {
756
  if (mode === "summary") return;
757
  if (didPrimeEvidence.current) return;
 
772
  const clearSelection = async () => {
773
  if (commands.setLabelFilter) commands.setLabelFilter(null);
774
  setPanelError(null);
775
+ await commands.clearQueryContext({ persist: true });
 
776
  };
777
 
778
  const selectModelQuery = async (item, model) => {
 
785
  }
786
  setLoadingKey(key);
787
  try {
788
+ await loadEvidenceForSample({
 
789
  sampleId: item.queryId,
790
+ model,
 
 
791
  source: `fashion-demo:${model.key}`,
792
+ errorPrefix: "Could not select target item",
 
793
  });
 
794
  } catch (error) {
795
  const message = error instanceof Error ? error.message : String(error);
796
  setPanelError(`Could not select target item: ${message}`);
 
812
  }
813
  setLoadingKey(key);
814
  try {
815
+ await loadEvidenceForSample({
 
816
  sampleId: result.sampleId,
817
+ model,
 
 
818
  source: `fashion-ranked-result:${model.key}`,
819
+ errorPrefix: "Could not select ranked result",
 
820
  });
 
821
  } catch (error) {
822
  const message = error instanceof Error ? error.message : String(error);
823
  setPanelError(`Could not select ranked result: ${message}`);
 
927
  { style: { color: colors.bodyText, fontSize: 12, lineHeight: 1.45 } },
928
  "Demo examples are not configured.",
929
  ),
930
+ selectedExample && inspectableModels.length
931
+ ? React.createElement(
932
+ Section,
933
+ { title: "Inspect Exact Target" },
934
+ React.createElement(
935
+ TextBlock,
936
+ null,
937
+ `Load the same target product in Samples: ${selectedExample?.targetTitle || "target product"}.`,
938
+ ),
939
+ React.createElement(ExactTargetActions, {
940
+ item: selectedExample,
941
+ models: inspectableModels,
942
+ loadingKey,
943
+ onSelectQuery: selectModelQuery,
944
+ compact: compactLayout,
945
+ }),
946
+ )
947
+ : null,
948
  selectedExample && models.length
949
  ? React.createElement(SearchResultComparison, {
950
  item: selectedExample,
 
956
  })
957
  : null,
958
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
959
  warnings.length
960
  ? React.createElement(
961
  "div",
Dockerfile CHANGED
@@ -42,7 +42,7 @@ COPY --chown=user . .
42
 
43
  ENV HYPERVIEW_HOST=0.0.0.0 \
44
  HYPERVIEW_PORT=7860 \
45
- HYPERVIEW_WORKSPACE_ID=fashion-retail-search-actual-scatter \
46
  HYPERVIEW_DATASETS_DIR=/home/user/app/demo_data/datasets \
47
  HYPERVIEW_MEDIA_DIR=/home/user/app/demo_data/media \
48
  FASHION_ENABLE_CONTEXT_MAPS=1 \
 
42
 
43
  ENV HYPERVIEW_HOST=0.0.0.0 \
44
  HYPERVIEW_PORT=7860 \
45
+ HYPERVIEW_WORKSPACE_ID=fashion-retail-search-v062-samples-visible \
46
  HYPERVIEW_DATASETS_DIR=/home/user/app/demo_data/datasets \
47
  HYPERVIEW_MEDIA_DIR=/home/user/app/demo_data/media \
48
  FASHION_ENABLE_CONTEXT_MAPS=1 \
demo.py CHANGED
@@ -19,7 +19,7 @@ from hyperview.core.sample import Sample
19
  SPACE_DIR = Path(__file__).resolve().parent
20
  SPACE_HOST = os.environ.get("HYPERVIEW_HOST", "127.0.0.1")
21
  SPACE_PORT = int(os.environ.get("HYPERVIEW_PORT", "6262"))
22
- WORKSPACE_ID = os.environ.get("HYPERVIEW_WORKSPACE_ID", "fashion-retail-search-actual-scatter")
23
  DATASET_NAME = os.environ.get("HYPERVIEW_DATASET_NAME", "deepfashion_text_search_clip_hyper3clip")
24
  EXTENSION_DIR = SPACE_DIR / ".hyperview" / "extensions" / "fashion-search-readout"
25
 
@@ -458,8 +458,8 @@ def build_demo_view(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.ui.View:
458
  panel="fashion-comparison",
459
  position="center",
460
  layout=hv.ui.PanelLayout(
461
- width=int(os.environ.get("FASHION_RESULTS_WIDTH", "760")),
462
- min_width=560,
463
  ),
464
  props={
465
  **shared_props,
@@ -473,9 +473,9 @@ def build_demo_view(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.ui.View:
473
  reference_panel_id="fashion-ranked-results",
474
  direction="right",
475
  layout=hv.ui.PanelLayout(
476
- width=int(os.environ.get("FASHION_SAMPLES_WIDTH", "900")),
477
- min_width=480,
478
- min_height=420,
479
  ),
480
  )
481
 
@@ -485,9 +485,9 @@ def build_demo_view(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.ui.View:
485
  clip_spec = MODEL_SPECS[0]
486
  candidate_spec = MODEL_SPECS[1]
487
  map_layout = hv.ui.PanelLayout(
488
- height=int(os.environ.get("FASHION_MAP_HEIGHT", "280")),
489
- min_height=220,
490
- min_width=260,
491
  )
492
  clip_map = hv.ui.Scatter(
493
  id="fashion-map-clip",
 
19
  SPACE_DIR = Path(__file__).resolve().parent
20
  SPACE_HOST = os.environ.get("HYPERVIEW_HOST", "127.0.0.1")
21
  SPACE_PORT = int(os.environ.get("HYPERVIEW_PORT", "6262"))
22
+ WORKSPACE_ID = os.environ.get("HYPERVIEW_WORKSPACE_ID", "fashion-retail-search-v062-samples-visible")
23
  DATASET_NAME = os.environ.get("HYPERVIEW_DATASET_NAME", "deepfashion_text_search_clip_hyper3clip")
24
  EXTENSION_DIR = SPACE_DIR / ".hyperview" / "extensions" / "fashion-search-readout"
25
 
 
458
  panel="fashion-comparison",
459
  position="center",
460
  layout=hv.ui.PanelLayout(
461
+ width=int(os.environ.get("FASHION_RESULTS_WIDTH", "620")),
462
+ min_width=500,
463
  ),
464
  props={
465
  **shared_props,
 
473
  reference_panel_id="fashion-ranked-results",
474
  direction="right",
475
  layout=hv.ui.PanelLayout(
476
+ width=int(os.environ.get("FASHION_SAMPLES_WIDTH", "660")),
477
+ min_width=420,
478
+ min_height=480,
479
  ),
480
  )
481
 
 
485
  clip_spec = MODEL_SPECS[0]
486
  candidate_spec = MODEL_SPECS[1]
487
  map_layout = hv.ui.PanelLayout(
488
+ height=int(os.environ.get("FASHION_MAP_HEIGHT", "180")),
489
+ min_height=150,
490
+ min_width=220,
491
  )
492
  clip_map = hv.ui.Scatter(
493
  id="fashion-map-clip",