fffiloni commited on
Commit
46d5b88
·
verified ·
1 Parent(s): a9ddf04

Upload 15 files

Browse files
Files changed (3) hide show
  1. src/timeline_model.py +199 -51
  2. src/version.py +2 -2
  3. src/worker_payload.py +5 -5
src/timeline_model.py CHANGED
@@ -161,64 +161,211 @@ def _latest_event_by_step(bundle: dict[str, Any], step_name: str) -> dict[str, A
161
 
162
 
163
 
164
- def _runtime_history(bundle: dict[str, Any], *, live: dict[str, Any], runtime: dict[str, Any], smoke: dict[str, Any], gate: dict[str, Any]) -> list[dict[str, Any]]:
165
- """Return a compact build→runtime→API→smoke history for Live test.
166
 
167
- v198.26.9 keeps this curated: it restores the building/runtime feedback
168
- users need without dumping raw events into Active Run.
169
  """
170
- history: list[dict[str, Any]] = []
171
- seen: set[str] = set()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
- def add(stage: str, label: str, status: str = "info", ts: Any = "", detail: Any = "") -> None:
174
- key = f"{stage}:{label}:{status}"
175
- if key in seen:
176
- return
177
- seen.add(key)
178
- history.append({"stage": stage, "label": label, "status": status, "ts": ts or "", "detail": str(detail or "")[:500]})
179
 
180
- for event in _events(bundle):
181
- step = _lower(event.get("step"))
182
- status = _lower(event.get("status")) or "info"
183
- ts = event.get("ts") or event.get("created_at") or ""
184
- msg = event.get("message") or event.get("step") or ""
185
- if step in {"create_space", "create_space_hardware"}:
186
- add("space_created", "Space created" if status in SUCCESS_STATUSES else "Creating Space", status, ts, msg)
187
- elif step == "upload_files":
188
- add("runtime_uploaded", "Runtime uploaded" if status in SUCCESS_STATUSES else "Uploading runtime", status, ts, msg)
189
- elif step in {"space_runtime", "live_wait"}:
190
- lower_msg = _lower(msg)
191
- if "building" in lower_msg or "build" in lower_msg:
192
- add("space_building", "Space is building", status, ts, msg)
193
- elif "running" in lower_msg or "runtime" in lower_msg or step == "space_runtime":
194
- add("space_running", "Space runtime observed", status, ts, msg)
195
- else:
196
- add("space_runtime", "Space runtime check", status, ts, msg)
197
- elif step in {"endpoint_discovery", "api_validation"}:
198
- add("api_schema", "Gradio API checked" if status in SUCCESS_STATUSES else "Checking Gradio API", status, ts, msg)
199
- elif step == "generation_smoke":
200
- add("generation_smoke", "Generation smoke passed" if status in SUCCESS_STATUSES else "Generation smoke running" if status in RUNNING_STATUSES else "Generation smoke checked", status, ts, msg)
201
- elif step == "inference_gate":
202
- add("inference_gate", "Inference gate resolved", status, ts, msg)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
  runtime_stage = str(runtime.get("stage") or runtime.get("status") or runtime.get("runtime_status") or "").strip()
205
  upper_stage = runtime_stage.upper()
206
- if upper_stage:
207
- if "BUILD" in upper_stage and not any(h["stage"] == "space_building" for h in history):
208
- add("space_building", "Space is building", "running", runtime.get("updated_at") or "", runtime_stage)
209
- if "RUNNING" in upper_stage and not any(h["stage"] == "space_running" for h in history):
210
- add("space_running", "Space runtime is running", "success", runtime.get("updated_at") or "", runtime_stage)
211
- if "ERROR" in upper_stage:
212
- add("space_runtime_error", "Space runtime error", "failed", runtime.get("updated_at") or "", runtime.get("error") or runtime_stage)
213
- if live.get("stage") and not history:
214
- add(str(live.get("stage")), str(live.get("message") or live.get("stage")), str(live.get("status") or "info"), live.get("updated_at") or "")
 
 
215
  endpoints = gate.get("endpoints") or gate.get("api_endpoints") or gate.get("named_endpoints") or []
216
- selected = str(smoke.get("api_name") or gate.get("api_name") or gate.get("selected_endpoint") or gate.get("selected_api_name") or "").strip()
217
- if (selected or (isinstance(endpoints, list) and endpoints)) and not any(h["stage"] == "api_schema" for h in history):
218
- add("api_schema", f"Gradio API detected{': ' + selected if selected else ''}", "success")
219
- if (_lower(smoke.get("status")) == "success" or smoke.get("ok") is True or (gate.get("implementation_signals") or {}).get("generation_smoke_passed") is True) and not any(h["stage"] == "generation_smoke" for h in history):
220
- add("generation_smoke", "Generation smoke passed", "success", smoke.get("updated_at") or "", smoke.get("api_name") or "")
221
- return history[-10:]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
  def build_live_validation_model(bundle: dict[str, Any]) -> dict[str, Any]:
224
  """Compact live validation telemetry for the UI.
@@ -342,6 +489,7 @@ def build_live_validation_model(bundle: dict[str, Any]) -> dict[str, Any]:
342
  "message": smoke.get("error") or smoke.get("message") or live_message or api_event.get("message") or runtime_error or "",
343
  "next_action": next_action,
344
  "runtime_history": _runtime_history(bundle, live=live_status, runtime=runtime, smoke=smoke, gate=gate),
 
345
  }
346
 
347
 
@@ -572,7 +720,7 @@ def _build_phase_details(bundle: dict[str, Any], phase: str) -> list[dict[str, A
572
  runtime_history = live.get("runtime_history") if isinstance(live.get("runtime_history"), list) else []
573
  for item in runtime_history:
574
  if isinstance(item, dict) and item.get("label"):
575
- details.append({"label": str(item.get("label")), "status": str(item.get("status") or "info")})
576
  if runtime_stage and not runtime_history:
577
  details.append({"label": f"Space runtime: {runtime_stage}", "status": "info"})
578
  if live.get("health") == "passed":
 
161
 
162
 
163
 
164
+ def _hardware_attempt_rows(bundle: dict[str, Any]) -> list[dict[str, Any]]:
165
+ """Return normalized hardware-at-creation attempts.
166
 
167
+ v198.26.11 uses these only to explain the Live Test story. They must not
168
+ influence the run verdict.
169
  """
170
+ sources = (bundle.get("hardware_attempts") or {}, bundle.get("hardware_strategy") or {}, bundle.get("state") or {})
171
+ for source in sources:
172
+ attempts = source.get("attempts") if isinstance(source, dict) else None
173
+ if isinstance(attempts, list):
174
+ return [a for a in attempts if isinstance(a, dict)]
175
+ return []
176
+
177
+
178
+ def _attempt_hardware(attempt: dict[str, Any]) -> str:
179
+ return str(
180
+ attempt.get("hardware")
181
+ or attempt.get("requested_hardware")
182
+ or attempt.get("target_hardware")
183
+ or attempt.get("sku")
184
+ or attempt.get("flavor")
185
+ or ""
186
+ ).strip()
187
 
 
 
 
 
 
 
188
 
189
+ def _attempt_status(attempt: dict[str, Any]) -> str:
190
+ return _lower(attempt.get("status") or attempt.get("result") or attempt.get("outcome"))
191
+
192
+
193
+ def _selected_hardware(bundle: dict[str, Any]) -> str:
194
+ for source in (bundle.get("hardware_attempts") or {}, bundle.get("hardware_strategy") or {}, bundle.get("summary") or {}, bundle.get("state") or {}):
195
+ if isinstance(source, dict):
196
+ value = str(source.get("selected_hardware") or source.get("hardware") or "").strip()
197
+ if value:
198
+ return value
199
+ return ""
200
+
201
+
202
+ def _zero_gpu_fallback_story(bundle: dict[str, Any]) -> tuple[bool, str, str]:
203
+ attempts = _hardware_attempt_rows(bundle)
204
+ selected = _selected_hardware(bundle)
205
+ zero_failed = False
206
+ fallback_hardware = selected
207
+ fallback_success = False
208
+ for attempt in attempts:
209
+ hw = _attempt_hardware(attempt)
210
+ status = _attempt_status(attempt)
211
+ hw_lower = _lower(hw)
212
+ is_zero = "zero" in hw_lower or hw_lower.startswith("zero-")
213
+ if is_zero and (status in FAILED_STATUSES or status in {"refused", "unavailable", "denied", "quota_exceeded", "quota"} or "fail" in status):
214
+ zero_failed = True
215
+ if not is_zero and (status in SUCCESS_STATUSES or status in {"selected", "created", "ok"}):
216
+ fallback_success = True
217
+ fallback_hardware = hw or selected
218
+ if zero_failed and (fallback_success or (selected and "zero" not in _lower(selected))):
219
+ return True, fallback_hardware or selected or "fallback hardware", "ZeroGPU unavailable → using " + (fallback_hardware or selected or "fallback hardware")
220
+ return False, fallback_hardware or selected, ""
221
+
222
+
223
+ def _latest_step_status(bundle: dict[str, Any], steps: set[str]) -> str:
224
+ for event in reversed(_events(bundle)):
225
+ if _lower(event.get("step")) in steps:
226
+ return _lower(event.get("status"))
227
+ return ""
228
+
229
+
230
+ def _latest_step_message(bundle: dict[str, Any], steps: set[str]) -> str:
231
+ for event in reversed(_events(bundle)):
232
+ if _lower(event.get("step")) in steps:
233
+ return str(event.get("message") or "").strip()
234
+ return ""
235
+
236
+
237
+ def build_live_test_story(bundle: dict[str, Any], *, live: dict[str, Any] | None = None, runtime: dict[str, Any] | None = None, smoke: dict[str, Any] | None = None, gate: dict[str, Any] | None = None) -> dict[str, Any]:
238
+ """Curated Live Test story for Active Run.
239
+
240
+ This is deliberately not a raw event dump. It compresses retry/fallback
241
+ mechanics into user-facing process steps, so a normal ZeroGPU refusal followed
242
+ by an A10G fallback is shown as a controlled fallback rather than as a scary
243
+ duplicate "Creating Space failed" row.
244
+ """
245
+ live = live if isinstance(live, dict) else _live_status(bundle)
246
+ runtime = runtime if isinstance(runtime, dict) else _runtime(bundle)
247
+ smoke = smoke if isinstance(smoke, dict) else _smoke(bundle)
248
+ gate = gate if isinstance(gate, dict) else _gate(bundle)
249
+ signals = _signals(bundle)
250
+ summary = bundle.get("summary") or {}
251
+ state = bundle.get("state") or {}
252
+ steps: list[dict[str, Any]] = []
253
+
254
+ def add(step_id: str, label: str, status: str, detail: Any = "", ts: Any = "") -> None:
255
+ steps.append({
256
+ "id": step_id,
257
+ "stage": step_id,
258
+ "label": label,
259
+ "status": status or "pending",
260
+ "detail": str(detail or "")[:500],
261
+ "ts": ts or "",
262
+ })
263
+
264
+ fallback_used, fallback_hw, fallback_detail = _zero_gpu_fallback_story(bundle)
265
+ selected_hw = fallback_hw or _selected_hardware(bundle) or str(summary.get("selected_hardware") or state.get("selected_hardware") or "").strip()
266
+ create_status = _latest_step_status(bundle, {"create_space", "create_space_hardware"})
267
+ upload_status = _latest_step_status(bundle, {"upload_files"})
268
+ create_message = _latest_step_message(bundle, {"create_space", "create_space_hardware"})
269
+ runtime_upload_epoch = bundle.get("runtime_upload_epoch") or {}
270
+ runtime_uploaded = bool(
271
+ runtime_upload_epoch.get("last_upload_completed_at")
272
+ or runtime_upload_epoch.get("upload_sequence")
273
+ or upload_status in SUCCESS_STATUSES
274
+ )
275
+
276
+ zero_attempt_failed_without_fallback = (not fallback_used) and any(
277
+ ("zero" in _lower(_attempt_hardware(attempt))) and _attempt_status(attempt) in FAILED_STATUSES
278
+ for attempt in _hardware_attempt_rows(bundle)
279
+ )
280
+ if fallback_used:
281
+ add("hardware", "Hardware provisioning", "fallback", fallback_detail)
282
+ elif zero_attempt_failed_without_fallback or create_status in FAILED_STATUSES:
283
+ add("hardware", "Hardware provisioning", "failed", create_message or "Hardware provisioning failed")
284
+ elif selected_hw:
285
+ add("hardware", "Hardware provisioning", "selected", f"{selected_hw} selected")
286
+ else:
287
+ add("hardware", "Hardware provisioning", "running" if create_status in RUNNING_STATUSES else "pending", "Selecting hardware")
288
+
289
+ if create_status in FAILED_STATUSES and not fallback_used and not runtime_uploaded:
290
+ add("space", "Space setup", "failed", create_message or "Space creation failed")
291
+ elif create_status in SUCCESS_STATUSES or runtime_uploaded or summary.get("target_space") or state.get("target_space"):
292
+ suffix = f" on {selected_hw}" if selected_hw else ""
293
+ add("space", "Space setup", "success", f"Space created{suffix}")
294
+ elif create_status in RUNNING_STATUSES:
295
+ add("space", "Space setup", "running", "Creating Space")
296
+ else:
297
+ add("space", "Space setup", "pending", "Waiting for Space creation")
298
+
299
+ if runtime_uploaded:
300
+ add("runtime_upload", "Runtime deployment", "uploaded", "Runtime uploaded")
301
+ elif upload_status in RUNNING_STATUSES:
302
+ add("runtime_upload", "Runtime deployment", "running", "Uploading runtime")
303
+ elif upload_status in FAILED_STATUSES:
304
+ add("runtime_upload", "Runtime deployment", "failed", _latest_step_message(bundle, {"upload_files"}) or "Runtime upload failed")
305
+ else:
306
+ add("runtime_upload", "Runtime deployment", "pending", "Waiting for runtime upload")
307
 
308
  runtime_stage = str(runtime.get("stage") or runtime.get("status") or runtime.get("runtime_status") or "").strip()
309
  upper_stage = runtime_stage.upper()
310
+ if "ERROR" in upper_stage:
311
+ add("runtime_status", "Space runtime", "failed", runtime.get("error") or runtime_stage or "Runtime error")
312
+ elif "RUNNING" in upper_stage:
313
+ add("runtime_status", "Space runtime", "running", "Runtime running")
314
+ elif "BUILD" in upper_stage:
315
+ add("runtime_status", "Space runtime", "building", "Building on Hugging Face")
316
+ elif runtime_uploaded:
317
+ add("runtime_status", "Space runtime", "building", "Waiting for Hugging Face build")
318
+ else:
319
+ add("runtime_status", "Space runtime", "pending", "Waiting for upload")
320
+
321
  endpoints = gate.get("endpoints") or gate.get("api_endpoints") or gate.get("named_endpoints") or []
322
+ selected_endpoint = str(smoke.get("api_name") or gate.get("api_name") or gate.get("selected_endpoint") or gate.get("selected_api_name") or "").strip()
323
+ endpoint_known = bool(selected_endpoint or (isinstance(endpoints, list) and endpoints) or signals.get("generation_smoke_passed") is True or _lower(smoke.get("status")) == "success")
324
+ api_status = _latest_step_status(bundle, {"endpoint_discovery", "api_validation"})
325
+ if endpoint_known or api_status in SUCCESS_STATUSES:
326
+ add("api", "Gradio API", "ready", f"Ready{': ' + selected_endpoint if selected_endpoint else ''}")
327
+ elif api_status in FAILED_STATUSES:
328
+ add("api", "Gradio API", "failed", _latest_step_message(bundle, {"endpoint_discovery", "api_validation"}) or "API check failed")
329
+ elif api_status in RUNNING_STATUSES or (live.get("stage") in {"endpoint_discovery", "api_validation"}):
330
+ add("api", "Gradio API", "running", "Checking API schema")
331
+ else:
332
+ add("api", "Gradio API", "pending", "Waiting for runtime")
333
+
334
+ smoke_status = _lower(smoke.get("status") or _latest_step_status(bundle, {"generation_smoke"}))
335
+ if smoke_status == "success" or smoke.get("ok") is True or signals.get("generation_smoke_passed") is True:
336
+ add("smoke", "Generation smoke", "verified", "Generation verified")
337
+ elif smoke_status in FAILED_STATUSES or smoke_status in {"timeout", "exception"}:
338
+ add("smoke", "Generation smoke", "failed", smoke.get("error") or smoke.get("message") or "Generation not verified")
339
+ elif smoke_status in RUNNING_STATUSES or live.get("stage") == "generation_smoke":
340
+ add("smoke", "Generation smoke", "running", "Running smoke test")
341
+ else:
342
+ add("smoke", "Generation smoke", "pending", "Waiting for API")
343
+
344
+ story_status = "in_progress"
345
+ if steps and steps[-1]["status"] == "verified":
346
+ story_status = "success"
347
+ elif any(step["status"] == "failed" for step in steps):
348
+ story_status = "failed"
349
+ elif any(step["status"] in {"building", "running"} for step in steps):
350
+ story_status = "in_progress"
351
+ headline = "Generation verified" if story_status == "success" else "Waiting for Space runtime" if story_status == "in_progress" else "Live test needs attention"
352
+ return {
353
+ "schema_version": "live_test_story.v198_26_11",
354
+ "status": story_status,
355
+ "headline": headline,
356
+ "steps": steps,
357
+ "fallback_used": fallback_used,
358
+ "selected_hardware": selected_hw,
359
+ }
360
+
361
+
362
+ def _runtime_history(bundle: dict[str, Any], *, live: dict[str, Any], runtime: dict[str, Any], smoke: dict[str, Any], gate: dict[str, Any]) -> list[dict[str, Any]]:
363
+ """Return the curated Live Test story steps for timeline details.
364
+
365
+ v198.26.11 replaces duplicate raw create/upload events with a process story:
366
+ hardware fallback, Space setup, runtime deployment, HF build/runtime, API, smoke.
367
+ """
368
+ return build_live_test_story(bundle, live=live, runtime=runtime, smoke=smoke, gate=gate).get("steps") or []
369
 
370
  def build_live_validation_model(bundle: dict[str, Any]) -> dict[str, Any]:
371
  """Compact live validation telemetry for the UI.
 
489
  "message": smoke.get("error") or smoke.get("message") or live_message or api_event.get("message") or runtime_error or "",
490
  "next_action": next_action,
491
  "runtime_history": _runtime_history(bundle, live=live_status, runtime=runtime, smoke=smoke, gate=gate),
492
+ "live_test_story": build_live_test_story(bundle, live=live_status, runtime=runtime, smoke=smoke, gate=gate),
493
  }
