pbordescnil commited on
Commit
5be9274
·
1 Parent(s): ca526b9

Revert "Refresh graph directly from Hub API"

Browse files

This reverts commit cfe24a8555f547fd805afa88ed651ba284979b52.

database_refresh/refresh_database.py CHANGED
@@ -1,5 +1,5 @@
1
  #!/usr/bin/env python3
2
- """Build and publish a Neo4j dump from the public Hugging Face Hub API."""
3
 
4
  from __future__ import annotations
5
 
@@ -17,7 +17,6 @@ from huggingface_hub import CommitOperationAdd, HfApi, hf_hub_download
17
 
18
 
19
  SOURCE_REPO = "cfahlgren1/hub-stats"
20
- HUB_API_SOURCE = "huggingface.co/api"
21
  DEFAULT_DUMP_REPO = "cnil/genmod-dump-neo4j"
22
  PARQUET_REVISION = "refs/convert/parquet"
23
 
@@ -51,106 +50,6 @@ def download_sources(work_dir: Path) -> tuple[Path, Path]:
51
  return models, datasets
52
 
53
 
54
- def isoformat(value: object) -> str | None:
55
- return value.isoformat() if hasattr(value, "isoformat") else None
56
-
57
-
58
- def write_jsonl_snapshot(path: Path, rows: object, kind: str) -> int:
59
- count = 0
60
- with path.open("w", encoding="utf-8") as output:
61
- for info in rows:
62
- tags = list(info.tags or [])
63
- if kind == "model":
64
- safetensors = getattr(info, "safetensors", None)
65
- card_data = getattr(info, "card_data", None)
66
- base_models = getattr(info, "base_models", None) or {}
67
- record = {
68
- "_id": getattr(info, "_id", None),
69
- "id": info.id,
70
- "author": info.author,
71
- "lastModified": isoformat(info.last_modified),
72
- "downloadsAllTime": info.downloads_all_time or info.downloads,
73
- "pipeline_tag": info.pipeline_tag,
74
- "createdAt": isoformat(info.created_at),
75
- "parameters_total": getattr(safetensors, "total", None),
76
- "likes": info.likes,
77
- "license": (
78
- card_data.get("license")
79
- if isinstance(card_data, dict)
80
- else None
81
- ),
82
- "tags": tags,
83
- "base_model_relation": base_models.get("relation"),
84
- "base_model_ids": [
85
- model.get("id")
86
- for model in base_models.get("models", [])
87
- if model.get("id")
88
- ],
89
- }
90
- else:
91
- record = {
92
- "_id": getattr(info, "_id", None),
93
- "id": info.id,
94
- "author": info.author,
95
- "lastModified": isoformat(info.last_modified),
96
- "downloadsAllTime": info.downloads_all_time or info.downloads,
97
- "createdAt": isoformat(info.created_at),
98
- "tags": tags,
99
- }
100
- output.write(json.dumps(record, ensure_ascii=False) + "\n")
101
- count += 1
102
- if count % 50000 == 0:
103
- log(f"Collected {count:,} {kind} records")
104
- log(f"Collected {count:,} {kind} records in total")
105
- return count
106
-
107
-
108
- def download_sources_from_api(
109
- work_dir: Path,
110
- api: HfApi,
111
- max_models: int | None,
112
- max_datasets: int | None,
113
- ) -> tuple[Path, Path]:
114
- """Create deterministic local snapshots without relying on a third-party dataset."""
115
- model_fields = [
116
- "author",
117
- "baseModels",
118
- "cardData",
119
- "createdAt",
120
- "downloads",
121
- "downloadsAllTime",
122
- "lastModified",
123
- "likes",
124
- "pipeline_tag",
125
- "safetensors",
126
- "tags",
127
- ]
128
- dataset_fields = [
129
- "author",
130
- "createdAt",
131
- "downloads",
132
- "downloadsAllTime",
133
- "lastModified",
134
- "likes",
135
- "tags",
136
- ]
137
- models = work_dir / "models.jsonl"
138
- datasets = work_dir / "datasets.jsonl"
139
- log("Collecting the model snapshot from the public Hugging Face Hub API")
140
- write_jsonl_snapshot(
141
- models,
142
- api.list_models(expand=model_fields, limit=max_models),
143
- "model",
144
- )
145
- log("Collecting the dataset snapshot from the public Hugging Face Hub API")
146
- write_jsonl_snapshot(
147
- datasets,
148
- api.list_datasets(expand=dataset_fields, limit=max_datasets),
149
- "dataset",
150
- )
151
- return models, datasets
152
-
153
-
154
  def sql_path(path: Path) -> str:
