RFTSystems commited on
Commit
303465a
·
verified ·
1 Parent(s): c71f147

Create drp/simulate.py

Browse files
Files changed (1) hide show
  1. drp/simulate.py +139 -0
drp/simulate.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import random
3
+ import uuid
4
+ from typing import Any, Dict, List, Optional, Tuple
5
+
6
+ from .bundle import write_bundle_zip
7
+
8
+
9
+ def _env_fingerprint() -> Dict[str, Any]:
10
+ # Keep lightweight; users can expand this in real exporters
11
+ return {
12
+ "python": os.environ.get("PYTHON_VERSION") or "unknown",
13
+ "space": os.environ.get("SPACE_ID") or os.environ.get("HF_SPACE_ID") or "unknown",
14
+ }
15
+
16
+
17
+ def _mk_event(kind: str, step: str, payload: Dict[str, Any]) -> Dict[str, Any]:
18
+ return {"kind": kind, "step": step, "payload": payload}
19
+
20
+
21
+ def make_demo_bundle_zip(out_path: str, *, seed: int, chaos: float, label: str) -> str:
22
+ """
23
+ Creates a synthetic agent timeline with controlled randomness.
24
+ 'chaos' increases divergence probability.
25
+ """
26
+ rng = random.Random(seed)
27
+ run_id = f"demo-{label}-{uuid.uuid4().hex[:8]}"
28
+ framework = "demo-agent"
29
+ model_id = "demo-llm"
30
+
31
+ events: List[Dict[str, Any]] = []
32
+ memory: Dict[str, Any] = {"goal": "reach_target", "notes": []}
33
+
34
+ for i in range(40):
35
+ # planning
36
+ action = rng.choice(["scan", "move", "ask_tool", "write_memory"])
37
+ if rng.random() < chaos:
38
+ action = rng.choice(["scan", "move", "ask_tool", "write_memory", "panic"])
39
+
40
+ events.append(_mk_event("plan_step", f"t{i}.plan", {"action": action, "score": rng.random()}))
41
+
42
+ if action == "ask_tool":
43
+ q = rng.choice(["price", "status", "latency", "risk"])
44
+ events.append(_mk_event("tool_call", f"t{i}.tool_call", {"tool": "mock_api", "query": q}))
45
+ # tool sometimes flakes
46
+ if rng.random() < (0.15 + chaos * 0.2):
47
+ events.append(_mk_event("tool_result", f"t{i}.tool_result", {"ok": False, "error": "timeout"}))
48
+ else:
49
+ val = rng.randint(1, 100)
50
+ events.append(_mk_event("tool_result", f"t{i}.tool_result", {"ok": True, "value": val}))
51
+ elif action == "write_memory":
52
+ note = rng.choice(["cached", "retry", "validated", "unsafe", "needs_review"])
53
+ memory["notes"].append(note)
54
+ events.append(_mk_event("memory_write", f"t{i}.mem", {"write": {"notes": list(memory["notes"])}}))
55
+ elif action == "panic":
56
+ events.append(_mk_event("guardrail", f"t{i}.guardrail", {"blocked": True, "reason": "anomaly"}))
57
+ events.append(_mk_event("state_snapshot", f"t{i}.state", {"memory": memory, "mode": "halt"}))
58
+ break
59
+ else:
60
+ # move / scan influences a synthetic "state"
61
+ events.append(_mk_event("state_snapshot", f"t{i}.state", {"x": rng.randint(0, 9), "y": rng.randint(0, 9), "memory": memory}))
62
+
63
+ # llm sample (synthetic text)
64
+ txt = rng.choice(
65
+ [
66
+ "Proceed with caution.",
67
+ "Tool looks stable.",
68
+ "Memory updated.",
69
+ "Need more evidence.",
70
+ "I will retry once.",
71
+ ]
72
+ )
73
+ if rng.random() < chaos:
74
+ txt = rng.choice(
75
+ [
76
+ "Unexpected output detected.",
77
+ "I am uncertain; escalating.",
78
+ "This seems inconsistent.",
79
+ "Plan changed due to drift.",
80
+ ]
81
+ )
82
+ events.append(_mk_event("llm_sample", f"t{i}.llm", {"text": txt, "tokens": rng.randint(20, 180)}))
83
+
84
+ return write_bundle_zip(
85
+ out_path,
86
+ run_id=run_id,
87
+ framework=framework,
88
+ model_id=model_id,
89
+ env_fingerprint=_env_fingerprint(),
90
+ events_payloads=events,
91
+ )
92
+
93
+
94
+ def fork_patch_bundle(
95
+ out_path: str,
96
+ *,
97
+ source_zip: str,
98
+ fork_at_index: int,
99
+ patch_kind: Optional[str] = None,
100
+ patch_step: Optional[str] = None,
101
+ patch_payload_json: Optional[Dict[str, Any]] = None,
102
+ ) -> str:
103
+ """
104
+ Simple “what-if” fork: take an existing bundle and patch a single event
105
+ (kind/step/payload) then re-hash-chain and re-emit as a new run.
106
+ """
107
+ from .bundle import load_bundle, write_bundle_zip
108
+
109
+ b = load_bundle(source_zip)
110
+ src_events = b.events
111
+
112
+ payloads: List[Dict[str, Any]] = []
113
+ for ev in src_events:
114
+ payloads.append(
115
+ {
116
+ "ts": ev.get("ts"),
117
+ "kind": ev.get("kind"),
118
+ "step": ev.get("step"),
119
+ "payload": ev.get("payload", {}),
120
+ }
121
+ )
122
+
123
+ if 0 <= fork_at_index < len(payloads):
124
+ if patch_kind:
125
+ payloads[fork_at_index]["kind"] = patch_kind
126
+ if patch_step:
127
+ payloads[fork_at_index]["step"] = patch_step
128
+ if patch_payload_json is not None:
129
+ payloads[fork_at_index]["payload"] = patch_payload_json
130
+
131
+ new_run = f"{b.manifest.get('run_id','run')}-fork"
132
+ return write_bundle_zip(
133
+ out_path,
134
+ run_id=new_run,
135
+ framework=b.manifest.get("framework", "unknown"),
136
+ model_id=b.manifest.get("model_id", "unknown"),
137
+ env_fingerprint=b.manifest.get("env", {}),
138
+ events_payloads=payloads,
139
+ )