494
 
495
 
 
720
  runtime_history = live.get("runtime_history") if isinstance(live.get("runtime_history"), list) else []
721
  for item in runtime_history:
722
  if isinstance(item, dict) and item.get("label"):
723
+ details.append({"label": str(item.get("label")), "status": str(item.get("status") or "info"), "detail": str(item.get("detail") or "")})
724
  if runtime_stage and not runtime_history:
725
  details.append({"label": f"Space runtime: {runtime_stage}", "status": "info"})
726
  if live.get("health") == "passed":
src/version.py CHANGED
@@ -1,7 +1,7 @@
1
  from __future__ import annotations
2
 
3
- ASF_APP_VERSION = "v198.26.10"
4
- ASF_RELEASE_NAME = "Agentic Space Factory v198.26.10"
5
 
6
 
7
  def resolve_app_version(value: str | None = None) -> str:
 
1
  from __future__ import annotations
2
 
3
+ ASF_APP_VERSION = "v198.26.11"
4
+ ASF_RELEASE_NAME = "Agentic Space Factory v198.26.11"
5
 
6
 
7
  def resolve_app_version(value: str | None = None) -> str:
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.10"
39
- app_version = "v198.26.10"
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
@@ -3255,7 +3255,7 @@ def build_promise_validation_status(workspace: Path | None, validation: dict | N
3255
  str(demo_contract.get("promise_fulfillment_risk") or ""),
3256
  str(blockers.get("reason") or ""),
3257
  ]).lower()
