mnm-matin commited on
Commit
472e15a
·
verified ·
1 Parent(s): 004888d

Show samples alongside DeepFashion search demo

Browse files
.hyperview/extensions/fashion-search-readout/panel.js CHANGED
@@ -60,17 +60,14 @@ function TextBlock({ children }) {
60
 
61
  function normalizeModels(value) {
62
  if (!Array.isArray(value)) return [];
63
- return value
64
- .map((model, index) => ({
65
- key: String(model.key || `model-${index}`),
66
- displayName: String(model.displayName || model.display_name || model.key || `Model ${index + 1}`),
67
- buttonLabel: String(
68
- model.buttonLabel || model.button_label || `${model.displayName || model.key || "Model"} query`,
69
- ),
70
- layoutKey: model.layoutKey || model.layout_key || null,
71
- spaceKey: model.spaceKey || model.space_key || null,
72
- }))
73
- .filter((model) => model.layoutKey);
74
  }
75
 
76
  function getSummary(item, modelKey) {
@@ -348,7 +345,7 @@ function SearchResultComparison({ item, models, loadingKey, onSelectResult }) {
348
  React.createElement(
349
  "div",
350
  { style: { color: colors.mutedText, fontSize: 10, marginTop: 3 } },
351
- "Green card = exact product. The context map is secondary.",
352
  ),
353
  ),
354
  models.map((model) =>
@@ -609,6 +606,7 @@ export default function FashionSearchComparisonPanel() {
609
  const mode = panelProps.mode === "summary" ? "summary" : panelProps.mode === "results" ? "results" : "full";
610
 
611
  const models = React.useMemo(() => normalizeModels(panelProps.models), [panelProps.models]);
 
612
  const examples = Array.isArray(panelProps.examples) ? panelProps.examples : [];
613
  const warnings = Array.isArray(panelProps.warnings) ? panelProps.warnings.filter(Boolean) : [];
614
  const metrics = panelProps.metrics || {};
@@ -651,7 +649,6 @@ export default function FashionSearchComparisonPanel() {
651
  const timer = window.setTimeout(() => {
652
  [
653
  "explorer",
654
- "grid",
655
  "scatter-euclidean",
656
  "scatter-poincare",
657
  "scatter-spherical",
@@ -665,7 +662,6 @@ export default function FashionSearchComparisonPanel() {
665
  // Older HyperView builds may not have every panel id; ignore missing panes.
666
  }
667
  });
668
- commands.focusPanel?.("fashion-buyer-readout");
669
  commands.focusPanel?.("fashion-ranked-results");
670
  }, 350);
671
 
@@ -683,7 +679,8 @@ export default function FashionSearchComparisonPanel() {
683
  const key = `${item.queryId}:${model.key}`;
684
  setPanelError(null);
685
  if (!model.layoutKey) {
686
- setPanelError(`${model.displayName} layout is not ready yet. Try again in a moment.`);
 
687
  return;
688
  }
689
  setLoadingKey(key);
@@ -692,7 +689,6 @@ export default function FashionSearchComparisonPanel() {
692
  await commands.showSimilar({
693
  sampleId: item.queryId,
694
  layoutKey: model.layoutKey,
695
- spaceKey: model.spaceKey,
696
  k: 10,
697
  source: `fashion-demo:${model.key}`,
698
  focus: false,
@@ -711,7 +707,11 @@ export default function FashionSearchComparisonPanel() {
711
  const key = `${result.sampleId}:${model.key}:ranked-result`;
712
  setPanelError(null);
713
  if (!model.layoutKey) {
714
- setPanelError(`${model.displayName} layout is not ready yet. Try again in a moment.`);
 
 
 
 
715
  return;
716
  }
717
  setLoadingKey(key);
@@ -720,7 +720,6 @@ export default function FashionSearchComparisonPanel() {
720
  await commands.showSimilar({
721
  sampleId: result.sampleId,
722
  layoutKey: model.layoutKey,
723
- spaceKey: model.spaceKey,
724
  k: 10,
725
  source: `fashion-ranked-result:${model.key}`,
726
  focus: false,
@@ -766,7 +765,7 @@ export default function FashionSearchComparisonPanel() {
766
  React.createElement(
767
  TextBlock,
768
  null,
769
- "Business readout for the selected shopper query. Map tabs are optional neighborhood context.",
770
  ),
771
  examples.length
772
  ? React.createElement(ExampleChooser, {
@@ -843,27 +842,23 @@ export default function FashionSearchComparisonPanel() {
843
  })
844
  : null,
845
  ),
846
- React.createElement(
847
- Section,
848
- { title: "Inspect Exact Target" },
849
- React.createElement(
850
- TextBlock,
851
- null,
852
- `Exact-target summary for: ${selectedExample?.targetTitle || "target product"}.`,
853
- ),
854
- selectedExample && models.length
855
- ? React.createElement(ExactTargetActions, {
856
  item: selectedExample,
857
- models,
858
  loadingKey,
859
  onSelectQuery: selectModelQuery,
860
- })
861
- : React.createElement(
862
- "div",
863
- { style: { color: colors.bodyText, fontSize: 12, lineHeight: 1.45 } },
864
- "Demo examples are not configured.",
865
- ),
866
- ),
867
  warnings.length
868
  ? React.createElement(
869
  "div",
 
60
 
61
  function normalizeModels(value) {
62
  if (!Array.isArray(value)) return [];
63
+ return value.map((model, index) => ({
64
+ key: String(model.key || `model-${index}`),
65
+ displayName: String(model.displayName || model.display_name || model.key || `Model ${index + 1}`),
66
+ buttonLabel: String(
67
+ model.buttonLabel || model.button_label || `${model.displayName || model.key || "Model"} query`,
68
+ ),
69
+ layoutKey: model.layoutKey || model.layout_key || null,
70
+ }));
 
 
 
71
  }
72
 
73
  function getSummary(item, modelKey) {
 
345
  React.createElement(
346
  "div",
347
  { style: { color: colors.mutedText, fontSize: 10, marginTop: 3 } },
348
+ "Green card = exact product. Click any card to select it in Samples.",
349
  ),
350
  ),
351
  models.map((model) =>
 
606
  const mode = panelProps.mode === "summary" ? "summary" : panelProps.mode === "results" ? "results" : "full";
607
 
608
  const models = React.useMemo(() => normalizeModels(panelProps.models), [panelProps.models]);
609
+ const inspectableModels = React.useMemo(() => models.filter((model) => model.layoutKey), [models]);
610
  const examples = Array.isArray(panelProps.examples) ? panelProps.examples : [];
611
  const warnings = Array.isArray(panelProps.warnings) ? panelProps.warnings.filter(Boolean) : [];
612
  const metrics = panelProps.metrics || {};
 
649
  const timer = window.setTimeout(() => {
650
  [
651
  "explorer",
 
652
  "scatter-euclidean",
653
  "scatter-poincare",
654
  "scatter-spherical",
 
662
  // Older HyperView builds may not have every panel id; ignore missing panes.
663
  }
664
  });
 
665
  commands.focusPanel?.("fashion-ranked-results");
666
  }, 350);
667
 
 
679
  const key = `${item.queryId}:${model.key}`;
680
  setPanelError(null);
681
  if (!model.layoutKey) {
682
+ await commands.setSelection?.([item.queryId], { persist: false, source: `fashion-target:${model.key}` });
683
+ commands.focusPanel?.("grid");
684
  return;
685
  }
686
  setLoadingKey(key);
 
689
  await commands.showSimilar({
690
  sampleId: item.queryId,
691
  layoutKey: model.layoutKey,
 
692
  k: 10,
693
  source: `fashion-demo:${model.key}`,
694
  focus: false,
 
707
  const key = `${result.sampleId}:${model.key}:ranked-result`;
708
  setPanelError(null);
709
  if (!model.layoutKey) {
710
+ await commands.setSelection?.([result.sampleId], {
711
+ persist: false,
712
+ source: `fashion-ranked-result:${model.key}`,
713
+ });
714
+ commands.focusPanel?.("grid");
715
  return;
716
  }
717
  setLoadingKey(key);
 
720
  await commands.showSimilar({
721
  sampleId: result.sampleId,
722
  layoutKey: model.layoutKey,
 
723
  k: 10,
724
  source: `fashion-ranked-result:${model.key}`,
725
  focus: false,
 
765
  React.createElement(
766
  TextBlock,
767
  null,
768
+ "Business readout for the selected shopper query.",
769
  ),
770
  examples.length
771
  ? React.createElement(ExampleChooser, {
 
842
  })
843
  : null,
844
  ),
845
+ selectedExample && inspectableModels.length
846
+ ? React.createElement(
847
+ Section,
848
+ { title: "Inspect Exact Target" },
849
+ React.createElement(
850
+ TextBlock,
851
+ null,
852
+ `Exact-target summary for: ${selectedExample?.targetTitle || "target product"}.`,
853
+ ),
854
+ React.createElement(ExactTargetActions, {
855
  item: selectedExample,
856
+ models: inspectableModels,
857
  loadingKey,
858
  onSelectQuery: selectModelQuery,
859
+ }),
860
+ )
861
+ : null,
 
 
 
 
862
  warnings.length
863
  ? React.createElement(
864
  "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-composition \
46
  HYPERVIEW_DATASETS_DIR=/home/user/app/demo_data/datasets \
47
  HYPERVIEW_MEDIA_DIR=/home/user/app/demo_data/media \
48
  DEEPFASHION_SAMPLES_PER_CATEGORY=10 \
 
42
 
43
  ENV HYPERVIEW_HOST=0.0.0.0 \
44
  HYPERVIEW_PORT=7860 \
45
+ HYPERVIEW_WORKSPACE_ID=fashion-retail-search-samples \
46
  HYPERVIEW_DATASETS_DIR=/home/user/app/demo_data/datasets \
47
  HYPERVIEW_MEDIA_DIR=/home/user/app/demo_data/media \
48
  DEEPFASHION_SAMPLES_PER_CATEGORY=10 \
demo.py CHANGED
@@ -18,7 +18,7 @@ import hyperview as hv
18
  SPACE_DIR = Path(__file__).resolve().parent
19
  SPACE_HOST = os.environ.get("HYPERVIEW_HOST", "127.0.0.1")
20
  SPACE_PORT = int(os.environ.get("HYPERVIEW_PORT", "6262"))
21
- WORKSPACE_ID = os.environ.get("HYPERVIEW_WORKSPACE_ID", "fashion-retail-search-composition")
22
  DATASET_NAME = os.environ.get("HYPERVIEW_DATASET_NAME", "deepfashion_text_search_clip_hyper3clip")
23
  EXTENSION_DIR = SPACE_DIR / ".hyperview" / "extensions" / "fashion-search-readout"
24
 
@@ -37,6 +37,11 @@ ALLOW_CANDIDATE_FALLBACK = os.environ.get("HYPERVIEW_ALLOW_CANDIDATE_FALLBACK",
37
  "true",
38
  "yes",
39
  }
 
 
 
 
 
40
  EMBEDDING_MAX_ATTEMPTS = max(1, int(os.environ.get("HYPERVIEW_EMBEDDING_MAX_ATTEMPTS", "4")))
41
  EMBEDDING_RETRY_DELAY_SECONDS = float(os.environ.get("HYPERVIEW_EMBEDDING_RETRY_DELAY_SECONDS", "15"))
42
  RUNTIME_WARNINGS: list[str] = []
@@ -332,25 +337,20 @@ def ensure_layouts(dataset: hv.Dataset) -> dict[str, str]:
332
  def build_dataset() -> tuple[hv.Dataset, dict[str, str]]:
333
  dataset = hv.Dataset(DATASET_NAME)
334
  add_deepfashion_samples(dataset)
335
- layouts = ensure_layouts(dataset)
336
  return dataset, layouts
337
 
338
 
339
- def space_key_from_layout(layout_key: str) -> str:
340
- return layout_key.split("__euclidean_umap", 1)[0].split("__poincare_umap", 1)[0]
341
-
342
-
343
  def model_panel_props(layouts: dict[str, str]) -> list[dict[str, Any]]:
344
  props = []
345
  for spec in MODEL_SPECS:
346
- layout_key = layouts[spec["key"]]
347
  props.append(
348
  {
349
  "key": spec["key"],
350
  "displayName": spec["display_name"],
351
  "buttonLabel": spec["button_label"],
352
  "layoutKey": layout_key,
353
- "spaceKey": spec.get("space_key") or space_key_from_layout(layout_key),
354
  }
355
  )
356
  return props
@@ -438,12 +438,12 @@ def build_demo_view(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.ui.View:
438
  title="Ranked Search Results",
439
  extension="fashion-search-readout",
440
  panel="fashion-comparison",
441
- position="center",
442
  props={
443
  **shared_props,
444
  "mode": "results",
445
  "dockview": {
446
- "closeSamplesPanel": True,
447
  "hideExplorerPanel": True,
448
  "initialWidth": int(os.environ.get("FASHION_RESULTS_WIDTH", "780")),
449
  "minimumWidth": 620,
@@ -452,6 +452,9 @@ def build_demo_view(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.ui.View:
452
  },
453
  )
454
 
 
 
 
455
  clip_spec = MODEL_SPECS[0]
456
  candidate_spec = MODEL_SPECS[1]
457
  context_dockview = {
@@ -459,24 +462,12 @@ def build_demo_view(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.ui.View:
459
  "minimumWidth": 260,
460
  "maximumWidth": int(os.environ.get("FASHION_CONTEXT_MAX_WIDTH", "440")),
461
  }
462
- summary_panel = hv.ui.ExtensionPanel(
463
- id="fashion-buyer-readout",
464
- title="Buyer Readout",
465
- extension="fashion-search-readout",
466
- panel="fashion-comparison",
467
- position="right",
468
- props={
469
- **shared_props,
470
- "mode": "summary",
471
- "dockview": context_dockview,
472
- },
473
- )
474
  clip_map = hv.ui.Scatter(
475
  id="fashion-map-clip",
476
  title="Context Map: CLIP",
477
  layout_key=layouts["clip"],
478
  position="right",
479
- reference_panel_id="fashion-buyer-readout",
480
  direction="within",
481
  geometry=clip_spec["geometry"],
482
  layout_dimension=clip_spec["layout_dimension"],
@@ -493,7 +484,7 @@ def build_demo_view(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.ui.View:
493
  layout_dimension=candidate_spec["layout_dimension"],
494
  props={"dockview": context_dockview},
495
  )
496
- return hv.ui.View(results_panel, summary_panel, clip_map, candidate_map)
497
 
498
 
499
  def launch_demo(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.Session:
@@ -512,15 +503,21 @@ def launch_demo(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.Session:
512
  session.ui.set_active_layout(None, workspace_id=WORKSPACE_ID)
513
  session.ui.set_selection([], workspace_id=WORKSPACE_ID)
514
  print(f"\nHyperView DeepFashion text-search demo is running at {session.url}", flush=True)
515
- print(" Ranked results are primary; buyer readout and scatter maps are secondary context tabs.", flush=True)
 
 
 
516
  return session
517
 
518
 
519
  def main() -> None:
520
  dataset, layouts = build_dataset()
521
- print("Layouts:", flush=True)
522
- for spec in MODEL_SPECS:
523
- print(f" {spec['display_name']}: {layouts[spec['key']]}", flush=True)
 
 
 
524
  session = launch_demo(dataset, layouts)
525
  session.wait()
526
 
 
18
  SPACE_DIR = Path(__file__).resolve().parent
19
  SPACE_HOST = os.environ.get("HYPERVIEW_HOST", "127.0.0.1")
20
  SPACE_PORT = int(os.environ.get("HYPERVIEW_PORT", "6262"))
21
+ WORKSPACE_ID = os.environ.get("HYPERVIEW_WORKSPACE_ID", "fashion-retail-search-samples")
22
  DATASET_NAME = os.environ.get("HYPERVIEW_DATASET_NAME", "deepfashion_text_search_clip_hyper3clip")
23
  EXTENSION_DIR = SPACE_DIR / ".hyperview" / "extensions" / "fashion-search-readout"
24
 
 
37
  "true",
38
  "yes",
39
  }
40
+ ENABLE_CONTEXT_MAPS = os.environ.get("FASHION_ENABLE_CONTEXT_MAPS", "").lower() in {
41
+ "1",
42
+ "true",
43
+ "yes",
44
+ }
45
  EMBEDDING_MAX_ATTEMPTS = max(1, int(os.environ.get("HYPERVIEW_EMBEDDING_MAX_ATTEMPTS", "4")))
46
  EMBEDDING_RETRY_DELAY_SECONDS = float(os.environ.get("HYPERVIEW_EMBEDDING_RETRY_DELAY_SECONDS", "15"))
47
  RUNTIME_WARNINGS: list[str] = []
 
337
  def build_dataset() -> tuple[hv.Dataset, dict[str, str]]:
338
  dataset = hv.Dataset(DATASET_NAME)
339
  add_deepfashion_samples(dataset)
340
+ layouts = ensure_layouts(dataset) if ENABLE_CONTEXT_MAPS else {}
341
  return dataset, layouts
342
 
343
 
 
 
 
 
344
  def model_panel_props(layouts: dict[str, str]) -> list[dict[str, Any]]:
345
  props = []
346
  for spec in MODEL_SPECS:
347
+ layout_key = layouts.get(spec["key"])
348
  props.append(
349
  {
350
  "key": spec["key"],
351
  "displayName": spec["display_name"],
352
  "buttonLabel": spec["button_label"],
353
  "layoutKey": layout_key,
 
354
  }
355
  )
356
  return props
 
438
  title="Ranked Search Results",
439
  extension="fashion-search-readout",
440
  panel="fashion-comparison",
441
+ position="right",
442
  props={
443
  **shared_props,
444
  "mode": "results",
445
  "dockview": {
446
+ "closeSamplesPanel": False,
447
  "hideExplorerPanel": True,
448
  "initialWidth": int(os.environ.get("FASHION_RESULTS_WIDTH", "780")),
449
  "minimumWidth": 620,
 
452
  },
453
  )
454
 
455
+ if not ENABLE_CONTEXT_MAPS:
456
+ return hv.ui.View(results_panel)
457
+
458
  clip_spec = MODEL_SPECS[0]
459
  candidate_spec = MODEL_SPECS[1]
460
  context_dockview = {
 
462
  "minimumWidth": 260,
463
  "maximumWidth": int(os.environ.get("FASHION_CONTEXT_MAX_WIDTH", "440")),
464
  }
 
 
 
 
 
 
 
 
 
 
 
 
465
  clip_map = hv.ui.Scatter(
466
  id="fashion-map-clip",
467
  title="Context Map: CLIP",
468
  layout_key=layouts["clip"],
469
  position="right",
470
+ reference_panel_id="fashion-ranked-results",
471
  direction="within",
472
  geometry=clip_spec["geometry"],
473
  layout_dimension=clip_spec["layout_dimension"],
 
484
  layout_dimension=candidate_spec["layout_dimension"],
485
  props={"dockview": context_dockview},
486
  )
487
+ return hv.ui.View(results_panel, clip_map, candidate_map)
488
 
489
 
490
  def launch_demo(dataset: hv.Dataset, layouts: dict[str, str]) -> hv.Session:
 
503
  session.ui.set_active_layout(None, workspace_id=WORKSPACE_ID)
504
  session.ui.set_selection([], workspace_id=WORKSPACE_ID)
505
  print(f"\nHyperView DeepFashion text-search demo is running at {session.url}", flush=True)
506
+ if ENABLE_CONTEXT_MAPS:
507
+ print(" Samples stay visible; scatter maps are opt-in context tabs.", flush=True)
508
+ else:
509
+ print(" Samples stay visible; ranked text-search results are the main demo.", flush=True)
510
  return session
511
 
512
 
513
  def main() -> None:
514
  dataset, layouts = build_dataset()
515
+ if layouts:
516
+ print("Layouts:", flush=True)
517
+ for spec in MODEL_SPECS:
518
+ print(f" {spec['display_name']}: {layouts[spec['key']]}", flush=True)
519
+ else:
520
+ print("Context maps disabled; skipping embedding/layout startup.", flush=True)
521
  session = launch_demo(dataset, layouts)
522
  session.wait()
523