426e9eeabd
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Waiting to run
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Waiting to run
Build Agent Image / build-and-push (push) Waiting to run
Chat shell gestures / Chat shell gesture + parity e2e (push) Waiting to run
ci / test (push) Waiting to run
ci / lint-and-format (push) Waiting to run
ci / build (push) Waiting to run
ci / dev-startup (push) Waiting to run
Cloud Gateway Discord / Test (push) Waiting to run
Cloud Gateway Webhook / Test (push) Waiting to run
Cloud Tests / lint-and-types (push) Waiting to run
Cloud Tests / unit-tests (push) Waiting to run
Cloud Tests / integration-tests (push) Waiting to run
Cloud Tests / e2e-tests (push) Blocked by required conditions
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Apps Worker (Product 2) / Determine environment (push) Waiting to run
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Blocked by required conditions
Deploy Eliza Provisioning Worker / Determine environment (push) Waiting to run
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Blocked by required conditions
Dev Smoke / Classify changed paths (push) Waiting to run
Dev Smoke / bun run dev onboarding chat (push) Blocked by required conditions
Dev Smoke / Vite HMR dependency-level smoke (push) Blocked by required conditions
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Waiting to run
gitleaks / gitleaks (push) Waiting to run
Markdown Links / Relative Markdown Links (push) Waiting to run
Publish @elizaos/example-code / check_npm (push) Waiting to run
Publish @elizaos/example-code / publish_npm (push) Blocked by required conditions
Publish @elizaos/plugin-elizacloud / verify_version (push) Waiting to run
Publish @elizaos/plugin-elizacloud / publish_npm (push) Blocked by required conditions
Quality (Extended) / Homepage Build (PR smoke) (push) Waiting to run
Quality (Extended) / Comment-only diff guard (push) Waiting to run
Quality (Extended) / Format + Type Safety Ratchet (push) Waiting to run
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Waiting to run
Quality (Extended) / Develop Gate (lint) (push) Waiting to run
Sandbox Live Smoke / Sandbox live smoke (push) Waiting to run
Snap Build & Test / Build Snap (amd64) (push) Waiting to run
Snap Build & Test / Build Snap (arm64) (push) Waiting to run
supply-chain / sbom (push) Waiting to run
supply-chain / vulnerability-scan (push) Waiting to run
Build, Push & Deploy to Phala Cloud / build-and-push (push) Waiting to run
Test Packaging / Validate Packaging Configs (push) Waiting to run
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Waiting to run
Test Packaging / Pack & Test JS Tarballs (push) Waiting to run
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Waiting to run
UI Fixture E2E / ui-fixture-e2e (push) Waiting to run
UI Fixture E2E / fixture-e2e (push) Waiting to run
UI Story Gate / story-gate (push) Waiting to run
vault-ci / test (macos-latest) (push) Waiting to run
vault-ci / test (ubuntu-latest) (push) Waiting to run
vault-ci / test (windows-latest) (push) Waiting to run
vault-ci / app-core wiring tests (push) Waiting to run
verify-patches / verify patches/CHECKSUMS.sha256 (push) Waiting to run
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Waiting to run
Voice Benchmark Smoke / voice bench smoke summary (push) Blocked by required conditions
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Waiting to run
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Waiting to run
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Waiting to run
Test Packaging / Build & Test PyPI Package (push) Waiting to run
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
131 lines
5.0 KiB
Python
131 lines
5.0 KiB
Python
"""
|
||
Disruption injection for P4 / P7 / P8 / P9 / P10.
|
||
|
||
Each REALM task instance may carry a ``disruption_scenarios`` list copied
|
||
from the upstream JSON. This module applies a single disruption to the
|
||
live instance state mid-run and returns the modified instance, so the
|
||
agent harness can re-prompt the agent with the new constraints and
|
||
record a replanning attempt.
|
||
|
||
The supported disruption types mirror upstream
|
||
``evaluation/task_definitions.DisruptionType``:
|
||
|
||
machine_breakdown P11/P10 – disable machine for ``duration``
|
||
traffic_delay P4 – add delay to a route
|
||
road_closure P4/P8 – remove a route entirely for ``duration``
|
||
flight_delay P9 – delay a flight arrival
|
||
resource_shortage P7/P10 – reduce a resource by ``shortage`` ratio
|
||
weather_event P7 – block transport to a region
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import copy
|
||
import logging
|
||
from dataclasses import dataclass
|
||
from typing import Any
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
@dataclass
|
||
class Disruption:
|
||
type: str
|
||
payload: dict[str, Any]
|
||
|
||
@classmethod
|
||
def from_dict(cls, raw: dict[str, Any]) -> "Disruption":
|
||
t = raw.get("type")
|
||
if hasattr(t, "value"): # upstream Enum
|
||
t = t.value # type: ignore[union-attr]
|
||
return cls(type=str(t or "unknown"), payload=dict(raw))
|
||
|
||
|
||
def apply_disruption(instance: dict[str, Any], disruption: Disruption) -> dict[str, Any]:
|
||
"""Return a deep-copied instance with the disruption applied.
|
||
|
||
Falls back to "no change" + a logged warning for unknown disruption
|
||
types, which keeps the runner resilient to upstream schema drift.
|
||
"""
|
||
new_instance = copy.deepcopy(instance)
|
||
t = disruption.type
|
||
p = disruption.payload
|
||
|
||
if t == "machine_breakdown":
|
||
# Mark a machine offline for ``duration`` time units.
|
||
mid = p.get("machine_id")
|
||
duration = p.get("duration", 0)
|
||
downtime = new_instance.setdefault("machine_downtime", {})
|
||
downtime[str(mid)] = downtime.get(str(mid), 0) + duration
|
||
|
||
elif t == "traffic_delay":
|
||
# Add delay to a single route in ``distances`` / ``travel_times``.
|
||
route = p.get("route")
|
||
delay = p.get("delay", 0)
|
||
for field in ("distances", "travel_times"):
|
||
d = new_instance.get(field)
|
||
if isinstance(d, dict) and route in d:
|
||
d[route] = d[route] + delay
|
||
rev = "-".join(reversed(route.split("-")))
|
||
if rev in d:
|
||
d[rev] = d[rev] + delay
|
||
|
||
elif t == "road_closure":
|
||
# Remove a route from the distance / travel_time matrix.
|
||
route = p.get("route")
|
||
for field in ("distances", "travel_times"):
|
||
d = new_instance.get(field)
|
||
if isinstance(d, dict) and route in d:
|
||
d[route] = float("inf")
|
||
rev = "-".join(reversed(route.split("-")))
|
||
if rev in d:
|
||
d[rev] = float("inf")
|
||
closed = new_instance.setdefault("blocked_routes", [])
|
||
if route not in closed:
|
||
closed.append(route)
|
||
|
||
elif t == "flight_delay":
|
||
flight = p.get("flight")
|
||
delay = p.get("delay", 0)
|
||
flights = new_instance.get("flight_schedules") or new_instance.get("constraints", {}).get(
|
||
"flights"
|
||
)
|
||
if isinstance(flights, dict) and flight in flights:
|
||
# Best-effort: append "+Nm" annotation so the agent prompt
|
||
# carries the disruption forward. Numeric times left intact
|
||
# because schedules are typically HH:MM strings here.
|
||
flights[flight] = f"{flights[flight]} (+{delay}min)"
|
||
new_instance.setdefault("flight_delays", []).append({"flight": flight, "delay": delay})
|
||
|
||
elif t == "resource_shortage":
|
||
resource = p.get("resource") or p.get("component")
|
||
shortage = float(p.get("shortage", 0))
|
||
# P7-style: ``resources`` is a dict of name->amount.
|
||
resources = new_instance.get("resources")
|
||
if isinstance(resources, dict) and resource in resources:
|
||
resources[resource] = resources[resource] * (1.0 - shortage)
|
||
# P10-style: ``suppliers[i].capacity`` for a component.
|
||
for sup in new_instance.get("suppliers", []) or []:
|
||
if "capacity" in sup:
|
||
sup["capacity"] = int(sup["capacity"] * (1.0 - shortage))
|
||
|
||
elif t == "weather_event":
|
||
region = p.get("region")
|
||
for r in new_instance.get("regions", []) or []:
|
||
if r.get("id") == region:
|
||
r["transport_blocked"] = True
|
||
|
||
else:
|
||
logger.warning("[disruption] Unknown type %r; passing through", t)
|
||
|
||
new_instance.setdefault("applied_disruptions", []).append({"type": t, **p})
|
||
return new_instance
|
||
|
||
|
||
def first_disruption(instance: dict[str, Any]) -> Disruption | None:
|
||
"""Return the first disruption scenario from the instance, if any."""
|
||
raw = instance.get("disruption_scenarios") or []
|
||
if not raw:
|
||
return None
|
||
return Disruption.from_dict(raw[0])
|