3258
- # v198.26.10: "official Space" by itself is not a blocker. Pi often
3259
  # mentions the upstream official Space as provenance or comparison even when
3260
  # the generated Space implements real inference. Treat only explicit
3261
  # redirect/only/not-implemented formulations as official-only blockers.
@@ -9788,7 +9788,7 @@ def infer_generation_gate(workspace: Path, implementation_mode: str, validation:
9788
  minimal_smoke_ok = isinstance(generation_smoke, dict) and (generation_smoke.get("status") == "demo_usable_smoke_passed" or generation_smoke.get("demo_usable_smoke_passed") is True)
9789
  full_inference_requested = implementation_mode in {"full-inference-gated", "full-inference-attempt"}
9790
  promise_fulfilled = bool(promise_validation.get("promise_fulfilled"))
9791
- # v198.26.10 contradiction guard: when a canonical live generation smoke
9792
  # succeeds and the machine-readable contract does not declare no-full
9793
  # inference, a broad/heuristic diagnostic promise must not manufacture a
9794
  # blocker. This keeps metrics as evidence while preserving real explicit
@@ -12563,7 +12563,7 @@ def smoke_generate(target_space_id: str, token: str, run_dir: Path, events_path:
12563
  # write_json(run_dir / "tests" / "payload_source.json", payload_source_record)
12564
  # write_json(run_dir / "tests" / "replay_source.json", replay_source)
12565
  # write_json(run_dir / "tests" / "validation_preflight.json", validation_preflight)
12566
- app_version = "v198.26.10"
12567
  engine_version = "unified_gradio_validation_harness_v198_25_3"
12568
  parent_build_run_id = os.environ.get("PARENT_BUILD_RUN_ID", "").strip()
12569
  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.11"
39
+ app_version = "v198.26.11"
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
 
3255
  str(demo_contract.get("promise_fulfillment_risk") or ""),
3256
  str(blockers.get("reason") or ""),
3257
  ]).lower()
