Upload 15 files
Browse files- src/version.py +2 -2
- src/view_models.py +68 -11
- src/worker_payload.py +43 -3
src/version.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
-
ASF_APP_VERSION = "v198.26.
|
| 4 |
-
ASF_RELEASE_NAME = "Agentic Space Factory v198.26.
|
| 5 |
|
| 6 |
|
| 7 |
def resolve_app_version(value: str | None = None) -> str:
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
|
| 3 |
+
ASF_APP_VERSION = "v198.26.8"
|
| 4 |
+
ASF_RELEASE_NAME = "Agentic Space Factory v198.26.8"
|
| 5 |
|
| 6 |
|
| 7 |
def resolve_app_version(value: str | None = None) -> str:
|
src/view_models.py
CHANGED
|
@@ -454,8 +454,13 @@ def build_diagnostics(bundle: dict[str, Any], status_model: dict[str, Any], phas
|
|
| 454 |
issue_detail = "Automatic hardware selection was not available. Choose hardware in Space settings, then run Space Test."
|
| 455 |
issue_status = "action_required"
|
| 456 |
elif status_model["global_status"] == "failed":
|
| 457 |
-
|
| 458 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 459 |
issue_status = "open"
|
| 460 |
|
| 461 |
health_passed = bool((gate.get("implementation_signals") or {}).get("health_passed") or smoke.get("health_passed"))
|
|
@@ -536,21 +541,65 @@ def _space_target_url_from_bundle(bundle: dict[str, Any], target: str = "") -> s
|
|
| 536 |
return f"https://huggingface.co/spaces/{target}" if target else ""
|
| 537 |
|
| 538 |
|
| 539 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 540 |
identity = bundle.get("space_identity") or {}
|
| 541 |
-
if identity.get("links_ready") or identity.get("space_created") or identity.get("space_uploaded"):
|
| 542 |
-
return True
|
| 543 |
runtime_upload_epoch = bundle.get("runtime_upload_epoch") or {}
|
| 544 |
-
if runtime_upload_epoch.get("
|
| 545 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 546 |
for event in bundle.get("events") or []:
|
| 547 |
if not isinstance(event, dict):
|
| 548 |
continue
|
| 549 |
step = str(event.get("step") or "")
|
| 550 |
status = str(event.get("status") or "").lower()
|
| 551 |
-
if status == "success" and step in {"create_space", "create_space_hardware"
|
| 552 |
-
|
| 553 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 554 |
|
| 555 |
|
| 556 |
def _known_gradio_endpoint_info(bundle: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -744,11 +793,18 @@ def build_run_view_model(
|
|
| 744 |
elapsed_seconds = int((end - created).total_seconds())
|
| 745 |
target = _space_target_from_bundle(bundle)
|
| 746 |
target_url = _space_target_url_from_bundle(bundle, target)
|
|
|
|
| 747 |
links = {
|
| 748 |
"job_url": _job_url_from_view_sources(bucket_source=bucket_source, summary=summary, state=state, launch=bundle.get("launch") or {}),
|
| 749 |
"target_space_url": target_url,
|
| 750 |
"target_space_settings_url": f"{target_url}/settings" if target_url else "",
|
| 751 |
"artifacts_url": summary.get("artifacts_url") or f"https://huggingface.co/buckets/{bucket_source}/tree/{_runs_prefix()}/{run_id}",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 752 |
}
|
| 753 |
return {
|
| 754 |
"schema_version": "run_view_model.v1",
|
|
@@ -777,6 +833,7 @@ def build_run_view_model(
|
|
| 777 |
"diagnostics": build_diagnostics(bundle, status_model, phase),
|
| 778 |
"space_test": {**build_space_test_model(bundle, status_model), "linked_validation": manual_validation, "mode": "linked_build_run"},
|
| 779 |
"space_test_policy": space_test_policy,
|
|
|
|
| 780 |
"manual_validation": manual_validation,
|
| 781 |
"post_build_validation": effective_run_status.get("post_build_validation") or {},
|
| 782 |
"display_status": effective_run_status.get("display_status") or status_model.get("global_status"),
|
|
@@ -785,7 +842,7 @@ def build_run_view_model(
|
|
| 785 |
"actions": {
|
| 786 |
"can_resume": status_model["global_status"] in {"running", "stale", "unknown"},
|
| 787 |
"can_stop": status_model["global_status"] in {"running", "queued", "unknown"},
|
| 788 |
-
"can_open_space": bool(
|
| 789 |
"can_validate": bool(space_test_policy.get("enabled")),
|
| 790 |
"requires_manual_action": status_model["requires_manual_action"],
|
| 791 |
},
|
|
|
|
| 454 |
issue_detail = "Automatic hardware selection was not available. Choose hardware in Space settings, then run Space Test."
|
| 455 |
issue_status = "action_required"
|
| 456 |
elif status_model["global_status"] == "failed":
|
| 457 |
+
link_state = build_space_link_state(bundle)
|
| 458 |
+
if link_state.get("links_ready"):
|
| 459 |
+
issue_title = "Validation failed after Space upload"
|
| 460 |
+
issue_detail = "The generated Space exists and remains available, but automatic validation or recovery ended in failure."
|
| 461 |
+
else:
|
| 462 |
+
issue_title = "Run failed"
|
| 463 |
+
issue_detail = "Inspect logs and run artifacts for the failing step."
|
| 464 |
issue_status = "open"
|
| 465 |
|
| 466 |
health_passed = bool((gate.get("implementation_signals") or {}).get("health_passed") or smoke.get("health_passed"))
|
|
|
|
| 541 |
return f"https://huggingface.co/spaces/{target}" if target else ""
|
| 542 |
|
| 543 |
|
| 544 |
+
def build_space_link_state(bundle: dict[str, Any], *, target: str = "", target_url: str = "") -> dict[str, Any]:
|
| 545 |
+
"""Return durable Space link readiness for Active Run.
|
| 546 |
+
|
| 547 |
+
v198.26.8 invariant: Space links are a persisted run fact. They must not
|
| 548 |
+
depend on the frontend receiving the create/upload event inside a truncated
|
| 549 |
+
recent-events window. If a runtime upload epoch or Space runtime probe is
|
| 550 |
+
present, a terminal failed run still gets Open Space / Settings links.
|
| 551 |
+
"""
|
| 552 |
identity = bundle.get("space_identity") or {}
|
|
|
|
|
|
|
| 553 |
runtime_upload_epoch = bundle.get("runtime_upload_epoch") or {}
|
| 554 |
+
runtime_history = runtime_upload_epoch.get("history") if isinstance(runtime_upload_epoch.get("history"), list) else []
|
| 555 |
+
space_runtime = bundle.get("space_runtime") or {}
|
| 556 |
+
target = _space_target_from_bundle(bundle) if not target else _normalize_target_space_id(target)
|
| 557 |
+
target_url = target_url or _space_target_url_from_bundle(bundle, target)
|
| 558 |
+
create_event_ok = False
|
| 559 |
+
upload_event_ok = False
|
| 560 |
for event in bundle.get("events") or []:
|
| 561 |
if not isinstance(event, dict):
|
| 562 |
continue
|
| 563 |
step = str(event.get("step") or "")
|
| 564 |
status = str(event.get("status") or "").lower()
|
| 565 |
+
if status == "success" and step in {"create_space", "create_space_hardware"}:
|
| 566 |
+
create_event_ok = True
|
| 567 |
+
if status == "success" and step in {"upload_files", "runtime_upload_epoch"}:
|
| 568 |
+
upload_event_ok = True
|
| 569 |
+
runtime_uploaded = bool(identity.get("runtime_uploaded") or identity.get("space_uploaded") or runtime_upload_epoch.get("last_upload_completed_at") or runtime_upload_epoch.get("upload_sequence") or runtime_history or upload_event_ok)
|
| 570 |
+
space_runtime_known = bool(identity.get("space_runtime_known") or (isinstance(space_runtime, dict) and (space_runtime.get("stage") or space_runtime.get("status") or space_runtime.get("runtime_status") or space_runtime.get("updated_at") or space_runtime.get("url"))))
|
| 571 |
+
space_created = bool(identity.get("space_created") or (target and (create_event_ok or runtime_uploaded or space_runtime_known)))
|
| 572 |
+
links_ready = bool(target_url and (identity.get("links_ready") or space_created or runtime_uploaded or space_runtime_known))
|
| 573 |
+
sources: list[str] = []
|
| 574 |
+
if identity.get("source"):
|
| 575 |
+
sources.append(str(identity.get("source")))
|
| 576 |
+
if create_event_ok:
|
| 577 |
+
sources.append("create_space_event")
|
| 578 |
+
if upload_event_ok:
|
| 579 |
+
sources.append("upload_event")
|
| 580 |
+
if runtime_uploaded:
|
| 581 |
+
sources.append("runtime_upload_epoch")
|
| 582 |
+
if space_runtime_known:
|
| 583 |
+
sources.append("space_runtime")
|
| 584 |
+
return {
|
| 585 |
+
"schema_version": "space_link_state.v198_26_8",
|
| 586 |
+
"target_space": target,
|
| 587 |
+
"target_space_id": target,
|
| 588 |
+
"target_space_known": bool(target),
|
| 589 |
+
"target_space_url": target_url,
|
| 590 |
+
"target_space_settings_url": f"{target_url}/settings" if target_url else "",
|
| 591 |
+
"space_created": bool(space_created),
|
| 592 |
+
"runtime_uploaded": bool(runtime_uploaded),
|
| 593 |
+
"space_uploaded": bool(runtime_uploaded),
|
| 594 |
+
"space_runtime_known": bool(space_runtime_known),
|
| 595 |
+
"links_ready": bool(links_ready),
|
| 596 |
+
"can_open_space": bool(links_ready),
|
| 597 |
+
"source": "+".join(dict.fromkeys([s for s in sources if s])) or "resolved_from_persisted_artifacts",
|
| 598 |
+
}
|
| 599 |
+
|
| 600 |
+
|
| 601 |
+
def _space_links_ready_from_bundle(bundle: dict[str, Any]) -> bool:
|
| 602 |
+
return bool(build_space_link_state(bundle).get("links_ready"))
|
| 603 |
|
| 604 |
|
| 605 |
def _known_gradio_endpoint_info(bundle: dict[str, Any]) -> dict[str, Any]:
|
|
|
|
| 793 |
elapsed_seconds = int((end - created).total_seconds())
|
| 794 |
target = _space_target_from_bundle(bundle)
|
| 795 |
target_url = _space_target_url_from_bundle(bundle, target)
|
| 796 |
+
space_link_state = build_space_link_state(bundle, target=target, target_url=target_url)
|
| 797 |
links = {
|
| 798 |
"job_url": _job_url_from_view_sources(bucket_source=bucket_source, summary=summary, state=state, launch=bundle.get("launch") or {}),
|
| 799 |
"target_space_url": target_url,
|
| 800 |
"target_space_settings_url": f"{target_url}/settings" if target_url else "",
|
| 801 |
"artifacts_url": summary.get("artifacts_url") or f"https://huggingface.co/buckets/{bucket_source}/tree/{_runs_prefix()}/{run_id}",
|
| 802 |
+
"links_ready": bool(space_link_state.get("links_ready")),
|
| 803 |
+
"space_created": bool(space_link_state.get("space_created")),
|
| 804 |
+
"space_uploaded": bool(space_link_state.get("runtime_uploaded")),
|
| 805 |
+
"runtime_uploaded": bool(space_link_state.get("runtime_uploaded")),
|
| 806 |
+
"space_runtime_known": bool(space_link_state.get("space_runtime_known")),
|
| 807 |
+
"source": space_link_state.get("source") or "",
|
| 808 |
}
|
| 809 |
return {
|
| 810 |
"schema_version": "run_view_model.v1",
|
|
|
|
| 833 |
"diagnostics": build_diagnostics(bundle, status_model, phase),
|
| 834 |
"space_test": {**build_space_test_model(bundle, status_model), "linked_validation": manual_validation, "mode": "linked_build_run"},
|
| 835 |
"space_test_policy": space_test_policy,
|
| 836 |
+
"space_link_state": space_link_state,
|
| 837 |
"manual_validation": manual_validation,
|
| 838 |
"post_build_validation": effective_run_status.get("post_build_validation") or {},
|
| 839 |
"display_status": effective_run_status.get("display_status") or status_model.get("global_status"),
|
|
|
|
| 842 |
"actions": {
|
| 843 |
"can_resume": status_model["global_status"] in {"running", "stale", "unknown"},
|
| 844 |
"can_stop": status_model["global_status"] in {"running", "queued", "unknown"},
|
| 845 |
+
"can_open_space": bool(space_link_state.get("can_open_space")),
|
| 846 |
"can_validate": bool(space_test_policy.get("enabled")),
|
| 847 |
"requires_manual_action": status_model["requires_manual_action"],
|
| 848 |
},
|
src/worker_payload.py
CHANGED
|
@@ -35,8 +35,8 @@ DEFAULT_PREFERRED_SPACE_HARDWARE = "zero-a10g"
|
|
| 35 |
DEFAULT_FALLBACK_SPACE_HARDWARE = "a10g-large"
|
| 36 |
DEFAULT_MODEL_ID = "sshleifer/tiny-gpt2"
|
| 37 |
MAX_PI_REPAIR_ATTEMPTS = 3
|
| 38 |
-
APP_VERSION = "v198.26.
|
| 39 |
-
app_version = "v198.26.
|
| 40 |
|
| 41 |
# Internal agent/recovery files may be needed inside the transient Pi
|
| 42 |
# workspace, but they should not be published to the generated Space or shown
|
|
@@ -2056,6 +2056,7 @@ def fail(run_dir: Path, events_path: Path, message: str, details: dict | None =
|
|
| 2056 |
write_json(run_dir / "state.json", failure_state)
|
| 2057 |
cleanup_status = run_final_cleanup_if_needed(run_dir, events_path, failure_state)
|
| 2058 |
failure_state["cleanup"] = cleanup_status
|
|
|
|
| 2059 |
write_final_summary(run_dir, failure_state, {}, {}, status=status, message=message)
|
| 2060 |
live_failure_data = dict(safe)
|
| 2061 |
live_failure_data["cleanup"] = cleanup_status
|
|
@@ -5250,6 +5251,45 @@ def reconcile_final_success_state(run_dir: Path, events_path: Path | None, final
|
|
| 5250 |
return clean
|
| 5251 |
|
| 5252 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5253 |
|
| 5254 |
def _normalize_dependency_package_name(value: str = "") -> str:
|
| 5255 |
name = re.split(r"[<>=!~;\[,\s]", str(value or "").strip(), 1)[0].strip().lower().replace("_", "-")
|
|
@@ -12463,7 +12503,7 @@ def smoke_generate(target_space_id: str, token: str, run_dir: Path, events_path:
|
|
| 12463 |
# write_json(run_dir / "tests" / "payload_source.json", payload_source_record)
|
| 12464 |
# write_json(run_dir / "tests" / "replay_source.json", replay_source)
|
| 12465 |
# write_json(run_dir / "tests" / "validation_preflight.json", validation_preflight)
|
| 12466 |
-
app_version = "v198.26.
|
| 12467 |
engine_version = "unified_gradio_validation_harness_v198_25_3"
|
| 12468 |
parent_build_run_id = os.environ.get("PARENT_BUILD_RUN_ID", "").strip()
|
| 12469 |
validation_mode = os.environ.get("VALIDATION_MODE") or os.environ.get("SPACE_TEST_POLICY_MODE") or "linked"
|
|
|
|
| 35 |
DEFAULT_FALLBACK_SPACE_HARDWARE = "a10g-large"
|
| 36 |
DEFAULT_MODEL_ID = "sshleifer/tiny-gpt2"
|
| 37 |
MAX_PI_REPAIR_ATTEMPTS = 3
|
| 38 |
+
APP_VERSION = "v198.26.8"
|
| 39 |
+
app_version = "v198.26.8"
|
| 40 |
|
| 41 |
# Internal agent/recovery files may be needed inside the transient Pi
|
| 42 |
# workspace, but they should not be published to the generated Space or shown
|
|
|
|
| 2056 |
write_json(run_dir / "state.json", failure_state)
|
| 2057 |
cleanup_status = run_final_cleanup_if_needed(run_dir, events_path, failure_state)
|
| 2058 |
failure_state["cleanup"] = cleanup_status
|
| 2059 |
+
write_terminal_status_reconciliation(run_dir, events_path, failure_state, status=status, message=message, details=safe, job_exit_code=1)
|
| 2060 |
write_final_summary(run_dir, failure_state, {}, {}, status=status, message=message)
|
| 2061 |
live_failure_data = dict(safe)
|
| 2062 |
live_failure_data["cleanup"] = cleanup_status
|
|
|
|
| 5251 |
return clean
|
| 5252 |
|
| 5253 |
|
| 5254 |
+
def write_terminal_status_reconciliation(run_dir: Path, events_path: Path | None, final_state: dict, *, status: str, message: str = "", details: dict | None = None, job_exit_code: int | None = None) -> dict:
|
| 5255 |
+
"""Persist a terminal truth artifact for every final outcome.
|
| 5256 |
+
|
| 5257 |
+
v198.26.8: the UI must not have to infer failed/stopped states from a mix
|
| 5258 |
+
of state, repair_outcome and recent events. Success already writes
|
| 5259 |
+
final_status_reconciliation.json; failed/auth/cancelled terminal paths now
|
| 5260 |
+
write the same artifact so Active Run can converge deterministically.
|
| 5261 |
+
"""
|
| 5262 |
+
state = final_state or {}
|
| 5263 |
+
safe = safe_details(details or {})
|
| 5264 |
+
runtime_upload_epoch = load_json_if_exists(run_dir / "runtime_upload_epoch.json") if (run_dir / "runtime_upload_epoch.json").exists() else {}
|
| 5265 |
+
space_runtime = load_json_if_exists(run_dir / "space_runtime.json") if (run_dir / "space_runtime.json").exists() else {}
|
| 5266 |
+
runtime_history = runtime_upload_epoch.get("history") if isinstance(runtime_upload_epoch.get("history"), list) else []
|
| 5267 |
+
target_space = state.get("target_space") or state.get("target_space_id") or runtime_upload_epoch.get("target_space_id") or os.environ.get("TARGET_SPACE_ID", "")
|
| 5268 |
+
runtime_uploaded = bool(runtime_upload_epoch.get("last_upload_completed_at") or runtime_upload_epoch.get("upload_sequence") or runtime_history)
|
| 5269 |
+
space_runtime_known = bool(isinstance(space_runtime, dict) and (space_runtime.get("stage") or space_runtime.get("status") or space_runtime.get("runtime_status") or space_runtime.get("updated_at") or space_runtime.get("url")))
|
| 5270 |
+
payload = {
|
| 5271 |
+
"schema_version": "final_status_reconciliation.v198_26_8",
|
| 5272 |
+
"status": status,
|
| 5273 |
+
"terminal": True,
|
| 5274 |
+
"message": message or state.get("message") or "",
|
| 5275 |
+
"job_exit_code": int(job_exit_code if job_exit_code is not None else (0 if status == "full_inference_success" else 1)),
|
| 5276 |
+
"target_space": target_space,
|
| 5277 |
+
"target_space_url": f"https://huggingface.co/spaces/{target_space}" if target_space else "",
|
| 5278 |
+
"space_created": bool(target_space and (runtime_uploaded or space_runtime_known)),
|
| 5279 |
+
"runtime_uploaded": bool(runtime_uploaded),
|
| 5280 |
+
"space_uploaded": bool(runtime_uploaded),
|
| 5281 |
+
"space_runtime_known": bool(space_runtime_known),
|
| 5282 |
+
"failure_type": safe.get("failure_type") or safe.get("repair_failure_type") or state.get("failure_type") or "",
|
| 5283 |
+
"failure_phase": safe.get("phase") or safe.get("failure_phase") or safe.get("step") or "",
|
| 5284 |
+
"details": safe,
|
| 5285 |
+
"updated_at": now(),
|
| 5286 |
+
}
|
| 5287 |
+
write_json(run_dir / "final_status_reconciliation.json", payload)
|
| 5288 |
+
if events_path is not None:
|
| 5289 |
+
append_event(events_path, "final_status_reconciliation", "success" if status == "full_inference_success" else "failed", "Wrote terminal final status reconciliation", {k: payload.get(k) for k in ("status", "job_exit_code", "target_space", "runtime_uploaded", "space_runtime_known", "failure_type")})
|
| 5290 |
+
return payload
|
| 5291 |
+
|
| 5292 |
+
|
| 5293 |
|
| 5294 |
def _normalize_dependency_package_name(value: str = "") -> str:
|
| 5295 |
name = re.split(r"[<>=!~;\[,\s]", str(value or "").strip(), 1)[0].strip().lower().replace("_", "-")
|
|
|
|
| 12503 |
# write_json(run_dir / "tests" / "payload_source.json", payload_source_record)
|
| 12504 |
# write_json(run_dir / "tests" / "replay_source.json", replay_source)
|
| 12505 |
# write_json(run_dir / "tests" / "validation_preflight.json", validation_preflight)
|
| 12506 |
+
app_version = "v198.26.8"
|
| 12507 |
engine_version = "unified_gradio_validation_harness_v198_25_3"
|
| 12508 |
parent_build_run_id = os.environ.get("PARENT_BUILD_RUN_ID", "").strip()
|
| 12509 |
validation_mode = os.environ.get("VALIDATION_MODE") or os.environ.get("SPACE_TEST_POLICY_MODE") or "linked"
|