|
|
import os |
|
|
import random |
|
|
import uuid |
|
|
from typing import Any, Dict, List, Optional |
|
|
|
|
|
from .bundle import write_bundle_zip |
|
|
|
|
|
|
|
|
def _env_fingerprint() -> Dict[str, Any]: |
|
|
return { |
|
|
"python": os.environ.get("PYTHON_VERSION") or "unknown", |
|
|
"space": os.environ.get("SPACE_ID") or os.environ.get("HF_SPACE_ID") or "unknown", |
|
|
} |
|
|
|
|
|
|
|
|
def _mk_event(kind: str, step: str, payload: Dict[str, Any]) -> Dict[str, Any]: |
|
|
return {"kind": kind, "step": step, "payload": payload} |
|
|
|
|
|
|
|
|
def make_demo_bundle_zip(out_path: str, *, seed: int, chaos: float, label: str) -> str: |
|
|
""" |
|
|
Creates a synthetic agent timeline with controlled randomness. |
|
|
'chaos' increases divergence probability. |
|
|
Also includes reward_total in state snapshots to demonstrate reward delta. |
|
|
""" |
|
|
rng = random.Random(seed) |
|
|
run_id = f"demo-{label}-{uuid.uuid4().hex[:8]}" |
|
|
framework = "demo-agent" |
|
|
model_id = "demo-llm" |
|
|
|
|
|
events: List[Dict[str, Any]] = [] |
|
|
memory: Dict[str, Any] = {"goal": "reach_target", "notes": []} |
|
|
reward_total = 0.0 |
|
|
|
|
|
|
|
|
replay = { |
|
|
"base_url": "https://example.com/replay", |
|
|
"pattern": "/?run_id={run_id}&i={i}", |
|
|
} |
|
|
|
|
|
for i in range(40): |
|
|
action = rng.choice(["scan", "move", "ask_tool", "write_memory"]) |
|
|
if rng.random() < chaos: |
|
|
action = rng.choice(["scan", "move", "ask_tool", "write_memory", "panic"]) |
|
|
|
|
|
score = rng.random() |
|
|
events.append(_mk_event("plan_step", f"t{i}.plan", {"action": action, "score": score})) |
|
|
|
|
|
if action == "ask_tool": |
|
|
q = rng.choice(["price", "status", "latency", "risk"]) |
|
|
events.append(_mk_event("tool_call", f"t{i}.tool_call", {"tool": "mock_api", "query": q})) |
|
|
if rng.random() < (0.15 + chaos * 0.2): |
|
|
events.append(_mk_event("tool_result", f"t{i}.tool_result", {"ok": False, "error": "timeout"})) |
|
|
reward_total -= 0.5 |
|
|
else: |
|
|
val = rng.randint(1, 100) |
|
|
events.append(_mk_event("tool_result", f"t{i}.tool_result", {"ok": True, "value": val})) |
|
|
reward_total += 0.2 |
|
|
elif action == "write_memory": |
|
|
note = rng.choice(["cached", "retry", "validated", "unsafe", "needs_review"]) |
|
|
memory["notes"].append(note) |
|
|
events.append(_mk_event("memory_write", f"t{i}.mem", {"write": {"notes": list(memory["notes"])}})) |
|
|
reward_total += 0.05 |
|
|
elif action == "panic": |
|
|
events.append(_mk_event("guardrail", f"t{i}.guardrail", {"blocked": True, "reason": "anomaly"})) |
|
|
events.append( |
|
|
_mk_event("state_snapshot", f"t{i}.state", {"memory": memory, "mode": "halt", "reward_total": reward_total}) |
|
|
) |
|
|
break |
|
|
else: |
|
|
|
|
|
x = rng.randint(0, 9) |
|
|
y = rng.randint(0, 9) |
|
|
reward_total += 0.01 |
|
|
events.append(_mk_event("state_snapshot", f"t{i}.state", {"x": x, "y": y, "memory": memory, "reward_total": reward_total})) |
|
|
|
|
|
txt = rng.choice( |
|
|
[ |
|
|
"Proceed with caution.", |
|
|
"Tool looks stable.", |
|
|
"Memory updated.", |
|
|
"Need more evidence.", |
|
|
"I will retry once.", |
|
|
] |
|
|
) |
|
|
if rng.random() < chaos: |
|
|
txt = rng.choice( |
|
|
[ |
|
|
"Unexpected output detected.", |
|
|
"I am uncertain; escalating.", |
|
|
"This seems inconsistent.", |
|
|
"Plan changed due to drift.", |
|
|
] |
|
|
) |
|
|
events.append(_mk_event("llm_sample", f"t{i}.llm", {"text": txt, "tokens": rng.randint(20, 180)})) |
|
|
|
|
|
return write_bundle_zip( |
|
|
out_path, |
|
|
run_id=run_id, |
|
|
framework=framework, |
|
|
model_id=model_id, |
|
|
env_fingerprint=_env_fingerprint(), |
|
|
events_payloads=events, |
|
|
replay=replay, |
|
|
) |
|
|
|
|
|
|
|
|
def fork_patch_bundle( |
|
|
out_path: str, |
|
|
*, |
|
|
source_zip: str, |
|
|
fork_at_index: int, |
|
|
patch_kind: Optional[str] = None, |
|
|
patch_step: Optional[str] = None, |
|
|
patch_payload_json: Optional[Dict[str, Any]] = None, |
|
|
) -> str: |
|
|
""" |
|
|
Counterfactual workflow: patch an event at index N, re-hash-chain into a new bundle. |
|
|
""" |
|
|
from .bundle import load_bundle, write_bundle_zip |
|
|
|
|
|
b = load_bundle(source_zip) |
|
|
src_events = b.events |
|
|
|
|
|
payloads: List[Dict[str, Any]] = [] |
|
|
for ev in src_events: |
|
|
payloads.append( |
|
|
{ |
|
|
"ts": ev.get("ts"), |
|
|
"kind": ev.get("kind"), |
|
|
"step": ev.get("step"), |
|
|
"payload": ev.get("payload", {}), |
|
|
} |
|
|
) |
|
|
|
|
|
if 0 <= fork_at_index < len(payloads): |
|
|
if patch_kind: |
|
|
payloads[fork_at_index]["kind"] = patch_kind |
|
|
if patch_step: |
|
|
payloads[fork_at_index]["step"] = patch_step |
|
|
if patch_payload_json is not None: |
|
|
payloads[fork_at_index]["payload"] = patch_payload_json |
|
|
|
|
|
new_run = f"{b.manifest.get('run_id','run')}-fork" |
|
|
return write_bundle_zip( |
|
|
out_path, |
|
|
run_id=new_run, |
|
|
framework=b.manifest.get("framework", "unknown"), |
|
|
model_id=b.manifest.get("model_id", "unknown"), |
|
|
env_fingerprint=b.manifest.get("env", {}), |
|
|
events_payloads=payloads, |
|
|
replay=b.manifest.get("replay"), |
|
|
run_url=b.manifest.get("run_url"), |
|
|
) |