3258
+ # v198.26.11: "official Space" by itself is not a blocker. Pi often
3259
  # mentions the upstream official Space as provenance or comparison even when
3260
  # the generated Space implements real inference. Treat only explicit
3261
  # redirect/only/not-implemented formulations as official-only blockers.
 
9788
  minimal_smoke_ok = isinstance(generation_smoke, dict) and (generation_smoke.get("status") == "demo_usable_smoke_passed" or generation_smoke.get("demo_usable_smoke_passed") is True)
9789
  full_inference_requested = implementation_mode in {"full-inference-gated", "full-inference-attempt"}
9790
  promise_fulfilled = bool(promise_validation.get("promise_fulfilled"))
9791
+ # v198.26.11 contradiction guard: when a canonical live generation smoke
9792
  # succeeds and the machine-readable contract does not declare no-full
9793
  # inference, a broad/heuristic diagnostic promise must not manufacture a
9794
  # blocker. This keeps metrics as evidence while preserving real explicit
 
12563
  # write_json(run_dir / "tests" / "payload_source.json", payload_source_record)
12564
  # write_json(run_dir / "tests" / "replay_source.json", replay_source)
12565
  # write_json(run_dir / "tests" / "validation_preflight.json", validation_preflight)
12566
+ app_version = "v198.26.11"
12567
  engine_version = "unified_gradio_validation_harness_v198_25_3"
12568
  parent_build_run_id = os.environ.get("PARENT_BUILD_RUN_ID", "").strip()
12569
  validation_mode = os.environ.get("VALIDATION_MODE") or os.environ.get("SPACE_TEST_POLICY_MODE") or "linked"