155
  return str(path).replace("'", "''")
156
 
@@ -164,47 +63,18 @@ def create_views(
164
  ) -> None:
165
  model_limit = f" LIMIT {max_models}" if max_models else ""
166
  dataset_limit = f" LIMIT {max_datasets}" if max_datasets else ""
167
- if models_path.suffix == ".jsonl":
168
- model_source = f"read_json_auto('{sql_path(models_path)}', format='newline_delimited')"
169
- dataset_source = f"read_json_auto('{sql_path(datasets_path)}', format='newline_delimited')"
170
- model_columns = """
171
- TRY_CAST(_id AS VARCHAR) AS _id, id, author, lastModified,
172
- TRY_CAST(downloadsAllTime AS BIGINT) AS downloadsAllTime,
173
- pipeline_tag, createdAt,
174
- TRY_CAST(parameters_total AS BIGINT) AS parameters_total,
175
- TRY_CAST(likes AS BIGINT) AS likes,
176
- TRY_CAST(license AS VARCHAR) AS license, tags,
177
- TRY_CAST(base_model_relation AS VARCHAR) AS base_model_relation,
178
- TRY_CAST(base_model_ids AS VARCHAR[]) AS base_model_ids
179
- """
180
- dataset_columns = """
181
- TRY_CAST(_id AS VARCHAR) AS _id, id, author, lastModified,
182
- TRY_CAST(downloadsAllTime AS BIGINT) AS downloadsAllTime,
183
- createdAt, tags
184
- """
185
- else:
186
- model_source = f"read_parquet('{sql_path(models_path)}')"
187
- dataset_source = f"read_parquet('{sql_path(datasets_path)}')"
188
- model_columns = """
189
- _id, id, author, lastModified, downloadsAllTime, pipeline_tag,
190
- createdAt, safetensors.total AS parameters_total, likes,
191
- json_extract_string(cardData, '$.license') AS license, tags,
192
- baseModels.relation AS base_model_relation,
193
- list_transform(baseModels.models, model -> model.id) AS base_model_ids
194
- """
195
- dataset_columns = "_id, id, author, lastModified, downloadsAllTime, createdAt, tags"
196
  connection.execute(
197
  f"""
198
  CREATE VIEW source_models AS
199
  SELECT * EXCLUDE (_dedupe_rank)
200
  FROM (
201
  SELECT
202
- {model_columns},
203
  row_number() OVER (
204
  PARTITION BY id
205
  ORDER BY lastModified DESC NULLS LAST, _id DESC NULLS LAST
206
  ) AS _dedupe_rank
207
- FROM {model_source}
208
  WHERE id IS NOT NULL AND trim(id) <> ''
209
  )
210
  WHERE _dedupe_rank = 1
@@ -217,12 +87,12 @@ def create_views(
217
  SELECT * EXCLUDE (_dedupe_rank)
218
  FROM (
219
  SELECT
220
- {dataset_columns},
221
  row_number() OVER (
222
  PARTITION BY id
223
  ORDER BY lastModified DESC NULLS LAST, _id DESC NULLS LAST
224
  ) AS _dedupe_rank
225
- FROM {dataset_source}
226
  WHERE id IS NOT NULL AND trim(id) <> ''
227
  )
228
  WHERE _dedupe_rank = 1
@@ -233,13 +103,14 @@ def create_views(
233
  """
234
  CREATE VIEW base_model_edges AS
235
  SELECT DISTINCT
236
- base_id AS parent_id,
237
  child.id AS child_id,
238
- COALESCE(child.base_model_relation, 'derived') AS relation_name
239
  FROM source_models AS child,
240
- UNNEST(child.base_model_ids) AS nested(base_id)
241
- WHERE base_id IS NOT NULL
242
- AND trim(base_id) <> ''
 
243
  AND child.id IS NOT NULL
244
  """
245
  )
@@ -308,17 +179,17 @@ def prepare_csv_files(
308
  pipeline_tag AS task,
309
  CAST(createdAt AS VARCHAR) AS created_at,
310
  CASE
311
- WHEN parameters_total >= 1000000000
312
- THEN printf('%.1fB', parameters_total / 1000000000.0)
313
- WHEN parameters_total >= 1000000
314
- THEN printf('%.1fM', parameters_total / 1000000.0)
315
- WHEN parameters_total >= 1000
316
- THEN printf('%.1fK', parameters_total / 1000.0)
317
- WHEN parameters_total IS NOT NULL
318
- THEN CAST(parameters_total AS VARCHAR)
319
  END AS parameters,
320
  likes,
321
- license
322
  FROM source_models
323
  WHERE id IS NOT NULL AND trim(id) <> ''
324
  ),
@@ -468,14 +339,13 @@ def build_dump(files: dict[str, Path], output_dir: Path, neo4j_admin: str) -> Pa
468
 
469
  def write_metadata(
470
  output_dir: Path,
471
- source_repo: str,
472
  source_revision: str,
473
  model_count: int,
474
  dataset_count: int,
475
  ) -> Path:
476
  metadata = {
477
  "built_at": datetime.now(timezone.utc).isoformat(),
478
- "source_repo": source_repo,
479
  "source_revision": source_revision,
480
  "model_count": model_count,
481
  "dataset_count": dataset_count,
@@ -487,11 +357,10 @@ def write_metadata(
487
 
488
  def parquet_unique_id_count(path: Path) -> int:
489
  connection = duckdb.connect()
490
- reader = "read_json_auto" if path.suffix == ".jsonl" else "read_parquet"
491
  count = connection.execute(
492
  f"""
493
  SELECT count(DISTINCT id)
494
- FROM {reader}('{sql_path(path)}')
495
  WHERE id IS NOT NULL AND trim(id) <> ''
496
  """
497
  ).fetchone()[0]
@@ -528,7 +397,7 @@ def publish_dump(
528
  path_or_fileobj=str(metadata_path),
529
  ),
530
  ],
531
- commit_message="Refresh Neo4j graph from the Hugging Face Hub API",
532
  )
533
 
534
 
@@ -544,11 +413,6 @@ def parse_args() -> argparse.Namespace:
544
  parser.add_argument("--dump-repo", default=os.getenv("NEO4J_DUMP_REPO", DEFAULT_DUMP_REPO))
545
  parser.add_argument("--dump-revision", default=os.getenv("NEO4J_DUMP_REVISION", "main"))
546
  parser.add_argument("--neo4j-admin", default=os.getenv("NEO4J_ADMIN", "neo4j-admin"))
547
- parser.add_argument(
548
- "--source",
549
- choices=("hub-api", "hub-stats"),
550
- default=os.getenv("GENMOD_REFRESH_SOURCE", "hub-api"),
551
- )
552
  parser.add_argument("--models-parquet", type=Path)
553
  parser.add_argument("--datasets-parquet", type=Path)
554
  parser.add_argument("--max-models", type=int)
@@ -567,25 +431,13 @@ def main() -> int:
567
  args.work_dir.mkdir(parents=True, exist_ok=True)
568
 
569
  api = HfApi()
 
570
  if bool(args.models_parquet) != bool(args.datasets_parquet):
571
  raise SystemExit("Provide both --models-parquet and --datasets-parquet.")
572
  if args.models_parquet:
573
  models_path, datasets_path = args.models_parquet, args.datasets_parquet
574
- source_repo = "local-files"
575
- source_revision = "provided"
576
- elif args.source == "hub-stats":
577
- source_repo = SOURCE_REPO
578
- source_revision = api.dataset_info(SOURCE_REPO).sha
579
- models_path, datasets_path = download_sources(args.work_dir)
580
  else:
581
- source_repo = HUB_API_SOURCE
582
- source_revision = datetime.now(timezone.utc).isoformat()
583
- models_path, datasets_path = download_sources_from_api(
584
- args.work_dir,
585
- api,
586
- args.max_models,
587
- args.max_datasets,
588
- )
589
 
590
  csv_dir = args.work_dir / "csv"
591
  files = prepare_csv_files(
@@ -604,7 +456,6 @@ def main() -> int:
604
  dataset_count = args.max_datasets or parquet_unique_id_count(datasets_path)
605
  metadata_path = write_metadata(
606
  args.work_dir,
607
- source_repo,
608
  source_revision,
609
  model_count,
610
  dataset_count,
 
1
  #!/usr/bin/env python3
2
+ """Build and publish a Neo4j dump from the public cfahlgren1/hub-stats dataset."""
3
 
4
  from __future__ import annotations
5
 
 
17
 
18
 
19
  SOURCE_REPO = "cfahlgren1/hub-stats"
 
20
  DEFAULT_DUMP_REPO = "cnil/genmod-dump-neo4j"
21
  PARQUET_REVISION = "refs/convert/parquet"
22
 
 
50
  return models, datasets
51
 
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  def sql_path(path: Path) -> str:
54
  return str(path).replace("'", "''")
55
 
 
63
  ) -> None:
64
  model_limit = f" LIMIT {max_models}" if max_models else ""
65
  dataset_limit = f" LIMIT {max_datasets}" if max_datasets else ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  connection.execute(
67
  f"""
68
  CREATE VIEW source_models AS
69
  SELECT * EXCLUDE (_dedupe_rank)
70
  FROM (
71
  SELECT
72
+ *,
73
  row_number() OVER (
74
  PARTITION BY id
75
  ORDER BY lastModified DESC NULLS LAST, _id DESC NULLS LAST
76
  ) AS _dedupe_rank
77
+ FROM read_parquet('{sql_path(models_path)}')
78
  WHERE id IS NOT NULL AND trim(id) <> ''
79
  )
80
  WHERE _dedupe_rank = 1
 
87
  SELECT * EXCLUDE (_dedupe_rank)
88
  FROM (
89
  SELECT
90
+ *,
91
  row_number() OVER (
92
  PARTITION BY id
93
  ORDER BY lastModified DESC NULLS LAST, _id DESC NULLS LAST
94
  ) AS _dedupe_rank
95
+ FROM read_parquet('{sql_path(datasets_path)}')
96
  WHERE id IS NOT NULL AND trim(id) <> ''
97
  )
98
  WHERE _dedupe_rank = 1
 
103
  """
104
  CREATE VIEW base_model_edges AS
105
  SELECT DISTINCT
106
+ base.id AS parent_id,
107
  child.id AS child_id,
108
+ COALESCE(child.baseModels.relation, 'derived') AS relation_name
109
  FROM source_models AS child,
110
+ UNNEST(child.baseModels.models) AS nested(base)
111
+ WHERE child.baseModels IS NOT NULL
112
+ AND base.id IS NOT NULL
113
+ AND trim(base.id) <> ''
114
  AND child.id IS NOT NULL
115
  """
116
  )
 
