amanydv2112 commited on
Commit
82fdcfd
Β·
verified Β·
1 Parent(s): c465a7f

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. src/search.py +2 -1
  2. src/server.py +14 -11
  3. src/storage.py +72 -9
  4. vercel.json +2 -2
src/search.py CHANGED
@@ -12,8 +12,9 @@ from rank_bm25 import BM25Okapi
12
  load_dotenv()
13
 
14
  from src.embeddings import Embedder, default_min_score, embeddings_filename
 
15
 
16
- DATA_DIR = Path(__file__).resolve().parent.parent / "data"
17
 
18
  RRF_K = 60
19
  HYBRID_TOP_N = 100
 
12
  load_dotenv()
13
 
14
  from src.embeddings import Embedder, default_min_score, embeddings_filename
15
+ from src.storage import resolve_data_dir
16
 
17
+ DATA_DIR = resolve_data_dir()
18
 
19
  RRF_K = 60
20
  HYBRID_TOP_N = 100
src/server.py CHANGED
@@ -21,26 +21,29 @@ engine: SearchEngine | None = None
21
 
22
 
23
  def _embedding_files_to_fetch() -> list[str]:
24
- """Return filenames that should be pulled from HF Hub (only when configured)."""
25
- if not os.environ.get("HF_DATASET_REPO"):
26
- return []
27
  provider = get_provider()
28
- return [
29
- embeddings_filename(provider, multi=True),
30
- embeddings_filename(provider, multi=False),
31
- ]
32
 
33
 
34
  @asynccontextmanager
35
  async def lifespan(app: FastAPI):
36
  global engine
37
- files = _embedding_files_to_fetch()
38
- if files:
39
- from src.storage import ensure_embeddings
40
- ensure_embeddings(files)
 
 
 
 
 
 
 
41
  engine = SearchEngine()
42
  print(f"Loaded {len(engine.companies)} companies with embeddings of shape {engine.embeddings.shape}")
43
  print(f"Embedding provider: {engine.embedder.provider}")
 
44
  yield
45
 
46
 
 
21
 
22
 
23
  def _embedding_files_to_fetch() -> list[str]:
24
+ """Filenames to ensure locally (prefer multi-field for the active provider)."""
 
 
25
  provider = get_provider()
26
+ return [embeddings_filename(provider, multi=True)]
 
 
 
27
 
28
 
29
  @asynccontextmanager
30
  async def lifespan(app: FastAPI):
31
  global engine
32
+ from src.storage import ensure_embeddings, resolve_data_dir
33
+
34
+ # On Vercel, /var/task is read-only and azure .npy is gitignored β€” resolve a
35
+ # writable data dir and pull embeddings from HF Hub when missing.
36
+ data_dir = resolve_data_dir()
37
+ os.environ["DATA_DIR"] = str(data_dir)
38
+ ensure_embeddings(_embedding_files_to_fetch(), data_dir=data_dir)
39
+
40
+ import src.search as search_mod
41
+ search_mod.DATA_DIR = data_dir
42
+
43
  engine = SearchEngine()
44
  print(f"Loaded {len(engine.companies)} companies with embeddings of shape {engine.embeddings.shape}")
45
  print(f"Embedding provider: {engine.embedder.provider}")
46
+ print(f"Data dir: {data_dir}")
47
  yield
48
 
49
 
src/storage.py CHANGED
@@ -1,9 +1,11 @@
1
  """Download embedding files from Hugging Face Hub Dataset at startup if not present locally."""
2
 
3
  import os
 
4
  from pathlib import Path
5
 
6
- DATA_DIR = Path(__file__).resolve().parent.parent / "data"
 
7
 
8
 
9
  def _is_valid_npy(path: Path) -> bool:
@@ -15,12 +17,71 @@ def _is_valid_npy(path: Path) -> bool:
15
  return False
16
 
17
 
