someone-in-the-world Claude Sonnet 4.6 commited on
Commit
dccc3f5
·
1 Parent(s): ba8e81a

Fix image preview: build Parquet directly with PyArrow + HF schema metadata

Browse files

datasets.Dataset was not writing the Arrow Image extension type metadata
correctly. Build the table directly with PyArrow using the exact struct
schema {bytes: binary, path: utf8} and a 'huggingface' schema metadata
key — the format the HF dataset viewer requires to render image columns.
Use replace_schema_metadata after concat to preserve the metadata.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (2) hide show
  1. app.py +68 -49
  2. requirements.txt +1 -1
app.py CHANGED
@@ -192,24 +192,43 @@ def log_inference(pil_inputs, output_pil, prompt, seed, steps, guidance_scale,
192
  if not HF_TOKEN or not DATASET_REPO:
193
  return
194
  try:
195
- import tempfile
196
- import datasets as ds_lib
 
197
  from huggingface_hub import HfApi, hf_hub_download
198
 
199
- features = ds_lib.Features({
200
- "timestamp": ds_lib.Value("string"),
201
- "prompt": ds_lib.Value("string"),
202
- "seed": ds_lib.Value("int32"),
203
- "steps": ds_lib.Value("int32"),
204
- "guidance_scale": ds_lib.Value("float32"),
205
- "input_images": ds_lib.Sequence(ds_lib.Image()),
206
- "output_image": ds_lib.Image(),
207
- "duration_seconds": ds_lib.Value("float32"),
208
- "input_width": ds_lib.Value("int32"),
209
- "input_height": ds_lib.Value("int32"),
210
- "success": ds_lib.Value("bool"),
211
- "error_message": ds_lib.Value("string"),
212
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  def _to_jpeg(img, max_dim=1536, quality=85):
214
  if img is None:
215
  return None
@@ -221,58 +240,58 @@ def log_inference(pil_inputs, output_pil, prompt, seed, steps, guidance_scale,
221
  img.convert("RGB").save(buf, format="JPEG", quality=quality)
222
  return buf.getvalue()
223
 
224
- row = {
225
- "timestamp": datetime.now(timezone.utc).isoformat(),
226
- "prompt": prompt,
227
- "seed": int(seed),
228
- "steps": int(steps),
229
- "guidance_scale": float(guidance_scale),
230
- "input_images": [_to_jpeg(img) for img in pil_inputs],
231
- "output_image": _to_jpeg(output_pil),
232
- "duration_seconds": float(duration_seconds),
233
- "input_width": int(input_width),
234
- "input_height": int(input_height),
235
- "success": bool(success),
236
- "error_message": str(error_message),
237
- }
238
- print(f"[log] building dataset row (success={success})")
239
- new_ds = ds_lib.Dataset.from_dict(
240
- {k: [v] for k, v in row.items()}, features=features
241
- )
242
- print(f"[log] row built — features: { {k: str(v) for k, v in new_ds.features.items()} }")
 
 
243
 
244
  today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
245
  path_in_repo = f"data/{today}.parquet"
246
  api = HfApi(token=HF_TOKEN)
247
-
248
- print(f"[log] ensuring repo {DATASET_REPO} exists")
249
  api.create_repo(repo_id=DATASET_REPO, repo_type="dataset", private=True, exist_ok=True)
250
- print(f"[log] repo ready")
251
 
252
  try:
253
- print(f"[log] downloading existing {path_in_repo}")
254
  local_path = hf_hub_download(
255
  repo_id=DATASET_REPO, filename=path_in_repo,
256
  repo_type="dataset", token=HF_TOKEN,
257
  )
258
- existing_ds = ds_lib.Dataset.from_parquet(local_path, features=features)
259
- print(f"[log] existing file has {len(existing_ds)} row(s), appending")
260
- combined_ds = ds_lib.concatenate_datasets([existing_ds, new_ds])
 
261
  except Exception as dl_err:
262
  print(f"[log] no existing file ({dl_err}), starting fresh")
263
- combined_ds = new_ds
264
 
265
- with tempfile.NamedTemporaryFile(suffix=".parquet", delete=False) as tmp_out:
266
- tmp_out_path = tmp_out.name
267
- combined_ds.to_parquet(tmp_out_path)
268
- print(f"[log] uploading {path_in_repo} ({len(combined_ds)} row(s) total, {os.path.getsize(tmp_out_path)//1024}KB)")
269
  api.upload_file(
270
- path_or_fileobj=tmp_out_path, path_in_repo=path_in_repo,
271
  repo_id=DATASET_REPO, repo_type="dataset",
272
  )
273
  print(f"[log] upload done — {DATASET_REPO}/{path_in_repo}")
274
  except Exception as log_err:
275
- print(f"[log] WARNING: failed to push log row: {log_err}")
 
276
 
277
 
278
  @spaces.GPU
 
192
  if not HF_TOKEN or not DATASET_REPO:
193
  return
194
  try:
195
+ import tempfile, json as _json
196
+ import pyarrow as pa
197
+ import pyarrow.parquet as pq
198
  from huggingface_hub import HfApi, hf_hub_download
199
 
200
+ # Image columns need Arrow struct {bytes: binary, path: utf8} plus
201
+ # a 'huggingface' schema metadata key for the HF viewer to render them.
202
+ img_struct = pa.struct([("bytes", pa.binary()), ("path", pa.string())])
203
+ hf_meta = _json.dumps({"info": {"features": {
204
+ "timestamp": {"dtype": "string", "_type": "Value"},
205
+ "prompt": {"dtype": "string", "_type": "Value"},
206
+ "seed": {"dtype": "int32", "_type": "Value"},
207
+ "steps": {"dtype": "int32", "_type": "Value"},
208
+ "guidance_scale": {"dtype": "float32", "_type": "Value"},
209
+ "input_images": {"feature": {"_type": "Image"}, "_type": "Sequence"},
210
+ "output_image": {"_type": "Image"},
211
+ "duration_seconds": {"dtype": "float32", "_type": "Value"},
212
+ "input_width": {"dtype": "int32", "_type": "Value"},
213
+ "input_height": {"dtype": "int32", "_type": "Value"},
214
+ "success": {"dtype": "bool", "_type": "Value"},
215
+ "error_message": {"dtype": "string", "_type": "Value"},
216
+ }}}).encode()
217
+ schema = pa.schema([
218
+ ("timestamp", pa.string()),
219
+ ("prompt", pa.string()),
220
+ ("seed", pa.int32()),
221
+ ("steps", pa.int32()),
222
+ ("guidance_scale", pa.float32()),
223
+ ("input_images", pa.list_(img_struct)),
224
+ ("output_image", img_struct),
225
+ ("duration_seconds", pa.float32()),
226
+ ("input_width", pa.int32()),
227
+ ("input_height", pa.int32()),
228
+ ("success", pa.bool_()),
229
+ ("error_message", pa.string()),
230
+ ], metadata={b"huggingface": hf_meta})
231
+
232
  def _to_jpeg(img, max_dim=1536, quality=85):
233
  if img is None:
234
  return None
 
240
  img.convert("RGB").save(buf, format="JPEG", quality=quality)
241
  return buf.getvalue()
242
 
243
+ def _img(b):
244
+ return {"bytes": b, "path": None}
245
+
246
+ input_jpegs = [_to_jpeg(img) for img in pil_inputs]
247
+ output_jpeg = _to_jpeg(output_pil)
248
+
249
+ new_table = pa.table({
250
+ "timestamp": pa.array([datetime.now(timezone.utc).isoformat()], type=pa.string()),
251
+ "prompt": pa.array([prompt], type=pa.string()),
252
+ "seed": pa.array([int(seed)], type=pa.int32()),
253
+ "steps": pa.array([int(steps)], type=pa.int32()),
254
+ "guidance_scale": pa.array([float(guidance_scale)], type=pa.float32()),
255
+ "input_images": pa.array([[_img(b) for b in input_jpegs]], type=pa.list_(img_struct)),
256
+ "output_image": pa.array([_img(output_jpeg) if output_jpeg else None], type=img_struct),
257
+ "duration_seconds": pa.array([float(duration_seconds)], type=pa.float32()),
258
+ "input_width": pa.array([int(input_width)], type=pa.int32()),
259
+ "input_height": pa.array([int(input_height)], type=pa.int32()),
260
+ "success": pa.array([bool(success)], type=pa.bool_()),
261
+ "error_message": pa.array([str(error_message)], type=pa.string()),
262
+ }, schema=schema)
263
+ print(f"[log] built row — success={success}, inputs={len(input_jpegs)}")
264
 
265
  today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
266
  path_in_repo = f"data/{today}.parquet"
267
  api = HfApi(token=HF_TOKEN)
 
 
268
  api.create_repo(repo_id=DATASET_REPO, repo_type="dataset", private=True, exist_ok=True)
 
269
 
270
  try:
 
271
  local_path = hf_hub_download(
272
  repo_id=DATASET_REPO, filename=path_in_repo,
273
  repo_type="dataset", token=HF_TOKEN,
274
  )
275
+ existing = pq.read_table(local_path)
276
+ combined = pa.concat_tables([existing, new_table])
277
+ combined = combined.replace_schema_metadata(schema.metadata)
278
+ print(f"[log] appending to existing {existing.num_rows} row(s)")
279
  except Exception as dl_err:
280
  print(f"[log] no existing file ({dl_err}), starting fresh")
281
+ combined = new_table
282
 
283
+ with tempfile.NamedTemporaryFile(suffix=".parquet", delete=False) as tmp:
284
+ tmp_path = tmp.name
285
+ pq.write_table(combined, tmp_path)
286
+ print(f"[log] uploading {path_in_repo} ({combined.num_rows} row(s), {os.path.getsize(tmp_path)//1024}KB)")
287
  api.upload_file(
288
+ path_or_fileobj=tmp_path, path_in_repo=path_in_repo,
289
  repo_id=DATASET_REPO, repo_type="dataset",
290
  )
291
  print(f"[log] upload done — {DATASET_REPO}/{path_in_repo}")
292
  except Exception as log_err:
293
+ import traceback as _tb
294
+ print(f"[log] WARNING: {log_err}\n{_tb.format_exc()}")
295
 
296
 
297
  @spaces.GPU
requirements.txt CHANGED
@@ -3,7 +3,7 @@ git+https://github.com/huggingface/diffusers.git
3
  git+https://github.com/huggingface/peft.git
4
  transformers==4.57.1
5
  huggingface_hub
6
- datasets
7
  sentencepiece
8
  torchvision
9
  kernels
 
3
  git+https://github.com/huggingface/peft.git
4
  transformers==4.57.1
5
  huggingface_hub
6
+ pyarrow
7
  sentencepiece
8
  torchvision
9
  kernels