179
  pipeline_tag AS task,
180
  CAST(createdAt AS VARCHAR) AS created_at,
181
  CASE
182
+ WHEN safetensors.total >= 1000000000
183
+ THEN printf('%.1fB', safetensors.total / 1000000000.0)
184
+ WHEN safetensors.total >= 1000000
185
+ THEN printf('%.1fM', safetensors.total / 1000000.0)
186
+ WHEN safetensors.total >= 1000
187
+ THEN printf('%.1fK', safetensors.total / 1000.0)
188
+ WHEN safetensors.total IS NOT NULL
189
+ THEN CAST(safetensors.total AS VARCHAR)
190
  END AS parameters,
191
  likes,
192
+ json_extract_string(cardData, '$.license') AS license
193
  FROM source_models
194
  WHERE id IS NOT NULL AND trim(id) <> ''
195
  ),
 
339
 
340
  def write_metadata(
341
  output_dir: Path,
 
342
  source_revision: str,
343
  model_count: int,
344
  dataset_count: int,
345
  ) -> Path:
346
  metadata = {
347
  "built_at": datetime.now(timezone.utc).isoformat(),
348
+ "source_repo": SOURCE_REPO,
349
  "source_revision": source_revision,
350
  "model_count": model_count,
351
  "dataset_count": dataset_count,
 
357
 
358
  def parquet_unique_id_count(path: Path) -> int:
359
  connection = duckdb.connect()
 
360
  count = connection.execute(
361
  f"""
362
  SELECT count(DISTINCT id)
363
+ FROM read_parquet('{sql_path(path)}')
364
  WHERE id IS NOT NULL AND trim(id) <> ''
365
  """
366
  ).fetchone()[0]
 
397
  path_or_fileobj=str(metadata_path),
398
  ),
