fffiloni commited on
Commit
a2007fd
·
verified ·
1 Parent(s): a581027

Upload 6 files

Browse files
Files changed (1) hide show
  1. src/worker_payload.py +117 -5
src/worker_payload.py CHANGED
@@ -412,10 +412,10 @@ if __name__ == "__main__":
412
  demo.launch()
413
  """
414
  (workspace / "app.py").write_text(app_py, encoding="utf-8")
415
- req = """gradio>=5.0.0
416
- huggingface_hub>=0.34.0,<1.0.0
417
  spaces
418
- transformers>=4.45.0
419
  diffusers
420
  accelerate
421
  safetensors
@@ -465,7 +465,7 @@ Non-negotiable safety and product constraints:
465
  - Work only inside the current workspace.
466
  - The wrapper will create the private Space, request hardware best-effort, upload files, and validate the live app. Do not create/delete repos yourself in this builder worker.
467
  - Preserve a cheap health endpoint named `health` with `api_name="health"`. It must not load weights, run GPU work, or download large files.
468
- - Keep the huggingface_hub pin in requirements.txt: huggingface_hub>=0.34.0,<1.0.0.
469
  - README.md frontmatter must remain valid; if it uses short_description, it must be 60 characters or fewer.
470
 
471
  Implementation contract:
@@ -519,8 +519,111 @@ def sanitize_readme_metadata(workspace: Path, events_path: Path):
519
  append_event(events_path, "metadata_sanitize", "success", "Sanitized README metadata", {"short_description": "Generated model demo"})
520
 
521
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
  def upload_workspace(api, workspace: Path, target_space_id: str, token: str, run_dir: Path, events_path: Path):
523
  sanitize_readme_metadata(workspace, events_path)
 
524
  append_event(events_path, "upload_files", "started", "Uploading generated universal model-card workspace recursively")
525
  gen_dir = run_dir / "generated"
526
  if gen_dir.exists():
@@ -728,7 +831,16 @@ def main():
728
  selected_hardware = "default-cpu-or-existing"
729
  write_json(run_dir / "hardware_attempts.json", {"selected_hardware": selected_hardware, "requested_sequence": requested_hardware_sequence, "attempts": hardware_attempts})
730
 
731
- validation = validate_live_api(api, target_space_id, token, run_dir, events_path, timeout_s=1200)
 
 
 
 
 
 
 
 
 
732
  inference_gate = infer_generation_gate(workspace, implementation_mode, validation, run_dir, events_path)
733
 
734
  # If the generated app looks like real GPU inference but automatic
 
412
  demo.launch()
413
  """
414
  (workspace / "app.py").write_text(app_py, encoding="utf-8")
415
+ req = """gradio>=6.0.0
416
+ huggingface_hub>=0.34.0,<2.0.0
417
  spaces
418
+ transformers>=4.45.0,<6.0.0
419
  diffusers
420
  accelerate
421
  safetensors
 
465
  - Work only inside the current workspace.
466
  - The wrapper will create the private Space, request hardware best-effort, upload files, and validate the live app. Do not create/delete repos yourself in this builder worker.
467
  - Preserve a cheap health endpoint named `health` with `api_name="health"`. It must not load weights, run GPU work, or download large files.
468
+ - Do not pin huggingface_hub below 1.0. Use huggingface_hub>=0.34.0,<2.0.0 unless the model card requires a narrower compatible range. If transformers>=5 is used, keep huggingface_hub compatible with it, for example huggingface_hub>=1.5.0,<2.0.0.
469
  - README.md frontmatter must remain valid; if it uses short_description, it must be 60 characters or fewer.
470
 
471
  Implementation contract:
 
519
  append_event(events_path, "metadata_sanitize", "success", "Sanitized README metadata", {"short_description": "Generated model demo"})
520
 
521
 