18
- def ensure_embeddings(filenames: list[str]) -> None:
19
- """Download any missing or corrupted embedding files from HF Hub Dataset."""
20
- missing = [f for f in filenames if not (DATA_DIR / f).exists() or not _is_valid_npy(DATA_DIR / f)]
21
- if not missing:
22
- print("Embeddings already present locally, skipping download.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
  from huggingface_hub import hf_hub_download
26
 
@@ -29,18 +90,20 @@ def ensure_embeddings(filenames: list[str]) -> None:
29
 
30
  if not repo_id:
31
  raise RuntimeError(
32
- "Missing HF_DATASET_REPO env var. Set it to your HF dataset repo, e.g. 'yourname/nichefind-data'"
 
33
  )
34
 
35
  print(f"Downloading {len(missing)} embedding file(s) from HF dataset '{repo_id}' ...")
36
  for filename in missing:
37
- dest = DATA_DIR / filename
38
  print(f" ↓ {filename}")
39
  hf_hub_download(
40
  repo_id=repo_id,
41
  filename=filename,
42
  repo_type="dataset",
43
  token=token,
44
- local_dir=str(DATA_DIR),
45
  )
46
  print(f" βœ“ {filename} ({dest.stat().st_size / 1e6:.1f} MB)")
 
 
1
  """Download embedding files from Hugging Face Hub Dataset at startup if not present locally."""
2
 
3
  import os
4
+ import shutil
5
  from pathlib import Path
6
 
7
+ # Bundled/repo data directory (read-only on Vercel/Lambda).
8
+ REPO_DATA_DIR = Path(__file__).resolve().parent.parent / "data"
9
 
10
 
11
  def _is_valid_npy(path: Path) -> bool:
 
17
  return False
18
 
19
 
20
+ def _is_writable(path: Path) -> bool:
21
+ try:
22
+ path.mkdir(parents=True, exist_ok=True)
23
+ probe = path / ".write_test"
24
+ probe.write_text("ok")
25
+ probe.unlink()
26
+ return True
27
+ except Exception:
28
+ return False
29
+
30
+
31
+ def resolve_data_dir() -> Path:
32
+ """Return a writable data dir. On Vercel, /var/task is read-only so use /tmp."""
33
+ if explicit := os.environ.get("DATA_DIR"):
34
+ p = Path(explicit)
35
+ p.mkdir(parents=True, exist_ok=True)
36
+ return p
37
+ if _is_writable(REPO_DATA_DIR):
38
+ return REPO_DATA_DIR
39
+ tmp = Path("/tmp/nichefind-data")
40
+ tmp.mkdir(parents=True, exist_ok=True)
41
+ return tmp
42
+
43
+
44
+ def ensure_companies_json(data_dir: Path) -> None:
45
+ """Make sure companies.json is available in data_dir (copy from bundle if needed)."""
46
+ dest = data_dir / "companies.json"
47
+ if dest.exists():
48
  return
49
+ src = REPO_DATA_DIR / "companies.json"
50
+ if not src.exists():
51
+ raise FileNotFoundError(f"Missing companies.json at {src}")
52
+ if src.resolve() != dest.resolve():
53
+ shutil.copy2(src, dest)
54
+ print(f"Copied companies.json β†’ {dest}")
55
+
56
+
57
+ def ensure_embeddings(filenames: list[str], data_dir: Path | None = None) -> Path:
58
+ """Download any missing or corrupted embedding files from HF Hub Dataset.
59
+
60
+ Returns the data directory where files live.
61
+ """
62
+ data_dir = data_dir or resolve_data_dir()
63
+ ensure_companies_json(data_dir)
64
+
65
+ missing = [
66
+ f for f in filenames
67
+ if not (data_dir / f).exists() or not _is_valid_npy(data_dir / f)
68
+ ]
69
+ # Also check the bundled repo dir (CLI deploys that include the .npy locally)
70
+ if missing:
71
+ still_missing = []
72
+ for f in missing:
73
+ bundled = REPO_DATA_DIR / f
74
+ if bundled.exists() and _is_valid_npy(bundled):
75
+ if bundled.resolve() != (data_dir / f).resolve():
76
+ shutil.copy2(bundled, data_dir / f)
77
+ print(f"Copied {f} β†’ {data_dir / f}")
78
+ else:
79
+ still_missing.append(f)
80
+ missing = still_missing
81
+
82
+ if not missing:
83
+ print("Embeddings already present, skipping download.")
84
+ return data_dir
85
 
86
  from huggingface_hub import hf_hub_download
87
 
 
90
 
91
  if not repo_id:
92
  raise RuntimeError(
93
+ "Missing embeddings and HF_DATASET_REPO is not set. "
94
+ "Either bundle embeddings.azure.multi.npy or set HF_DATASET_REPO."
95
  )
96
 
97
  print(f"Downloading {len(missing)} embedding file(s) from HF dataset '{repo_id}' ...")
98
  for filename in missing:
99
+ dest = data_dir / filename
100
  print(f" ↓ {filename}")
101
  hf_hub_download(
102
  repo_id=repo_id,
103
  filename=filename,
104
  repo_type="dataset",
105
  token=token,
106
+ local_dir=str(data_dir),
107
  )
108
  print(f" βœ“ {filename} ({dest.stat().st_size / 1e6:.1f} MB)")
109
+ return data_dir
vercel.json CHANGED
@@ -4,8 +4,8 @@
4
  ],
5
  "functions": {
6
  "api/index.py": {
7
- "maxDuration": 60,
8
- "includeFiles": "data/{companies.json,embeddings.azure.multi.npy},static/**"
9
  }
10
  }
11
  }
 
4
  ],
5
  "functions": {
6
  "api/index.py": {
7
+ "maxDuration": 300,
8
+ "includeFiles": "{data/companies.json,static/**}"
9
  }
10
  }
11
  }