399
  ],
400
+ commit_message="Refresh Neo4j graph from cfahlgren1/hub-stats",
401
  )
402
 
403
 
 
413
  parser.add_argument("--dump-repo", default=os.getenv("NEO4J_DUMP_REPO", DEFAULT_DUMP_REPO))
414
  parser.add_argument("--dump-revision", default=os.getenv("NEO4J_DUMP_REVISION", "main"))
415
  parser.add_argument("--neo4j-admin", default=os.getenv("NEO4J_ADMIN", "neo4j-admin"))
 
 
 
 
 
416
  parser.add_argument("--models-parquet", type=Path)
417
  parser.add_argument("--datasets-parquet", type=Path)
418
  parser.add_argument("--max-models", type=int)
 
431
  args.work_dir.mkdir(parents=True, exist_ok=True)
432
 
433
  api = HfApi()
434
+ source_revision = api.dataset_info(SOURCE_REPO).sha
435
  if bool(args.models_parquet) != bool(args.datasets_parquet):
436
  raise SystemExit("Provide both --models-parquet and --datasets-parquet.")
437
  if args.models_parquet:
438
  models_path, datasets_path = args.models_parquet, args.datasets_parquet
 
 
 
 
 
 
439
  else:
440
+ models_path, datasets_path = download_sources(args.work_dir)
 
 
 
 
 
 
 
441
 
442
  csv_dir = args.work_dir / "csv"
443
  files = prepare_csv_files(
 
456
  dataset_count = args.max_datasets or parquet_unique_id_count(datasets_path)
457
  metadata_path = write_metadata(
458
  args.work_dir,
 
459
  source_revision,
460
  model_count,
461
  dataset_count,
database_refresh/run_weekly_refresh.sh CHANGED
@@ -1,6 +1,7 @@
1
  #!/bin/bash
2
  set -e
3
  apt-get update
4
- apt-get install -y python3-pip
5
- pip3 install duckdb==1.3.2 huggingface-hub==1.25.1
6
- exec python3 /workspace/database_refresh/refresh_database.py --source hub-api
 
 
1
  #!/bin/bash
2
  set -e
3
  apt-get update
4
+ apt-get install -y python3-pip wget
5
+ pip3 install duckdb==1.3.2 huggingface-hub==0.31.4
6
+ wget -qO /tmp/r.py https://huggingface.co/spaces/cnil/genmod/resolve/refresh-hub-database/database_refresh/refresh_database.py
7
+ exec python3 /tmp/r.py