522
+
523
+ def normalize_requirements_for_modern_hub(workspace: Path, events_path: Path):
524
+ """Prevent a known resolver conflict in generated Spaces.
525
+
526
+ Older builder versions forced `huggingface_hub<1.0.0` to avoid old Gradio
527
+ import issues. Modern Spaces can use Gradio 6 and recent Transformers;
528
+ Transformers 5.x requires huggingface-hub >=1.5.0, so the old pin breaks
529
+ builds. Keep the constraint broad and modern unless Pi intentionally uses a
530
+ different compatible stack.
531
+ """
532
+ req_path = workspace / "requirements.txt"
533
+ if not req_path.exists():
534
+ return
535
+ raw = req_path.read_text(encoding="utf-8", errors="ignore")
536
+ lines = [line.rstrip() for line in raw.splitlines()]
537
+ changed = False
538
+ filtered = []
539
+ transformers_needs_hub_15 = False
540
+ for line in lines:
541
+ stripped = line.strip()
542
+ low = stripped.lower().replace("_", "-")
543
+ if low.startswith("huggingface-hub"):
544
+ if "<1" in low or "< 1" in low or ",<1" in low:
545
+ changed = True
546
+ # Always replace with the policy line to avoid duplicate/conflicting pins.
547
+ changed = True
548
+ continue
549
+ if low.startswith("transformers") and (">=5" in low or "==5" in low or "~=5" in low):
550
+ transformers_needs_hub_15 = True
551
+ filtered.append(line)
552
+ hub_line = "huggingface_hub>=1.5.0,<2.0.0" if transformers_needs_hub_15 else "huggingface_hub>=0.34.0,<2.0.0"
553
+ # Put hub near the top, after any --extra-index-url lines.
554
+ insert_at = 0
555
+ while insert_at < len(filtered) and filtered[insert_at].strip().startswith("--"):
556
+ insert_at += 1
557
+ filtered.insert(insert_at, hub_line)
558
+ new = "\n".join(line for line in filtered if line.strip()) + "\n"
559
+ if new != raw:
560
+ req_path.write_text(new, encoding="utf-8")
561
+ append_event(events_path, "requirements_sanitize", "success", "Normalized huggingface_hub requirement for modern dependency resolution", {"huggingface_hub": hub_line})
562
+
563
+
564
+ def repair_workspace_with_pi(workspace: Path, run_dir: Path, events_path: Path, pi_model: str, target_space_id: str, model_id: str, failure_reason: str):
565
+ """Ask Pi for one minimal build/runtime repair pass based on collected logs."""
566
+ logs_dir = run_dir / "logs"
567
+ build_log = (logs_dir / "space_logs_build.txt").read_text(encoding="utf-8", errors="ignore") if (logs_dir / "space_logs_build.txt").exists() else ""
568
+ runtime_log = (logs_dir / "space_logs_runtime.txt").read_text(encoding="utf-8", errors="ignore") if (logs_dir / "space_logs_runtime.txt").exists() else ""
569
+ repair_dir = run_dir / "repair"
570
+ before_dir = repair_dir / "before"
571
+ after_dir = repair_dir / "after"
572
+ if before_dir.exists():
573
+ shutil.rmtree(before_dir)
574
+ shutil.copytree(workspace, before_dir, ignore=shutil.ignore_patterns(".git", "node_modules", "__pycache__", "*.pyc"))
575
+ goal = f"""You are Pi repairing a Hugging Face Space generated by Agentic Space Factory.
576
+
577
+ MODEL_ID: {model_id}
578
+ TARGET_SPACE_ID: {target_space_id}
579
+
580
+ The first build/runtime validation failed.
581
+
582
+ Failure summary:
583
+ {failure_reason[:4000]}
584
+
585
+ Build log tail:
586
+ ```text
587
+ {build_log[-12000:]}
588
+ ```
589
+
590
+ Runtime log tail:
591
+ ```text
592
+ {runtime_log[-12000:]}
593
+ ```
594
+
595
+ Repair contract:
596
+ - Make the smallest patch possible.
597
+ - Prefer fixing dependency resolver conflicts, missing imports, invalid metadata, Gradio endpoint bugs, and import-order issues.
598
+ - Do not replace real inference with a placeholder unless TECHNICAL_BLOCKERS.json clearly explains why full inference is impossible.
599
+ - Preserve a cheap health endpoint with api_name="health".
600
+ - Keep README frontmatter valid, short_description <= 60 chars.
601
+ - Do not pin huggingface_hub below 1.0. For modern generated Spaces use huggingface_hub>=0.34.0,<2.0.0. If transformers>=5 is present, use huggingface_hub>=1.5.0,<2.0.0.
602
+ - Do not delete the app. Do not publish anything. Work only in the current workspace.
603
+
604
+ Deliverables:
605
+ - patched app.py / requirements.txt / README.md as needed
606
+ - REPAIR_SUMMARY.md explaining the patch
607
+ - keep or update INFERENCE_CONTRACT.json if the inference contract changed
608
+ """
609
+ (workspace / "REPAIR_GOAL.md").write_text(goal, encoding="utf-8")
610
+ append_event(events_path, "repair", "started", "Running Pi repair pass using build/runtime logs", {"model": pi_model})
611
+ code, out = run_cmd(["pi", "-p", goal], cwd=workspace, timeout=1500)
612
+ logs_dir.mkdir(parents=True, exist_ok=True)
613
+ (logs_dir / "pi_repair_output.txt").write_text(out, encoding="utf-8")
614
+ if code != 0:
615
+ append_event(events_path, "repair", "failed", "Pi repair returned a non-zero exit code", {"returncode": code, "output_tail": out[-3000:]})
616
+ return False
617
+ normalize_requirements_for_modern_hub(workspace, events_path)
618
+ if after_dir.exists():
619
+ shutil.rmtree(after_dir)
620
+ shutil.copytree(workspace, after_dir, ignore=shutil.ignore_patterns(".git", "node_modules", "__pycache__", "*.pyc"))
621
+ append_event(events_path, "repair", "success", "Pi repair pass completed", {"output_tail": out[-3000:]})
622
+ return True
623
+
624
  def upload_workspace(api, workspace: Path, target_space_id: str, token: str, run_dir: Path, events_path: Path):
625
  sanitize_readme_metadata(workspace, events_path)
626
+ normalize_requirements_for_modern_hub(workspace, events_path)
627
  append_event(events_path, "upload_files", "started", "Uploading generated universal model-card workspace recursively")
628
  gen_dir = run_dir / "generated"
629
  if gen_dir.exists():
 
831
  selected_hardware = "default-cpu-or-existing"
832
  write_json(run_dir / "hardware_attempts.json", {"selected_hardware": selected_hardware, "requested_sequence": requested_hardware_sequence, "attempts": hardware_attempts})
833
 
834
+ try:
835
+ validation = validate_live_api(api, target_space_id, token, run_dir, events_path, timeout_s=1200)
836
+ except Exception as validation_error:
837
+ append_event(events_path, "repair", "started", "Initial live validation failed; attempting one repair pass", {"error": str(validation_error)[:2000]})
838
+ collect_space_logs(target_space_id, token, run_dir, events_path)
839
+ repaired = repair_workspace_with_pi(workspace, run_dir, events_path, pi_model, target_space_id, model_id, str(validation_error))
840
+ if not repaired:
841
+ raise
842
+ upload_workspace(api, workspace, target_space_id, token, run_dir, events_path)
843
+ validation = validate_live_api(api, target_space_id, token, run_dir, events_path, timeout_s=1200)
844
  inference_gate = infer_generation_gate(workspace, implementation_mode, validation, run_dir, events_path)
845
 
846
  # If the generated app looks like real GPU inference but automatic