tostido commited on
Commit
04e8b3f
·
verified ·
1 Parent(s): 2d875c0

Build MeshScale bare metal controller

Browse files
Files changed (40) hide show
  1. Dockerfile +18 -0
  2. README.md +55 -5
  3. key_farm/__init__.py +29 -0
  4. key_farm/__pycache__/__init__.cpython-310.pyc +0 -0
  5. key_farm/__pycache__/amalgam_bridge.cpython-310.pyc +0 -0
  6. key_farm/__pycache__/contracts.cpython-310.pyc +0 -0
  7. key_farm/__pycache__/controller.cpython-310.pyc +0 -0
  8. key_farm/__pycache__/evaluator.cpython-310.pyc +0 -0
  9. key_farm/__pycache__/faculty.cpython-310.pyc +0 -0
  10. key_farm/__pycache__/regulation.cpython-310.pyc +0 -0
  11. key_farm/__pycache__/scaler.cpython-310.pyc +0 -0
  12. key_farm/__pycache__/serialization.cpython-310.pyc +0 -0
  13. key_farm/__pycache__/store.cpython-310.pyc +0 -0
  14. key_farm/__pycache__/symbiotic_trace.cpython-310.pyc +0 -0
  15. key_farm/amalgam_bridge.py +33 -0
  16. key_farm/cli.py +235 -0
  17. key_farm/contracts.py +266 -0
  18. key_farm/controller.py +276 -0
  19. key_farm/evaluator.py +201 -0
  20. key_farm/faculty.py +156 -0
  21. key_farm/regulation.py +37 -0
  22. key_farm/scaler.py +271 -0
  23. key_farm/serialization.py +52 -0
  24. key_farm/store.py +339 -0
  25. key_farm/symbiotic_trace.py +231 -0
  26. key_farm/worker.py +215 -0
  27. meshscale/__init__.py +6 -0
  28. meshscale/cli.py +7 -0
  29. meshscale_space/Dockerfile +18 -0
  30. meshscale_space/README.md +60 -0
  31. meshscale_space/__init__.py +1 -0
  32. meshscale_space/__pycache__/__init__.cpython-310.pyc +0 -0
  33. meshscale_space/__pycache__/app.cpython-310.pyc +0 -0
  34. meshscale_space/__pycache__/os_routes.cpython-310.pyc +0 -0
  35. meshscale_space/app.py +201 -0
  36. meshscale_space/os_routes.py +156 -0
  37. meshscale_space/requirements.txt +5 -0
  38. meshscale_space/static/app.js +288 -0
  39. meshscale_space/static/index.html +84 -0
  40. meshscale_space/static/styles.css +377 -0
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ ENV PYTHONUNBUFFERED=1
4
+ ENV PORT=7860
5
+ ENV KEY_FARM_ROOT=/data/farm
6
+ ENV MESHSCALE_ENABLE_APPLY=0
7
+ ENV MESHSCALE_FULL_KEY_REQUIREMENTS=0
8
+
9
+ WORKDIR /app
10
+ COPY . /app
11
+
12
+ RUN python -m pip install --upgrade pip \
13
+ && python -m pip install --no-cache-dir -r meshscale_space/requirements.txt \
14
+ && if [ "$MESHSCALE_FULL_KEY_REQUIREMENTS" = "1" ] && [ -f requirements.txt ]; then \
15
+ python -m pip install --no-cache-dir -r requirements.txt; \
16
+ fi
17
+
18
+ CMD ["python", "-m", "uvicorn", "meshscale_space.app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,10 +1,60 @@
1
  ---
2
- title: Meshscale
3
- emoji: 🦀
4
- colorFrom: green
5
- colorTo: gray
6
  sdk: docker
 
7
  pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: MeshScale
3
+ emoji: 🔩
4
+ colorFrom: gray
5
+ colorTo: yellow
6
  sdk: docker
7
+ app_port: 7860
8
  pinned: false
9
  ---
10
 
11
+ # MeshScale
12
+
13
+ Bare-metal search surface for the MeshScale CPU worker mesh.
14
+
15
+ Required Space variable:
16
+
17
+ ```text
18
+ KEY_FARM_ROOT=/data/farm
19
+ ```
20
+
21
+ Safe default:
22
+
23
+ ```text
24
+ MESHSCALE_ENABLE_APPLY=0
25
+ ```
26
+
27
+ Set `MESHSCALE_ENABLE_APPLY=1` only on the controller Space that is allowed to
28
+ start or pause Hugging Face CPU worker Spaces.
29
+
30
+ Common variables:
31
+
32
+ ```text
33
+ MESHSCALE_TEMPLATE_SPACE=tostido/meshscale-worker-template
34
+ MESHSCALE_NAMESPACE=tostido
35
+ MESHSCALE_WORKER_PREFIX=meshscale-worker
36
+ MESHSCALE_HARDWARE=cpu-upgrade
37
+ MESHSCALE_MAX_WORKERS=24
38
+ MESHSCALE_BUDGET_HOURLY_USD=0.72
39
+ MESHSCALE_KEY_SPACE_URL=https://huggingface.co/spaces/tostido/Ouroboros
40
+ MESHSCALE_BUCKET_URL=https://huggingface.co/buckets/tostido/Ouroboros-storage
41
+ MESHSCALE_WORKER_TEMPLATE_URL=https://huggingface.co/spaces/tostido/meshscale-worker-template
42
+ ```
43
+
44
+ The UI accepts search commands:
45
+
46
+ ```text
47
+ guide
48
+ status
49
+ trace
50
+ plan 420
51
+ scale dry
52
+ scale apply
53
+ run
54
+ drain 4
55
+ shutdown
56
+ ```
57
+
58
+ The OS route layer returns contextual next steps before acting. For example,
59
+ `scale apply` stops early when the controller pulse is quiet and routes the user
60
+ to KEY instead of blindly scaling workers.
key_farm/__init__.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """CPU farm primitives for KEY evolution runs.
2
+
3
+ The farm keeps NEAT generation ownership in KEY and distributes only
4
+ fitness-evaluation shards to CPU workers.
5
+ """
6
+
7
+ from .contracts import FarmPolicy, RegulationIntent, ShardJob, ShardResult
8
+ from .controller import FarmController
9
+ from .evaluator import get_handler, register_handler
10
+ from .faculty import farm_faculty_manifest, load_cocoon_faculty
11
+ from .scaler import MeshScaleScaler, ScalePolicy
12
+ from .store import FarmStore
13
+ from .symbiotic_trace import symbiotic_trace
14
+
15
+ __all__ = [
16
+ "FarmController",
17
+ "FarmPolicy",
18
+ "FarmStore",
19
+ "MeshScaleScaler",
20
+ "RegulationIntent",
21
+ "ScalePolicy",
22
+ "ShardJob",
23
+ "ShardResult",
24
+ "get_handler",
25
+ "farm_faculty_manifest",
26
+ "load_cocoon_faculty",
27
+ "register_handler",
28
+ "symbiotic_trace",
29
+ ]
key_farm/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (868 Bytes). View file
 
key_farm/__pycache__/amalgam_bridge.cpython-310.pyc ADDED
Binary file (1.05 kB). View file
 
key_farm/__pycache__/contracts.cpython-310.pyc ADDED
Binary file (11 kB). View file
 
key_farm/__pycache__/controller.cpython-310.pyc ADDED
Binary file (7.24 kB). View file
 
key_farm/__pycache__/evaluator.cpython-310.pyc ADDED
Binary file (5.71 kB). View file
 
key_farm/__pycache__/faculty.cpython-310.pyc ADDED
Binary file (4.92 kB). View file
 
key_farm/__pycache__/regulation.cpython-310.pyc ADDED
Binary file (1.06 kB). View file
 
key_farm/__pycache__/scaler.cpython-310.pyc ADDED
Binary file (8.95 kB). View file
 
key_farm/__pycache__/serialization.cpython-310.pyc ADDED
Binary file (2.23 kB). View file
 
key_farm/__pycache__/store.cpython-310.pyc ADDED
Binary file (13 kB). View file
 
key_farm/__pycache__/symbiotic_trace.cpython-310.pyc ADDED
Binary file (6.65 kB). View file
 
key_farm/amalgam_bridge.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ def observe_event(model_id: str, event_type: str, payload: dict[str, Any]) -> dict[str, Any] | None:
7
+ """Record a farm event through Amalgam/Cascade when available.
8
+
9
+ This is intentionally optional. KEY farm execution must not fail just
10
+ because a receipt backend is not installed in a worker Space.
11
+ """
12
+
13
+ envelope = {
14
+ "event_type": event_type,
15
+ "payload": payload,
16
+ "surface": "key_farm",
17
+ }
18
+
19
+ try:
20
+ from amalgam_agency import AmalgamAgency
21
+
22
+ agency = AmalgamAgency(model_id=model_id)
23
+ return agency.record(event_type, payload, surface="key_farm")
24
+ except Exception:
25
+ pass
26
+
27
+ try:
28
+ from cascade.store import observe
29
+
30
+ receipt = observe(model_id=model_id, data=envelope, sync=False)
31
+ return receipt.to_dict()
32
+ except Exception:
33
+ return None
key_farm/cli.py ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import shutil
6
+ import tempfile
7
+ import threading
8
+ from pathlib import Path
9
+
10
+ from .contracts import FarmPolicy, RegulationIntent
11
+ from .controller import FarmController
12
+ from .faculty import farm_faculty_manifest, load_cocoon_faculty
13
+ from .scaler import MeshScaleScaler, ScalePolicy
14
+ from .store import FarmStore
15
+ from .symbiotic_trace import symbiotic_trace
16
+ from .worker import worker_loop
17
+
18
+
19
+ def simulate(args: argparse.Namespace) -> None:
20
+ from node import create_population
21
+ from run import load_config
22
+
23
+ root = Path(args.root) if args.root else Path(tempfile.mkdtemp(prefix="key-farm-"))
24
+ if args.clean and root.exists():
25
+ shutil.rmtree(root)
26
+ root.mkdir(parents=True, exist_ok=True)
27
+
28
+ config = load_config()
29
+ config["population"] = {
30
+ **config.get("population", {}),
31
+ "size": args.nodes,
32
+ "dimensions": args.dimensions,
33
+ "trait_keys": [f"x{i}" for i in range(args.dimensions)],
34
+ "use_brains": False,
35
+ }
36
+ config["brain"] = {**config.get("brain", {}), "enabled": False}
37
+ config["embedding_brain"] = {**config.get("embedding_brain", {}), "enabled": False}
38
+ config["dreamer_brain"] = {**config.get("dreamer_brain", {}), "enabled": False}
39
+ config["scarecrow_brain"] = {**config.get("scarecrow_brain", {}), "enabled": False}
40
+ config["council_brain"] = {**config.get("council_brain", {}), "enabled": False}
41
+ config["fitness"] = {**config.get("fitness", {}), "function": args.fitness}
42
+
43
+ policy = FarmPolicy(
44
+ target_batch_size=args.shard_size,
45
+ max_workers=args.workers,
46
+ budget_hourly_usd=args.workers * 0.03,
47
+ )
48
+ store = FarmStore(root)
49
+ controller = FarmController(store, policy)
50
+ population = create_population(args.nodes, config["population"]["trait_keys"], brain_factory=None)
51
+ run_id, jobs = controller.submit_generation(population, config, generation=0)
52
+
53
+ threads = [
54
+ threading.Thread(
55
+ target=worker_loop,
56
+ args=(store, f"local_{idx}", policy),
57
+ kwargs={"poll_seconds": 0.05, "once": False},
58
+ daemon=True,
59
+ )
60
+ for idx in range(args.workers)
61
+ ]
62
+ for thread in threads:
63
+ thread.start()
64
+
65
+ expected = {node.id for node in population}
66
+ controller.collect_generation(run_id, 0, expected, timeout_seconds=args.timeout)
67
+ fitness_by_id = controller.merge_into_population(population, run_id, 0)
68
+ best = max(fitness_by_id.values()) if fitness_by_id else None
69
+ controller.shutdown_workers(reason="simulation_complete", run_id=run_id, generation=0)
70
+ for thread in threads:
71
+ thread.join(timeout=1.0)
72
+
73
+ print(json.dumps({
74
+ "root": str(root),
75
+ "run_id": run_id,
76
+ "jobs": len(jobs),
77
+ "nodes": len(population),
78
+ "results": len(fitness_by_id),
79
+ "best_fitness": best,
80
+ }, indent=2))
81
+
82
+
83
+ def regulate(args: argparse.Namespace) -> None:
84
+ store = FarmStore(args.root)
85
+ intent = RegulationIntent(
86
+ mode=args.mode,
87
+ reason=args.reason,
88
+ target_workers=args.target_workers,
89
+ formation=args.formation or None,
90
+ run_id=args.run_id or None,
91
+ generation=args.generation,
92
+ issued_by=args.issued_by,
93
+ )
94
+ store.write_regulation(intent)
95
+ print(json.dumps(intent.to_dict(), indent=2))
96
+
97
+
98
+ def plan(args: argparse.Namespace) -> None:
99
+ policy = FarmPolicy(
100
+ max_workers=args.max_workers,
101
+ budget_hourly_usd=args.budget_hourly_usd,
102
+ cpu_space_hourly_usd=args.cpu_space_hourly_usd,
103
+ target_batch_size=args.shard_size,
104
+ )
105
+ needed_for_jobs = max(1, (args.pending_jobs + args.jobs_per_worker - 1) // args.jobs_per_worker)
106
+ requested = max(0, min(policy.effective_max_workers(), needed_for_jobs) - args.active_workers)
107
+ print(json.dumps({
108
+ "pending_jobs": args.pending_jobs,
109
+ "active_workers": args.active_workers,
110
+ "requested_workers": requested,
111
+ "effective_max_workers": policy.effective_max_workers(),
112
+ "hourly_budget_usd": policy.budget_hourly_usd,
113
+ "cpu_space_hourly_usd": policy.cpu_space_hourly_usd,
114
+ }, indent=2))
115
+
116
+
117
+ def faculty(args: argparse.Namespace) -> None:
118
+ if args.cocoon_capabilities:
119
+ manifest = load_cocoon_faculty(args.cocoon_capabilities)
120
+ else:
121
+ manifest = farm_faculty_manifest()
122
+ print(json.dumps(manifest, indent=2))
123
+
124
+
125
+ def trace(args: argparse.Namespace) -> None:
126
+ policy = FarmPolicy(
127
+ max_workers=args.max_workers,
128
+ budget_hourly_usd=args.budget_hourly_usd,
129
+ cpu_space_hourly_usd=args.cpu_space_hourly_usd,
130
+ target_batch_size=args.shard_size,
131
+ )
132
+ report = symbiotic_trace(FarmStore(args.root), policy, receipt=not args.no_receipt)
133
+ print(json.dumps(report, indent=2))
134
+
135
+
136
+ def scale_once(args: argparse.Namespace) -> None:
137
+ policy = ScalePolicy(
138
+ template_space=args.template_space,
139
+ namespace=args.namespace,
140
+ worker_prefix=args.worker_prefix,
141
+ formation=args.formation,
142
+ hardware=args.hardware,
143
+ root_in_space=args.root_in_space,
144
+ max_workers=args.max_workers,
145
+ max_starts_per_tick=args.max_starts_per_tick,
146
+ start_cooldown_seconds=args.start_cooldown_seconds,
147
+ sleep_time_seconds=args.sleep_time_seconds,
148
+ budget_hourly_usd=args.budget_hourly_usd,
149
+ cpu_space_hourly_usd=args.cpu_space_hourly_usd,
150
+ private=not args.public,
151
+ dry_run=not args.apply,
152
+ pause_on_controller_lost=not args.keep_workers_without_controller,
153
+ )
154
+ report = MeshScaleScaler(FarmStore(args.root), policy).tick()
155
+ print(json.dumps(report, indent=2))
156
+
157
+
158
+ def main() -> None:
159
+ parser = argparse.ArgumentParser(description="KEY CPU farm tools")
160
+ sub = parser.add_subparsers(dest="command", required=True)
161
+
162
+ sim = sub.add_parser("simulate", help="Run a local CPU farm smoke simulation")
163
+ sim.add_argument("--root", default="")
164
+ sim.add_argument("--clean", action="store_true")
165
+ sim.add_argument("--nodes", type=int, default=32)
166
+ sim.add_argument("--dimensions", type=int, default=4)
167
+ sim.add_argument("--workers", type=int, default=4)
168
+ sim.add_argument("--shard-size", type=int, default=8)
169
+ sim.add_argument("--fitness", default="rastrigin")
170
+ sim.add_argument("--timeout", type=float, default=60.0)
171
+ sim.set_defaults(func=simulate)
172
+
173
+ plan_cmd = sub.add_parser("plan", help="Plan CPU Space capacity")
174
+ plan_cmd.add_argument("--pending-jobs", type=int, required=True)
175
+ plan_cmd.add_argument("--active-workers", type=int, default=0)
176
+ plan_cmd.add_argument("--jobs-per-worker", type=int, default=2)
177
+ plan_cmd.add_argument("--max-workers", type=int, default=24)
178
+ plan_cmd.add_argument("--budget-hourly-usd", type=float, default=0.72)
179
+ plan_cmd.add_argument("--cpu-space-hourly-usd", type=float, default=0.03)
180
+ plan_cmd.add_argument("--shard-size", type=int, default=24)
181
+ plan_cmd.set_defaults(func=plan)
182
+
183
+ regulate_cmd = sub.add_parser("regulate", help="Publish farm pulse regulation intent")
184
+ regulate_cmd.add_argument("--root", default="data/farm")
185
+ regulate_cmd.add_argument("--mode", choices=["run", "drain", "shutdown"], required=True)
186
+ regulate_cmd.add_argument("--reason", default="manual_regulation")
187
+ regulate_cmd.add_argument("--target-workers", type=int, default=None)
188
+ regulate_cmd.add_argument("--formation", default="")
189
+ regulate_cmd.add_argument("--run-id", default="")
190
+ regulate_cmd.add_argument("--generation", type=int, default=None)
191
+ regulate_cmd.add_argument("--issued-by", default="key-controller")
192
+ regulate_cmd.set_defaults(func=regulate)
193
+
194
+ faculty_cmd = sub.add_parser("faculty", help="Print CPU farm faculty manifest")
195
+ faculty_cmd.add_argument(
196
+ "--cocoon-capabilities",
197
+ default="",
198
+ help="Optional cocoon_capabilities.py path to extract faculty-only resources from.",
199
+ )
200
+ faculty_cmd.set_defaults(func=faculty)
201
+
202
+ trace_cmd = sub.add_parser("symbiotic-trace", help="Trace CPU farm traffic and pulse health")
203
+ trace_cmd.add_argument("--root", default="data/farm")
204
+ trace_cmd.add_argument("--max-workers", type=int, default=24)
205
+ trace_cmd.add_argument("--budget-hourly-usd", type=float, default=0.72)
206
+ trace_cmd.add_argument("--cpu-space-hourly-usd", type=float, default=0.03)
207
+ trace_cmd.add_argument("--shard-size", type=int, default=24)
208
+ trace_cmd.add_argument("--no-receipt", action="store_true")
209
+ trace_cmd.set_defaults(func=trace)
210
+
211
+ scale_cmd = sub.add_parser("scale-once", help="Honor one tick of MeshScale CPU Space scaling")
212
+ scale_cmd.add_argument("--root", default="data/farm")
213
+ scale_cmd.add_argument("--template-space", required=True)
214
+ scale_cmd.add_argument("--namespace", required=True)
215
+ scale_cmd.add_argument("--worker-prefix", default="meshscale-worker")
216
+ scale_cmd.add_argument("--formation", default="cpu-worker")
217
+ scale_cmd.add_argument("--hardware", default="cpu-upgrade")
218
+ scale_cmd.add_argument("--root-in-space", default="/data/farm")
219
+ scale_cmd.add_argument("--max-workers", type=int, default=24)
220
+ scale_cmd.add_argument("--max-starts-per-tick", type=int, default=2)
221
+ scale_cmd.add_argument("--start-cooldown-seconds", type=int, default=120)
222
+ scale_cmd.add_argument("--sleep-time-seconds", type=int, default=300)
223
+ scale_cmd.add_argument("--budget-hourly-usd", type=float, default=0.72)
224
+ scale_cmd.add_argument("--cpu-space-hourly-usd", type=float, default=0.03)
225
+ scale_cmd.add_argument("--public", action="store_true")
226
+ scale_cmd.add_argument("--apply", action="store_true", help="Actually call the Hugging Face API")
227
+ scale_cmd.add_argument("--keep-workers-without-controller", action="store_true")
228
+ scale_cmd.set_defaults(func=scale_once)
229
+
230
+ args = parser.parse_args()
231
+ args.func(args)
232
+
233
+
234
+ if __name__ == "__main__":
235
+ main()
key_farm/contracts.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict, dataclass, field
4
+ from datetime import datetime, timezone
5
+ from typing import Any, Literal
6
+ import math
7
+ import uuid
8
+
9
+
10
+ SCHEMA_VERSION = 1
11
+ JobState = Literal["pending", "claimed", "done", "failed", "refracted"]
12
+ RegulationMode = Literal["run", "drain", "shutdown"]
13
+ ResourceKind = Literal["cpu"]
14
+
15
+
16
+ def utc_now() -> str:
17
+ return datetime.now(timezone.utc).isoformat()
18
+
19
+
20
+ def new_id(prefix: str) -> str:
21
+ return f"{prefix}_{uuid.uuid4().hex[:16]}"
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class FarmPolicy:
26
+ """Budget and safety boundary for dynamic CPU Space expansion."""
27
+
28
+ target_batch_size: int = 24
29
+ min_batch_size: int = 4
30
+ max_depth: int = 4
31
+ max_workers: int = 24
32
+ max_outstanding_jobs: int = 512
33
+ job_ttl_seconds: int = 3600
34
+ claim_ttl_seconds: int = 900
35
+ pulse_ttl_seconds: int = 120
36
+ controller_pulse_ttl_seconds: int = 180
37
+ cpu_space_hourly_usd: float = 0.03
38
+ budget_hourly_usd: float = 0.72
39
+ expansion_cooldown_seconds: int = 180
40
+ exit_when_pulse_lost: bool = True
41
+ pulse_rate: float = 1.0
42
+ allowed_formations: tuple[str, ...] = ("cpu-worker",)
43
+
44
+ def effective_max_workers(self) -> int:
45
+ budget_workers = math.floor(self.budget_hourly_usd / self.cpu_space_hourly_usd)
46
+ return max(1, min(self.max_workers, budget_workers))
47
+
48
+ def to_dict(self) -> dict[str, Any]:
49
+ data = asdict(self)
50
+ data["effective_max_workers"] = self.effective_max_workers()
51
+ return data
52
+
53
+ @classmethod
54
+ def from_dict(cls, data: dict[str, Any] | None) -> "FarmPolicy":
55
+ if not data:
56
+ return cls()
57
+ data = dict(data)
58
+ aliases = {
59
+ "heartbeat_ttl_seconds": "pulse_ttl_seconds",
60
+ "controller_heartbeat_ttl_seconds": "controller_pulse_ttl_seconds",
61
+ "exit_when_controller_lost": "exit_when_pulse_lost",
62
+ }
63
+ for old_key, new_key in aliases.items():
64
+ if old_key in data and new_key not in data:
65
+ data[new_key] = data[old_key]
66
+ allowed = {field.name for field in cls.__dataclass_fields__.values()}
67
+ return cls(**{key: value for key, value in data.items() if key in allowed})
68
+
69
+
70
+ @dataclass(frozen=True)
71
+ class NodePayload:
72
+ node_id: str
73
+ encoding: str
74
+ payload: str
75
+ sha256: str
76
+
77
+ def to_dict(self) -> dict[str, Any]:
78
+ return asdict(self)
79
+
80
+ @classmethod
81
+ def from_dict(cls, data: dict[str, Any]) -> "NodePayload":
82
+ return cls(**data)
83
+
84
+
85
+ @dataclass(frozen=True)
86
+ class ShardJob:
87
+ run_id: str
88
+ generation: int
89
+ task_type: str
90
+ task_name: str
91
+ config: dict[str, Any]
92
+ nodes: list[NodePayload]
93
+ formation: str = "cpu-worker"
94
+ shard_id: str = field(default_factory=lambda: new_id("shard"))
95
+ parent_shard_id: str | None = None
96
+ depth: int = 0
97
+ created_at: str = field(default_factory=utc_now)
98
+ schema_version: int = SCHEMA_VERSION
99
+
100
+ def to_dict(self) -> dict[str, Any]:
101
+ data = asdict(self)
102
+ data["nodes"] = [node.to_dict() for node in self.nodes]
103
+ return data
104
+
105
+ @classmethod
106
+ def from_dict(cls, data: dict[str, Any]) -> "ShardJob":
107
+ data = dict(data)
108
+ if "task_name" not in data and "fitness_name" in data:
109
+ data["task_name"] = data.pop("fitness_name")
110
+ if "task_type" not in data:
111
+ data["task_type"] = "key_fitness"
112
+ data["nodes"] = [NodePayload.from_dict(node) for node in data.get("nodes", [])]
113
+ allowed = {field.name for field in cls.__dataclass_fields__.values()}
114
+ return cls(**{key: value for key, value in data.items() if key in allowed})
115
+
116
+ def split(self, policy: FarmPolicy) -> list["ShardJob"]:
117
+ if len(self.nodes) <= policy.target_batch_size:
118
+ return [self]
119
+ if self.depth >= policy.max_depth:
120
+ return [self]
121
+
122
+ batch_size = max(policy.min_batch_size, policy.target_batch_size)
123
+ children = []
124
+ for start in range(0, len(self.nodes), batch_size):
125
+ children.append(
126
+ ShardJob(
127
+ run_id=self.run_id,
128
+ generation=self.generation,
129
+ task_type=self.task_type,
130
+ task_name=self.task_name,
131
+ config=self.config,
132
+ nodes=self.nodes[start : start + batch_size],
133
+ formation=self.formation,
134
+ parent_shard_id=self.shard_id,
135
+ depth=self.depth + 1,
136
+ )
137
+ )
138
+ return children
139
+
140
+ @property
141
+ def fitness_name(self) -> str:
142
+ return self.task_name
143
+
144
+
145
+ @dataclass(frozen=True)
146
+ class NodeResult:
147
+ node_id: str
148
+ fitness: float | None
149
+ duration_ms: float
150
+ error: str | None = None
151
+ output: Any | None = None
152
+
153
+ def to_dict(self) -> dict[str, Any]:
154
+ return asdict(self)
155
+
156
+ @classmethod
157
+ def from_dict(cls, data: dict[str, Any]) -> "NodeResult":
158
+ return cls(**data)
159
+
160
+
161
+ @dataclass(frozen=True)
162
+ class ShardResult:
163
+ run_id: str
164
+ generation: int
165
+ shard_id: str
166
+ worker_id: str
167
+ status: JobState
168
+ node_results: list[NodeResult]
169
+ started_at: str
170
+ finished_at: str = field(default_factory=utc_now)
171
+ message: str = ""
172
+ schema_version: int = SCHEMA_VERSION
173
+
174
+ def to_dict(self) -> dict[str, Any]:
175
+ data = asdict(self)
176
+ data["node_results"] = [result.to_dict() for result in self.node_results]
177
+ return data
178
+
179
+ @classmethod
180
+ def from_dict(cls, data: dict[str, Any]) -> "ShardResult":
181
+ data = dict(data)
182
+ data["node_results"] = [
183
+ NodeResult.from_dict(result) for result in data.get("node_results", [])
184
+ ]
185
+ return cls(**data)
186
+
187
+
188
+ @dataclass(frozen=True)
189
+ class ExpansionRequest:
190
+ run_id: str
191
+ generation: int
192
+ reason: str
193
+ requested_workers: int
194
+ active_workers: int
195
+ pending_jobs: int
196
+ policy: dict[str, Any]
197
+ formation: str = "cpu-worker"
198
+ resource_kind: ResourceKind = "cpu"
199
+ hardware: str = "cpu-upgrade"
200
+ space_template: str | None = None
201
+ request_id: str = field(default_factory=lambda: new_id("expand"))
202
+ created_at: str = field(default_factory=utc_now)
203
+ schema_version: int = SCHEMA_VERSION
204
+
205
+ def to_dict(self) -> dict[str, Any]:
206
+ return asdict(self)
207
+
208
+
209
+ @dataclass(frozen=True)
210
+ class WorkerPulse:
211
+ worker_id: str
212
+ run_id: str | None
213
+ generation: int | None
214
+ status: str
215
+ current_shard_id: str | None = None
216
+ formation: str = "cpu-worker"
217
+ pulse: dict[str, float] = field(default_factory=dict)
218
+ timestamp: str = field(default_factory=utc_now)
219
+ schema_version: int = SCHEMA_VERSION
220
+
221
+ def to_dict(self) -> dict[str, Any]:
222
+ return asdict(self)
223
+
224
+
225
+ @dataclass(frozen=True)
226
+ class ControllerPulse:
227
+ controller_id: str
228
+ status: str
229
+ run_id: str | None = None
230
+ generation: int | None = None
231
+ pulse: dict[str, float] = field(default_factory=dict)
232
+ timestamp: str = field(default_factory=utc_now)
233
+ schema_version: int = SCHEMA_VERSION
234
+
235
+ def to_dict(self) -> dict[str, Any]:
236
+ return asdict(self)
237
+
238
+
239
+ @dataclass(frozen=True)
240
+ class RegulationIntent:
241
+ mode: RegulationMode = "run"
242
+ reason: str = "controller_running"
243
+ target_workers: int | None = None
244
+ formation: str | None = None
245
+ run_id: str | None = None
246
+ generation: int | None = None
247
+ issued_by: str = "key-controller"
248
+ issued_at: str = field(default_factory=utc_now)
249
+ schema_version: int = SCHEMA_VERSION
250
+
251
+ def to_dict(self) -> dict[str, Any]:
252
+ return asdict(self)
253
+
254
+ @classmethod
255
+ def from_dict(cls, data: dict[str, Any] | None) -> "RegulationIntent":
256
+ if not data:
257
+ return cls()
258
+ allowed = {field.name for field in cls.__dataclass_fields__.values()}
259
+ return cls(**{key: value for key, value in data.items() if key in allowed})
260
+
261
+
262
+ WorkerHeartbeat = WorkerPulse
263
+ ControllerHeartbeat = ControllerPulse
264
+ FarmControl = RegulationIntent
265
+ WorkItem = NodePayload
266
+ WorkResult = NodeResult
key_farm/controller.py ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from typing import Any
5
+
6
+ from .contracts import (
7
+ ControllerPulse,
8
+ ExpansionRequest,
9
+ FarmPolicy,
10
+ RegulationIntent,
11
+ ShardJob,
12
+ new_id,
13
+ )
14
+ from .evaluator import apply_results_to_population
15
+ from .regulation import create_pulse, tick_pulse
16
+ from .serialization import pack_item, pack_node
17
+ from .store import FarmStore
18
+
19
+
20
+ class FarmController:
21
+ """Controller-side API for dispatching fitness evaluation shards."""
22
+
23
+ def __init__(
24
+ self,
25
+ store: FarmStore,
26
+ policy: FarmPolicy | None = None,
27
+ controller_id: str = "key-controller",
28
+ ):
29
+ self.store = store
30
+ self.policy = policy or FarmPolicy()
31
+ self.controller_id = controller_id
32
+ self._pulse = create_pulse(self.policy.pulse_rate)
33
+
34
+ def pulse(
35
+ self,
36
+ *,
37
+ status: str = "running",
38
+ run_id: str | None = None,
39
+ generation: int | None = None,
40
+ ) -> None:
41
+ self.store.write_controller_pulse(
42
+ ControllerPulse(
43
+ controller_id=self.controller_id,
44
+ status=status,
45
+ run_id=run_id,
46
+ generation=generation,
47
+ pulse=tick_pulse(self._pulse),
48
+ )
49
+ )
50
+
51
+ def heartbeat(
52
+ self,
53
+ *,
54
+ status: str = "running",
55
+ run_id: str | None = None,
56
+ generation: int | None = None,
57
+ ) -> None:
58
+ self.pulse(status=status, run_id=run_id, generation=generation)
59
+
60
+ def resume_workers(
61
+ self,
62
+ *,
63
+ reason: str = "controller_running",
64
+ formation: str | None = None,
65
+ ) -> None:
66
+ self.store.write_regulation(
67
+ RegulationIntent(
68
+ mode="run",
69
+ reason=reason,
70
+ formation=formation,
71
+ issued_by=self.controller_id,
72
+ )
73
+ )
74
+
75
+ def drain_workers(
76
+ self,
77
+ target_workers: int = 0,
78
+ *,
79
+ reason: str = "controller_downshift",
80
+ formation: str | None = None,
81
+ run_id: str | None = None,
82
+ generation: int | None = None,
83
+ ) -> None:
84
+ self.store.write_regulation(
85
+ RegulationIntent(
86
+ mode="drain",
87
+ reason=reason,
88
+ target_workers=max(0, target_workers),
89
+ formation=formation,
90
+ run_id=run_id,
91
+ generation=generation,
92
+ issued_by=self.controller_id,
93
+ )
94
+ )
95
+
96
+ def shutdown_workers(
97
+ self,
98
+ *,
99
+ reason: str = "key_space_shutdown",
100
+ formation: str | None = None,
101
+ run_id: str | None = None,
102
+ generation: int | None = None,
103
+ ) -> None:
104
+ self.store.write_regulation(
105
+ RegulationIntent(
106
+ mode="shutdown",
107
+ reason=reason,
108
+ target_workers=0,
109
+ formation=formation,
110
+ run_id=run_id,
111
+ generation=generation,
112
+ issued_by=self.controller_id,
113
+ )
114
+ )
115
+
116
+ def create_run(self, config: dict[str, Any], run_id: str | None = None) -> str:
117
+ run_id = run_id or new_id("run")
118
+ self.resume_workers(reason="run_created")
119
+ self.pulse(run_id=run_id)
120
+ self.store.create_run(run_id, config, self.policy)
121
+ return run_id
122
+
123
+ def submit_generation(
124
+ self,
125
+ population: list[Any],
126
+ config: dict[str, Any],
127
+ *,
128
+ run_id: str | None = None,
129
+ generation: int = 0,
130
+ fitness_name: str | None = None,
131
+ formation: str = "cpu-worker",
132
+ ) -> tuple[str, list[ShardJob]]:
133
+ run_id = run_id or self.create_run(config)
134
+ fitness_name = fitness_name or config.get("fitness", {}).get("function", "unknown")
135
+ self.resume_workers(reason="generation_submitted", formation=formation)
136
+ self.pulse(run_id=run_id, generation=generation)
137
+ packed = [pack_node(node) for node in population]
138
+ jobs = []
139
+ batch_size = max(self.policy.min_batch_size, self.policy.target_batch_size)
140
+
141
+ for start in range(0, len(packed), batch_size):
142
+ jobs.append(
143
+ ShardJob(
144
+ run_id=run_id,
145
+ generation=generation,
146
+ task_type="key_fitness",
147
+ task_name=fitness_name,
148
+ config=config,
149
+ nodes=packed[start : start + batch_size],
150
+ formation=formation,
151
+ )
152
+ )
153
+
154
+ self.store.put_jobs(jobs)
155
+ self.store.append_event(
156
+ "farm_generation_submitted",
157
+ {
158
+ "run_id": run_id,
159
+ "generation": generation,
160
+ "nodes": len(packed),
161
+ "jobs": len(jobs),
162
+ "fitness_name": fitness_name,
163
+ },
164
+ )
165
+ self._request_initial_capacity(run_id, generation, len(jobs), formation)
166
+ return run_id, jobs
167
+
168
+ def submit_work(
169
+ self,
170
+ items: list[Any],
171
+ config: dict[str, Any],
172
+ *,
173
+ task_type: str = "python_callable",
174
+ task_name: str = "default",
175
+ formation: str = "cpu-worker",
176
+ run_id: str | None = None,
177
+ generation: int = 0,
178
+ ) -> tuple[str, list[ShardJob]]:
179
+ run_id = run_id or self.create_run(config)
180
+ self.resume_workers(reason="work_submitted", formation=formation)
181
+ self.pulse(run_id=run_id, generation=generation)
182
+ packed = [pack_item(item) for item in items]
183
+ jobs = []
184
+ batch_size = max(self.policy.min_batch_size, self.policy.target_batch_size)
185
+
186
+ for start in range(0, len(packed), batch_size):
187
+ jobs.append(
188
+ ShardJob(
189
+ run_id=run_id,
190
+ generation=generation,
191
+ task_type=task_type,
192
+ task_name=task_name,
193
+ config=config,
194
+ nodes=packed[start : start + batch_size],
195
+ formation=formation,
196
+ )
197
+ )
198
+
199
+ self.store.put_jobs(jobs)
200
+ self.store.append_event(
201
+ "farm_work_submitted",
202
+ {
203
+ "run_id": run_id,
204
+ "generation": generation,
205
+ "items": len(packed),
206
+ "jobs": len(jobs),
207
+ "task_type": task_type,
208
+ "task_name": task_name,
209
+ "formation": formation,
210
+ },
211
+ )
212
+ self._request_initial_capacity(run_id, generation, len(jobs), formation)
213
+ return run_id, jobs
214
+
215
+ def collect_generation(
216
+ self,
217
+ run_id: str,
218
+ generation: int,
219
+ expected_node_ids: set[str],
220
+ *,
221
+ timeout_seconds: float = 300.0,
222
+ poll_seconds: float = 1.0,
223
+ ) -> dict[str, float]:
224
+ deadline = time.time() + timeout_seconds
225
+ while True:
226
+ self.pulse(status="collecting", run_id=run_id, generation=generation)
227
+ fitness_by_id = self.result_map(run_id, generation)
228
+ if expected_node_ids.issubset(fitness_by_id.keys()):
229
+ return fitness_by_id
230
+ if time.time() >= deadline:
231
+ missing = sorted(expected_node_ids - fitness_by_id.keys())
232
+ raise TimeoutError(f"missing {len(missing)} fitness results: {missing[:10]}")
233
+ time.sleep(poll_seconds)
234
+
235
+ def result_map(self, run_id: str, generation: int) -> dict[str, float]:
236
+ fitness_by_id: dict[str, float] = {}
237
+ for shard_result in self.store.load_results(run_id, generation):
238
+ for node_result in shard_result.node_results:
239
+ if node_result.error is None and node_result.fitness is not None:
240
+ fitness_by_id[node_result.node_id] = node_result.fitness
241
+ return fitness_by_id
242
+
243
+ def merge_into_population(
244
+ self,
245
+ population: list[Any],
246
+ run_id: str,
247
+ generation: int,
248
+ ) -> dict[str, float]:
249
+ results = self.store.load_results(run_id, generation)
250
+ return apply_results_to_population(population, results)
251
+
252
+ def _request_initial_capacity(
253
+ self,
254
+ run_id: str,
255
+ generation: int,
256
+ pending_jobs: int,
257
+ formation: str,
258
+ ) -> None:
259
+ self.pulse(run_id=run_id, generation=generation)
260
+ active = self.store.active_worker_count(self.policy.pulse_ttl_seconds, formation)
261
+ if active >= min(pending_jobs, self.policy.effective_max_workers()):
262
+ return
263
+ request = ExpansionRequest(
264
+ run_id=run_id,
265
+ generation=generation,
266
+ reason="generation_submitted",
267
+ requested_workers=min(
268
+ pending_jobs,
269
+ self.policy.effective_max_workers() - active,
270
+ ),
271
+ active_workers=active,
272
+ pending_jobs=pending_jobs,
273
+ policy=self.policy.to_dict(),
274
+ formation=formation,
275
+ )
276
+ self.store.write_expansion_request(request)
key_farm/evaluator.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ import inspect
5
+ import json
6
+ import time
7
+ from collections.abc import Callable
8
+ from typing import Any
9
+
10
+ from .contracts import NodeResult, ShardJob, ShardResult, utc_now
11
+ from .serialization import unpack_node
12
+
13
+ TaskHandler = Callable[[ShardJob, str], ShardResult]
14
+ _HANDLERS: dict[str, TaskHandler] = {}
15
+
16
+
17
+ def _fitness_function(config: dict[str, Any]):
18
+ from run import create_fitness_function
19
+
20
+ return create_fitness_function(config)
21
+
22
+
23
+ def register_handler(task_type: str, handler: TaskHandler) -> None:
24
+ _HANDLERS[task_type] = handler
25
+
26
+
27
+ def get_handler(task_type: str) -> TaskHandler:
28
+ if task_type in _HANDLERS:
29
+ return _HANDLERS[task_type]
30
+ if task_type == "python_callable":
31
+ return evaluate_python_callable
32
+ raise KeyError(f"no CPU farm task handler registered for {task_type!r}")
33
+
34
+
35
+ def evaluate_job(job: ShardJob, worker_id: str) -> ShardResult:
36
+ """Evaluate a CPU shard through the registered task handler."""
37
+
38
+ try:
39
+ handler = get_handler(job.task_type)
40
+ return handler(job, worker_id)
41
+ except Exception as exc:
42
+ return ShardResult(
43
+ run_id=job.run_id,
44
+ generation=job.generation,
45
+ shard_id=job.shard_id,
46
+ worker_id=worker_id,
47
+ status="failed",
48
+ node_results=[],
49
+ started_at=utc_now(),
50
+ message=f"{type(exc).__name__}: {exc}",
51
+ )
52
+
53
+
54
+ def evaluate_key_fitness(job: ShardJob, worker_id: str) -> ShardResult:
55
+ """Evaluate a KEY fitness shard and return fitness results keyed by node id."""
56
+
57
+ started_at = utc_now()
58
+ fitness_fn = _fitness_function(job.config)
59
+ node_results: list[NodeResult] = []
60
+
61
+ for node_payload in job.nodes:
62
+ start = time.time()
63
+ try:
64
+ node = unpack_node(node_payload)
65
+ fitness = float(fitness_fn(node))
66
+ node_results.append(
67
+ NodeResult(
68
+ node_id=node_payload.node_id,
69
+ fitness=fitness,
70
+ duration_ms=(time.time() - start) * 1000,
71
+ )
72
+ )
73
+ except Exception as exc:
74
+ node_results.append(
75
+ NodeResult(
76
+ node_id=node_payload.node_id,
77
+ fitness=None,
78
+ duration_ms=(time.time() - start) * 1000,
79
+ error=f"{type(exc).__name__}: {exc}",
80
+ )
81
+ )
82
+
83
+ status = "done" if all(result.error is None for result in node_results) else "failed"
84
+ return ShardResult(
85
+ run_id=job.run_id,
86
+ generation=job.generation,
87
+ shard_id=job.shard_id,
88
+ worker_id=worker_id,
89
+ status=status,
90
+ node_results=node_results,
91
+ started_at=started_at,
92
+ )
93
+
94
+
95
+ def _load_callable(path: str) -> Callable[..., Any]:
96
+ module_name, _, attr = path.partition(":")
97
+ if not module_name or not attr:
98
+ raise ValueError("callable path must be 'module:function'")
99
+ module = importlib.import_module(module_name)
100
+ target = module
101
+ for part in attr.split("."):
102
+ target = getattr(target, part)
103
+ if not callable(target):
104
+ raise TypeError(f"{path!r} is not callable")
105
+ return target
106
+
107
+
108
+ def _safe_output(value: Any) -> Any:
109
+ try:
110
+ json.dumps(value)
111
+ return value
112
+ except TypeError:
113
+ return repr(value)
114
+
115
+
116
+ def evaluate_python_callable(job: ShardJob, worker_id: str) -> ShardResult:
117
+ """Generic CPU handler for worker templates that ship a local callable.
118
+
119
+ Configure with:
120
+ {"cpu_farm": {"callable": "package.module:function"}}
121
+
122
+ The callable receives either `(item, config, job)` or the leading subset of
123
+ those arguments, based on its signature. Numeric returns become `fitness`;
124
+ dict returns are stored in `output` and may include a numeric `fitness`.
125
+ """
126
+
127
+ started_at = utc_now()
128
+ handler_path = (
129
+ job.config.get("cpu_farm", {}).get("callable")
130
+ or job.config.get("farm", {}).get("callable")
131
+ )
132
+ if not handler_path:
133
+ raise ValueError("python_callable task requires cpu_farm.callable")
134
+
135
+ fn = _load_callable(str(handler_path))
136
+ params = inspect.signature(fn).parameters
137
+ node_results: list[NodeResult] = []
138
+
139
+ for node_payload in job.nodes:
140
+ start = time.time()
141
+ try:
142
+ item = unpack_node(node_payload)
143
+ args = [item, job.config, job][: len(params)]
144
+ value = fn(*args)
145
+ fitness = None
146
+ output = value
147
+ if isinstance(value, (int, float)):
148
+ fitness = float(value)
149
+ output = {"value": fitness}
150
+ elif isinstance(value, dict) and isinstance(value.get("fitness"), (int, float)):
151
+ fitness = float(value["fitness"])
152
+ node_results.append(
153
+ NodeResult(
154
+ node_id=node_payload.node_id,
155
+ fitness=fitness,
156
+ duration_ms=(time.time() - start) * 1000,
157
+ output=_safe_output(output),
158
+ )
159
+ )
160
+ except Exception as exc:
161
+ node_results.append(
162
+ NodeResult(
163
+ node_id=node_payload.node_id,
164
+ fitness=None,
165
+ duration_ms=(time.time() - start) * 1000,
166
+ error=f"{type(exc).__name__}: {exc}",
167
+ )
168
+ )
169
+
170
+ status = "done" if all(result.error is None for result in node_results) else "failed"
171
+ return ShardResult(
172
+ run_id=job.run_id,
173
+ generation=job.generation,
174
+ shard_id=job.shard_id,
175
+ worker_id=worker_id,
176
+ status=status,
177
+ node_results=node_results,
178
+ started_at=started_at,
179
+ )
180
+
181
+
182
+ def apply_results_to_population(population: list[Any], results: list[ShardResult]) -> dict[str, float]:
183
+ """Merge remote fitness values back into live nodes."""
184
+
185
+ node_by_id = {str(getattr(node, "id", "")): node for node in population}
186
+ fitness_by_id: dict[str, float] = {}
187
+ for shard_result in results:
188
+ for node_result in shard_result.node_results:
189
+ if node_result.error is not None or node_result.fitness is None:
190
+ continue
191
+ fitness_by_id[node_result.node_id] = float(node_result.fitness)
192
+
193
+ for node_id, fitness in fitness_by_id.items():
194
+ node = node_by_id.get(node_id)
195
+ if node is not None:
196
+ node.fitness = fitness
197
+
198
+ return fitness_by_id
199
+
200
+
201
+ register_handler("key_fitness", evaluate_key_fitness)
key_farm/faculty.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import importlib.util
4
+ import sys
5
+ from dataclasses import asdict, dataclass, field
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+
10
+ ASSOCIATION_KEYS = {
11
+ "after",
12
+ "before",
13
+ "edges",
14
+ "followups",
15
+ "graph",
16
+ "links",
17
+ "routes",
18
+ }
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class FarmFaculty:
23
+ key: str
24
+ group: str
25
+ label: str
26
+ teaches: str
27
+ payload: dict[str, Any] = field(default_factory=dict)
28
+ formation: str = "cpu-worker"
29
+ mutates: bool = False
30
+ risk_class: str = "inspect"
31
+ timeout_seconds: float = 15.0
32
+ expected_outputs: tuple[str, ...] = ()
33
+
34
+ def to_dict(self) -> dict[str, Any]:
35
+ data = asdict(self)
36
+ data["expected_outputs"] = list(self.expected_outputs)
37
+ return data
38
+
39
+
40
+ CPU_FARM_FACULTIES: tuple[FarmFaculty, ...] = (
41
+ FarmFaculty(
42
+ "health",
43
+ "diagnostics",
44
+ "Farm Health",
45
+ "Read pulse, worker, backlog, and regulation status.",
46
+ expected_outputs=("pulse", "workers", "traffic", "regulation"),
47
+ ),
48
+ FarmFaculty(
49
+ "faculty",
50
+ "diagnostics",
51
+ "Faculty Manifest",
52
+ "Expose CPU-farm faculties without graph links or followup routes.",
53
+ expected_outputs=("capabilities", "groups"),
54
+ ),
55
+ FarmFaculty(
56
+ "symbiotic_trace",
57
+ "diagnostics",
58
+ "Symbiotic Trace",
59
+ "Interpret CPU-farm traffic, pressure, pulse health, and failure signals.",
60
+ expected_outputs=("summary", "signals", "recommendations", "cascade"),
61
+ ),
62
+ FarmFaculty(
63
+ "plan",
64
+ "regulation",
65
+ "Capacity Plan",
66
+ "Estimate CPU worker count from backlog, budget, and active pulses.",
67
+ payload={"pending_jobs": 120, "active_workers": 4, "budget_hourly_usd": 0.72},
68
+ expected_outputs=("requested_workers", "effective_max_workers"),
69
+ ),
70
+ FarmFaculty(
71
+ "regulate",
72
+ "regulation",
73
+ "Pulse Regulation",
74
+ "Publish run, drain, or shutdown intent through the farm pulse ledger.",
75
+ payload={"mode": "drain", "target_workers": 4},
76
+ mutates=True,
77
+ risk_class="mutating",
78
+ expected_outputs=("mode", "reason", "target_workers"),
79
+ ),
80
+ FarmFaculty(
81
+ "submit_work",
82
+ "work",
83
+ "Submit CPU Work",
84
+ "Create CPU-only shard jobs for a registered task handler.",
85
+ payload={"task_type": "python_callable", "formation": "cpu-worker"},
86
+ mutates=True,
87
+ risk_class="mutating",
88
+ timeout_seconds=45.0,
89
+ expected_outputs=("run_id", "jobs"),
90
+ ),
91
+ FarmFaculty(
92
+ "worker",
93
+ "work",
94
+ "Worker Loop",
95
+ "Claim CPU shards, evaluate through a task handler, and write receipts.",
96
+ payload={"formation": "cpu-worker", "poll_seconds": 2},
97
+ mutates=True,
98
+ risk_class="mutating",
99
+ timeout_seconds=90.0,
100
+ expected_outputs=("worker_id", "pulse", "results"),
101
+ ),
102
+ )
103
+
104
+
105
+ def _strip_associations(data: dict[str, Any]) -> dict[str, Any]:
106
+ clean = {key: value for key, value in data.items() if key not in ASSOCIATION_KEYS}
107
+ affordance = clean.get("agent_affordance")
108
+ if isinstance(affordance, dict):
109
+ clean["agent_affordance"] = {
110
+ key: value for key, value in affordance.items() if key not in ASSOCIATION_KEYS
111
+ }
112
+ return clean
113
+
114
+
115
+ def farm_faculty_manifest() -> dict[str, Any]:
116
+ capabilities = [faculty.to_dict() for faculty in CPU_FARM_FACULTIES]
117
+ groups: dict[str, list[dict[str, Any]]] = {}
118
+ for capability in capabilities:
119
+ groups.setdefault(str(capability.get("group", "")), []).append(capability)
120
+ return {
121
+ "surface": "hf_cpu_farm",
122
+ "count": len(capabilities),
123
+ "groups": groups,
124
+ "capabilities": capabilities,
125
+ "links_included": False,
126
+ }
127
+
128
+
129
+ def load_cocoon_faculty(capability_file: str | Path) -> dict[str, Any]:
130
+ """Load only the authority faculty/capability contract.
131
+
132
+ This intentionally strips graph links, followups, and route associations so
133
+ the CPU farm can align to the authority OS substrate without importing that
134
+ topology.
135
+ """
136
+
137
+ path = Path(capability_file)
138
+ spec = importlib.util.spec_from_file_location("cocoon_capabilities_faculty_only", path)
139
+ if spec is None or spec.loader is None:
140
+ raise ImportError(f"cannot load capability file: {path}")
141
+ module = importlib.util.module_from_spec(spec)
142
+ sys.modules[spec.name] = module
143
+ spec.loader.exec_module(module)
144
+ manifest = module.capability_manifest()
145
+ capabilities = [_strip_associations(dict(item)) for item in manifest.get("capabilities", [])]
146
+ groups: dict[str, list[dict[str, Any]]] = {}
147
+ for capability in capabilities:
148
+ groups.setdefault(str(capability.get("group", "")), []).append(capability)
149
+ return {
150
+ "surface": "cocoon_authority_faculty_only",
151
+ "source": str(path),
152
+ "count": len(capabilities),
153
+ "groups": groups,
154
+ "capabilities": capabilities,
155
+ "links_included": False,
156
+ }
key_farm/regulation.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from typing import Any
5
+
6
+
7
+ def create_pulse(rate: float = 1.0) -> Any | None:
8
+ """Create KEY's Pulse when available without making the farm depend on UI code."""
9
+
10
+ try:
11
+ from pulse import Pulse
12
+
13
+ return Pulse(rate=rate)
14
+ except Exception:
15
+ return None
16
+
17
+
18
+ def tick_pulse(pulse: Any | None) -> dict[str, float]:
19
+ if pulse is None:
20
+ return {
21
+ "depth": 0.0,
22
+ "phase": 0.0,
23
+ "cycle": 0.0,
24
+ "intensity": 1.0,
25
+ "timestamp": time.time(),
26
+ }
27
+ try:
28
+ state = pulse.tick()
29
+ return {
30
+ "depth": float(state.get("depth", 0.0)),
31
+ "phase": float(state.get("phase", 0.0)),
32
+ "cycle": float(state.get("cycle", 0.0)),
33
+ "intensity": float(state.get("intensity", 1.0)),
34
+ "timestamp": float(state.get("timestamp", time.time())),
35
+ }
36
+ except Exception:
37
+ return tick_pulse(None)
key_farm/scaler.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import time
6
+ from dataclasses import asdict, dataclass, field
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from .contracts import FarmPolicy, utc_now
11
+ from .store import FarmStore
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class ScalePolicy:
16
+ """Hard boundary for starting and pausing MeshScale CPU worker Spaces."""
17
+
18
+ template_space: str
19
+ namespace: str
20
+ worker_prefix: str = "meshscale-worker"
21
+ formation: str = "cpu-worker"
22
+ hardware: str = "cpu-upgrade"
23
+ root_in_space: str = "/data/farm"
24
+ max_workers: int = 24
25
+ max_starts_per_tick: int = 2
26
+ start_cooldown_seconds: int = 120
27
+ sleep_time_seconds: int = 300
28
+ budget_hourly_usd: float = 0.72
29
+ cpu_space_hourly_usd: float = 0.03
30
+ private: bool = True
31
+ dry_run: bool = True
32
+ token_env: str = "HF_TOKEN"
33
+ pause_on_controller_lost: bool = True
34
+
35
+ def effective_max_workers(self) -> int:
36
+ if self.cpu_space_hourly_usd <= 0:
37
+ return max(1, self.max_workers)
38
+ budget_workers = int(self.budget_hourly_usd // self.cpu_space_hourly_usd)
39
+ return max(1, min(self.max_workers, budget_workers))
40
+
41
+ def to_dict(self) -> dict[str, Any]:
42
+ data = asdict(self)
43
+ data["effective_max_workers"] = self.effective_max_workers()
44
+ return data
45
+
46
+
47
+ @dataclass
48
+ class ScalerState:
49
+ workers: list[str] = field(default_factory=list)
50
+ processed_requests: list[str] = field(default_factory=list)
51
+ last_start_at: float = 0.0
52
+ last_tick_at: str = ""
53
+
54
+ @classmethod
55
+ def from_dict(cls, data: dict[str, Any] | None) -> "ScalerState":
56
+ if not data:
57
+ return cls()
58
+ return cls(
59
+ workers=list(data.get("workers", [])),
60
+ processed_requests=list(data.get("processed_requests", [])),
61
+ last_start_at=float(data.get("last_start_at", 0.0)),
62
+ last_tick_at=str(data.get("last_tick_at", "")),
63
+ )
64
+
65
+ def to_dict(self) -> dict[str, Any]:
66
+ return {
67
+ "workers": sorted(set(self.workers)),
68
+ "processed_requests": sorted(set(self.processed_requests)),
69
+ "last_start_at": self.last_start_at,
70
+ "last_tick_at": self.last_tick_at,
71
+ }
72
+
73
+
74
+ class MeshScaleScaler:
75
+ """Optional Hugging Face Space scaler for MeshScale workers.
76
+
77
+ The scaler is intentionally outside the KEY generation path. It only reads
78
+ expansion requests, starts CPU worker Spaces under policy, and pauses known
79
+ workers when the controller is no longer pulsing.
80
+ """
81
+
82
+ def __init__(self, store: FarmStore, policy: ScalePolicy):
83
+ self.store = store
84
+ self.policy = policy
85
+ self.state_path = self.store.root / "scaler" / "state.json"
86
+
87
+ def tick(self) -> dict[str, Any]:
88
+ state = self._load_state()
89
+ state.last_tick_at = utc_now()
90
+
91
+ regulation = self.store.read_regulation()
92
+ controller_alive = self.store.controller_pulse_alive(
93
+ FarmPolicy().controller_pulse_ttl_seconds
94
+ )
95
+ actions: list[dict[str, Any]] = []
96
+
97
+ should_pause = regulation.mode == "shutdown" or (
98
+ self.policy.pause_on_controller_lost and not controller_alive
99
+ )
100
+ if should_pause:
101
+ actions.extend(self._pause_workers(state))
102
+ self._save_state(state)
103
+ return self._report(state, actions, controller_alive, reason="pause")
104
+
105
+ requests = self._pending_expansion_requests(state)
106
+ requested = sum(max(0, int(req.get("requested_workers", 0))) for req in requests)
107
+ if requested <= 0:
108
+ self._save_state(state)
109
+ return self._report(state, actions, controller_alive, reason="idle")
110
+
111
+ active = self.store.active_worker_count(FarmPolicy().pulse_ttl_seconds, self.policy.formation)
112
+ known = len(set(state.workers))
113
+ allowed = self.policy.effective_max_workers()
114
+ available = max(0, allowed - max(active, known))
115
+ to_start = min(requested, available, self.policy.max_starts_per_tick)
116
+
117
+ cooldown_remaining = self._cooldown_remaining(state)
118
+ if cooldown_remaining > 0:
119
+ actions.append(
120
+ {
121
+ "action": "cooldown",
122
+ "seconds_remaining": round(cooldown_remaining, 3),
123
+ "requested_workers": requested,
124
+ }
125
+ )
126
+ self._mark_requests_processed(state, requests)
127
+ self._save_state(state)
128
+ return self._report(state, actions, controller_alive, reason="cooldown")
129
+
130
+ for _ in range(to_start):
131
+ repo_id = self._next_worker_repo_id(state)
132
+ action = self._start_worker(repo_id)
133
+ actions.append(action)
134
+ state.workers.append(repo_id)
135
+ state.last_start_at = time.time()
136
+
137
+ self._mark_requests_processed(state, requests)
138
+ self._save_state(state)
139
+ return self._report(state, actions, controller_alive, reason="scaled")
140
+
141
+ def _load_state(self) -> ScalerState:
142
+ try:
143
+ return ScalerState.from_dict(json.loads(self.state_path.read_text(encoding="utf-8")))
144
+ except Exception:
145
+ return ScalerState()
146
+
147
+ def _save_state(self, state: ScalerState) -> None:
148
+ self.store._write_json_atomic(self.state_path, state.to_dict())
149
+
150
+ def _pending_expansion_requests(self, state: ScalerState) -> list[dict[str, Any]]:
151
+ processed = set(state.processed_requests)
152
+ requests: list[dict[str, Any]] = []
153
+ expansion_dir = self.store.expansion_dir()
154
+ if not expansion_dir.exists():
155
+ return requests
156
+ for path in sorted(expansion_dir.glob("*.json")):
157
+ try:
158
+ data = json.loads(path.read_text(encoding="utf-8"))
159
+ except Exception:
160
+ continue
161
+ request_id = str(data.get("request_id") or path.stem)
162
+ if request_id in processed:
163
+ continue
164
+ if data.get("resource_kind", "cpu") != "cpu":
165
+ continue
166
+ if data.get("formation", self.policy.formation) != self.policy.formation:
167
+ continue
168
+ requests.append(data)
169
+ return requests
170
+
171
+ def _mark_requests_processed(self, state: ScalerState, requests: list[dict[str, Any]]) -> None:
172
+ processed = set(state.processed_requests)
173
+ for request in requests:
174
+ request_id = request.get("request_id")
175
+ if request_id:
176
+ processed.add(str(request_id))
177
+ state.processed_requests = sorted(processed)[-2048:]
178
+
179
+ def _cooldown_remaining(self, state: ScalerState) -> float:
180
+ if state.last_start_at <= 0:
181
+ return 0.0
182
+ elapsed = time.time() - state.last_start_at
183
+ return max(0.0, self.policy.start_cooldown_seconds - elapsed)
184
+
185
+ def _next_worker_repo_id(self, state: ScalerState) -> str:
186
+ existing = set(state.workers)
187
+ for idx in range(1, self.policy.effective_max_workers() + 1):
188
+ repo_id = f"{self.policy.namespace}/{self.policy.worker_prefix}-{idx:03d}"
189
+ if repo_id not in existing:
190
+ return repo_id
191
+ raise RuntimeError("no worker repo id available under policy cap")
192
+
193
+ def _start_worker(self, repo_id: str) -> dict[str, Any]:
194
+ variables = [
195
+ {"key": "KEY_FARM_ROOT", "value": self.policy.root_in_space},
196
+ {"key": "KEY_FARM_WORKER_ID", "value": repo_id.rsplit("/", 1)[-1]},
197
+ {"key": "KEY_FARM_FORMATION", "value": self.policy.formation},
198
+ ]
199
+ payload = {
200
+ "action": "start_worker",
201
+ "repo_id": repo_id,
202
+ "template_space": self.policy.template_space,
203
+ "hardware": self.policy.hardware,
204
+ "formation": self.policy.formation,
205
+ "dry_run": self.policy.dry_run,
206
+ }
207
+ if self.policy.dry_run:
208
+ self.store.append_event("farm_scaler_dry_run_start", payload)
209
+ return payload
210
+
211
+ api = self._api()
212
+ api.duplicate_space(
213
+ from_id=self.policy.template_space,
214
+ to_id=repo_id,
215
+ private=self.policy.private,
216
+ exist_ok=True,
217
+ hardware=self.policy.hardware,
218
+ sleep_time=self.policy.sleep_time_seconds,
219
+ variables=variables,
220
+ )
221
+ api.request_space_hardware(
222
+ repo_id=repo_id,
223
+ hardware=self.policy.hardware,
224
+ sleep_time=self.policy.sleep_time_seconds,
225
+ )
226
+ api.restart_space(repo_id=repo_id)
227
+ self.store.append_event("farm_scaler_started_worker", payload)
228
+ return payload
229
+
230
+ def _pause_workers(self, state: ScalerState) -> list[dict[str, Any]]:
231
+ actions = []
232
+ for repo_id in sorted(set(state.workers)):
233
+ payload = {
234
+ "action": "pause_worker",
235
+ "repo_id": repo_id,
236
+ "dry_run": self.policy.dry_run,
237
+ }
238
+ if not self.policy.dry_run:
239
+ self._api().pause_space(repo_id=repo_id)
240
+ actions.append(payload)
241
+ if actions:
242
+ self.store.append_event("farm_scaler_pause_workers", {"actions": actions})
243
+ state.workers = []
244
+ return actions
245
+
246
+ def _api(self):
247
+ token = os.environ.get(self.policy.token_env)
248
+ if not token:
249
+ raise RuntimeError(f"{self.policy.token_env} is required when dry_run is false")
250
+ from huggingface_hub import HfApi
251
+
252
+ return HfApi(token=token)
253
+
254
+ def _report(
255
+ self,
256
+ state: ScalerState,
257
+ actions: list[dict[str, Any]],
258
+ controller_alive: bool,
259
+ *,
260
+ reason: str,
261
+ ) -> dict[str, Any]:
262
+ return {
263
+ "reason": reason,
264
+ "controller_alive": controller_alive,
265
+ "policy": self.policy.to_dict(),
266
+ "state": state.to_dict(),
267
+ "actions": actions,
268
+ }
269
+
270
+
271
+ __all__ = ["MeshScaleScaler", "ScalePolicy", "ScalerState"]
key_farm/serialization.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import gzip
5
+ import hashlib
6
+ import pickle
7
+ from typing import Any
8
+
9
+ from .contracts import NodePayload
10
+
11
+
12
+ ENCODING = "pickle+gzip+base64"
13
+
14
+
15
+ def _encode_bytes(raw: bytes) -> str:
16
+ return base64.b64encode(gzip.compress(raw)).decode("ascii")
17
+
18
+
19
+ def _decode_bytes(payload: str) -> bytes:
20
+ return gzip.decompress(base64.b64decode(payload.encode("ascii")))
21
+
22
+
23
+ def pack_object(obj: Any) -> tuple[str, str]:
24
+ raw = pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL)
25
+ return _encode_bytes(raw), hashlib.sha256(raw).hexdigest()
26
+
27
+
28
+ def unpack_object(payload: str, sha256: str | None = None) -> Any:
29
+ raw = _decode_bytes(payload)
30
+ digest = hashlib.sha256(raw).hexdigest()
31
+ if sha256 and digest != sha256:
32
+ raise ValueError(f"payload digest mismatch: {digest} != {sha256}")
33
+ return pickle.loads(raw)
34
+
35
+
36
+ def pack_node(node: Any) -> NodePayload:
37
+ node_id = str(getattr(node, "id", ""))
38
+ if not node_id:
39
+ raise ValueError("node must expose a stable id before KEY generation dispatch")
40
+ return pack_item(node, node_id)
41
+
42
+
43
+ def pack_item(item: Any, item_id: str | None = None) -> NodePayload:
44
+ payload, digest = pack_object(item)
45
+ stable_id = item_id or str(getattr(item, "id", "")) or digest[:16]
46
+ return NodePayload(node_id=stable_id, encoding=ENCODING, payload=payload, sha256=digest)
47
+
48
+
49
+ def unpack_node(node_payload: NodePayload) -> Any:
50
+ if node_payload.encoding != ENCODING:
51
+ raise ValueError(f"unsupported node encoding: {node_payload.encoding}")
52
+ return unpack_object(node_payload.payload, node_payload.sha256)
key_farm/store.py ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import time
6
+ from pathlib import Path
7
+ from typing import Any, Iterable
8
+
9
+ from .amalgam_bridge import observe_event
10
+ from .contracts import (
11
+ ControllerPulse,
12
+ ExpansionRequest,
13
+ FarmPolicy,
14
+ RegulationIntent,
15
+ ShardJob,
16
+ ShardResult,
17
+ WorkerPulse,
18
+ new_id,
19
+ utc_now,
20
+ )
21
+
22
+
23
+ class FarmStore:
24
+ """Filesystem-backed farm ledger.
25
+
26
+ The root can be a local directory for tests or a bucket volume mount in
27
+ Hugging Face Spaces. The layout avoids a database so many CPU Spaces can
28
+ participate through ordinary file IO.
29
+ """
30
+
31
+ def __init__(self, root: str | Path):
32
+ self.root = Path(root)
33
+ self.root.mkdir(parents=True, exist_ok=True)
34
+
35
+ @classmethod
36
+ def from_env(cls) -> "FarmStore":
37
+ return cls(os.environ.get("KEY_FARM_ROOT", "/data/farm"))
38
+
39
+ def run_dir(self, run_id: str) -> Path:
40
+ return self.root / "runs" / run_id
41
+
42
+ def generation_dir(self, run_id: str, generation: int) -> Path:
43
+ return self.run_dir(run_id) / "generations" / str(generation)
44
+
45
+ def jobs_dir(self, run_id: str, generation: int, state: str) -> Path:
46
+ return self.generation_dir(run_id, generation) / "jobs" / state
47
+
48
+ def results_dir(self, run_id: str, generation: int) -> Path:
49
+ return self.generation_dir(run_id, generation) / "results"
50
+
51
+ def pulses_dir(self) -> Path:
52
+ return self.root / "pulses"
53
+
54
+ def worker_pulses_dir(self) -> Path:
55
+ return self.pulses_dir() / "workers"
56
+
57
+ def heartbeats_dir(self) -> Path:
58
+ return self.worker_pulses_dir()
59
+
60
+ def regulation_dir(self) -> Path:
61
+ return self.root / "regulation"
62
+
63
+ def control_dir(self) -> Path:
64
+ return self.regulation_dir()
65
+
66
+ def regulation_path(self) -> Path:
67
+ return self.regulation_dir() / "intent.json"
68
+
69
+ def control_path(self) -> Path:
70
+ return self.regulation_path()
71
+
72
+ def controller_pulse_path(self) -> Path:
73
+ return self.pulses_dir() / "controller.json"
74
+
75
+ def controller_heartbeat_path(self) -> Path:
76
+ return self.controller_pulse_path()
77
+
78
+ def expansion_dir(self) -> Path:
79
+ return self.root / "expansion_requests"
80
+
81
+ def event_log(self) -> Path:
82
+ return self.root / "events.jsonl"
83
+
84
+ def _write_json_atomic(self, path: Path, data: dict[str, Any]) -> None:
85
+ path.parent.mkdir(parents=True, exist_ok=True)
86
+ temp = path.with_name(f".{path.name}.{new_id('tmp')}")
87
+ temp.write_text(json.dumps(data, indent=2, sort_keys=True), encoding="utf-8")
88
+ last_error: OSError | None = None
89
+ for attempt in range(6):
90
+ try:
91
+ temp.replace(path)
92
+ return
93
+ except PermissionError as exc:
94
+ last_error = exc
95
+ time.sleep(0.025 * (attempt + 1))
96
+ if last_error is not None:
97
+ temp.unlink(missing_ok=True)
98
+ raise last_error
99
+ temp.replace(path)
100
+
101
+ def append_event(self, event_type: str, payload: dict[str, Any]) -> None:
102
+ event = {
103
+ "timestamp": utc_now(),
104
+ "event_type": event_type,
105
+ "payload": payload,
106
+ }
107
+ self.event_log().parent.mkdir(parents=True, exist_ok=True)
108
+ with self.event_log().open("a", encoding="utf-8") as handle:
109
+ handle.write(json.dumps(event, sort_keys=True) + "\n")
110
+ observe_event("key-cpu-farm", event_type, payload)
111
+
112
+ def create_run(self, run_id: str, config: dict[str, Any], policy: FarmPolicy) -> None:
113
+ data = {
114
+ "run_id": run_id,
115
+ "created_at": utc_now(),
116
+ "config": config,
117
+ "policy": policy.to_dict(),
118
+ }
119
+ self._write_json_atomic(self.run_dir(run_id) / "run.json", data)
120
+ self.append_event("farm_run_created", {"run_id": run_id, "policy": policy.to_dict()})
121
+
122
+ def put_job(self, job: ShardJob) -> Path:
123
+ path = self.jobs_dir(job.run_id, job.generation, "pending") / f"{job.shard_id}.json"
124
+ self._write_json_atomic(path, job.to_dict())
125
+ return path
126
+
127
+ def put_jobs(self, jobs: Iterable[ShardJob]) -> int:
128
+ count = 0
129
+ for job in jobs:
130
+ self.put_job(job)
131
+ count += 1
132
+ return count
133
+
134
+ def list_jobs(self, run_id: str, generation: int, state: str) -> list[Path]:
135
+ path = self.jobs_dir(run_id, generation, state)
136
+ if not path.exists():
137
+ return []
138
+ return sorted(path.glob("*.json"))
139
+
140
+ def pending_jobs(self, formation: str | None = None) -> list[Path]:
141
+ paths = sorted((self.root / "runs").glob("*/generations/*/jobs/pending/*.json"))
142
+ if formation is None:
143
+ return paths
144
+ filtered = []
145
+ for path in paths:
146
+ try:
147
+ data = json.loads(path.read_text(encoding="utf-8"))
148
+ if data.get("formation", "cpu-worker") == formation:
149
+ filtered.append(path)
150
+ except Exception:
151
+ continue
152
+ return filtered
153
+
154
+ def claimed_jobs(self) -> list[Path]:
155
+ return sorted((self.root / "runs").glob("*/generations/*/jobs/claimed/*.json"))
156
+
157
+ def claim_next(
158
+ self,
159
+ worker_id: str,
160
+ formations: tuple[str, ...] | None = None,
161
+ ) -> tuple[ShardJob, Path] | None:
162
+ allowed_formations = set(formations or ())
163
+ for path in self.pending_jobs():
164
+ try:
165
+ data = json.loads(path.read_text(encoding="utf-8"))
166
+ job = ShardJob.from_dict(data)
167
+ if allowed_formations and job.formation not in allowed_formations:
168
+ continue
169
+ claimed = self.jobs_dir(job.run_id, job.generation, "claimed") / path.name
170
+ claimed.parent.mkdir(parents=True, exist_ok=True)
171
+ path.rename(claimed)
172
+ claim_data = job.to_dict()
173
+ claim_data["claimed_by"] = worker_id
174
+ claim_data["claimed_at"] = utc_now()
175
+ self._write_json_atomic(claimed, claim_data)
176
+ return job, claimed
177
+ except FileNotFoundError:
178
+ continue
179
+ except OSError:
180
+ continue
181
+ return None
182
+
183
+ def complete_job(self, job: ShardJob, result: ShardResult, claimed_path: Path | None) -> None:
184
+ result_path = self.results_dir(job.run_id, job.generation) / f"{job.shard_id}.json"
185
+ try:
186
+ self._write_json_atomic(result_path, result.to_dict())
187
+ except PermissionError:
188
+ if not result_path.exists():
189
+ raise
190
+ if claimed_path and claimed_path.exists():
191
+ done_path = self.jobs_dir(job.run_id, job.generation, result.status) / claimed_path.name
192
+ done_path.parent.mkdir(parents=True, exist_ok=True)
193
+ try:
194
+ claimed_path.rename(done_path)
195
+ except OSError:
196
+ self._write_json_atomic(done_path, job.to_dict())
197
+ self.append_event(
198
+ "farm_job_completed",
199
+ {
200
+ "run_id": job.run_id,
201
+ "generation": job.generation,
202
+ "shard_id": job.shard_id,
203
+ "status": result.status,
204
+ "worker_id": result.worker_id,
205
+ "node_results": len(result.node_results),
206
+ },
207
+ )
208
+
209
+ def fail_job(self, job: ShardJob, worker_id: str, message: str, claimed_path: Path | None) -> None:
210
+ result = ShardResult(
211
+ run_id=job.run_id,
212
+ generation=job.generation,
213
+ shard_id=job.shard_id,
214
+ worker_id=worker_id,
215
+ status="failed",
216
+ node_results=[],
217
+ started_at=utc_now(),
218
+ message=message,
219
+ )
220
+ self.complete_job(job, result, claimed_path)
221
+
222
+ def load_results(self, run_id: str, generation: int) -> list[ShardResult]:
223
+ results = []
224
+ path = self.results_dir(run_id, generation)
225
+ if not path.exists():
226
+ return results
227
+ for result_path in sorted(path.glob("*.json")):
228
+ try:
229
+ results.append(ShardResult.from_dict(json.loads(result_path.read_text(encoding="utf-8"))))
230
+ except Exception:
231
+ continue
232
+ return results
233
+
234
+ def write_worker_pulse(self, pulse: WorkerPulse) -> None:
235
+ path = self.worker_pulses_dir() / f"{pulse.worker_id}.json"
236
+ self._write_json_atomic(path, pulse.to_dict())
237
+
238
+ def write_heartbeat(self, heartbeat: WorkerPulse) -> None:
239
+ self.write_worker_pulse(heartbeat)
240
+
241
+ def write_controller_pulse(self, pulse: ControllerPulse) -> None:
242
+ self._write_json_atomic(self.controller_pulse_path(), pulse.to_dict())
243
+
244
+ def write_controller_heartbeat(self, heartbeat: ControllerPulse) -> None:
245
+ self.write_controller_pulse(heartbeat)
246
+
247
+ def controller_pulse_alive(self, ttl_seconds: int) -> bool:
248
+ path = self.controller_pulse_path()
249
+ if not path.exists():
250
+ return False
251
+ try:
252
+ return path.stat().st_mtime >= time.time() - ttl_seconds
253
+ except OSError:
254
+ return False
255
+
256
+ def controller_alive(self, ttl_seconds: int) -> bool:
257
+ return self.controller_pulse_alive(ttl_seconds)
258
+
259
+ def active_worker_count(self, ttl_seconds: int, formation: str | None = None) -> int:
260
+ cutoff = time.time() - ttl_seconds
261
+ count = 0
262
+ pulse_dir = self.worker_pulses_dir()
263
+ for path in pulse_dir.glob("*.json") if pulse_dir.exists() else []:
264
+ try:
265
+ if path.stat().st_mtime < cutoff:
266
+ continue
267
+ if formation is not None:
268
+ data = json.loads(path.read_text(encoding="utf-8"))
269
+ if data.get("formation", "cpu-worker") != formation:
270
+ continue
271
+ count += 1
272
+ except OSError:
273
+ continue
274
+ except Exception:
275
+ continue
276
+ return count
277
+
278
+ def active_worker_ids(self, ttl_seconds: int, formation: str | None = None) -> list[str]:
279
+ cutoff = time.time() - ttl_seconds
280
+ worker_ids: list[str] = []
281
+ pulse_dir = self.worker_pulses_dir()
282
+ for path in pulse_dir.glob("*.json") if pulse_dir.exists() else []:
283
+ try:
284
+ if path.stat().st_mtime < cutoff:
285
+ continue
286
+ data = json.loads(path.read_text(encoding="utf-8"))
287
+ if formation is not None and data.get("formation", "cpu-worker") != formation:
288
+ continue
289
+ worker_id = data.get("worker_id")
290
+ if isinstance(worker_id, str):
291
+ worker_ids.append(worker_id)
292
+ except Exception:
293
+ continue
294
+ return sorted(worker_ids)
295
+
296
+ def write_regulation(self, intent: RegulationIntent) -> Path:
297
+ self._write_json_atomic(self.regulation_path(), intent.to_dict())
298
+ self.append_event("farm_regulation_changed", intent.to_dict())
299
+ return self.regulation_path()
300
+
301
+ def write_control(self, control: RegulationIntent) -> Path:
302
+ return self.write_regulation(control)
303
+
304
+ def read_regulation(self) -> RegulationIntent:
305
+ path = self.regulation_path()
306
+ if not path.exists():
307
+ return RegulationIntent()
308
+ try:
309
+ return RegulationIntent.from_dict(json.loads(path.read_text(encoding="utf-8")))
310
+ except Exception:
311
+ return RegulationIntent(mode="drain", reason="invalid_regulation_state")
312
+
313
+ def read_control(self) -> RegulationIntent:
314
+ return self.read_regulation()
315
+
316
+ def write_expansion_request(self, request: ExpansionRequest) -> Path:
317
+ path = self.expansion_dir() / f"{request.request_id}.json"
318
+ self._write_json_atomic(path, request.to_dict())
319
+ self.append_event("farm_expansion_requested", request.to_dict())
320
+ return path
321
+
322
+ def requeue_expired_claims(self, policy: FarmPolicy) -> int:
323
+ cutoff = time.time() - policy.claim_ttl_seconds
324
+ requeued = 0
325
+ for path in self.claimed_jobs():
326
+ try:
327
+ if path.stat().st_mtime > cutoff:
328
+ continue
329
+ data = json.loads(path.read_text(encoding="utf-8"))
330
+ job = ShardJob.from_dict(data)
331
+ pending = self.jobs_dir(job.run_id, job.generation, "pending") / path.name
332
+ pending.parent.mkdir(parents=True, exist_ok=True)
333
+ path.rename(pending)
334
+ requeued += 1
335
+ except Exception:
336
+ continue
337
+ if requeued:
338
+ self.append_event("farm_claims_requeued", {"count": requeued})
339
+ return requeued
key_farm/symbiotic_trace.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import time
5
+ from collections import Counter, defaultdict
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from .amalgam_bridge import observe_event
10
+ from .contracts import FarmPolicy, ShardJob, ShardResult
11
+ from .store import FarmStore
12
+
13
+
14
+ JOB_STATES = ("pending", "claimed", "done", "failed", "refracted")
15
+
16
+
17
+ def _read_json(path: Path) -> dict[str, Any] | None:
18
+ try:
19
+ return json.loads(path.read_text(encoding="utf-8"))
20
+ except Exception:
21
+ return None
22
+
23
+
24
+ def _event_to_dict(event: Any) -> dict[str, Any] | None:
25
+ if event is None:
26
+ return None
27
+ if hasattr(event, "to_dict"):
28
+ try:
29
+ return event.to_dict()
30
+ except Exception:
31
+ pass
32
+ if isinstance(event, dict):
33
+ return event
34
+ return {"repr": repr(event)}
35
+
36
+
37
+ def _interpret_with_cascade(signal: dict[str, Any]) -> dict[str, Any]:
38
+ result: dict[str, Any] = {"available": False}
39
+ try:
40
+ from cascade import Monitor, SymbioticAdapter
41
+
42
+ interpreted = SymbioticAdapter().interpret(signal)
43
+ monitor_event = Monitor("hf_cpu_farm").observe(signal)
44
+ result.update(
45
+ {
46
+ "available": True,
47
+ "symbiotic_event": _event_to_dict(interpreted),
48
+ "monitor_event": _event_to_dict(monitor_event),
49
+ }
50
+ )
51
+ except Exception as exc:
52
+ result["error"] = f"{type(exc).__name__}: {exc}"
53
+ return result
54
+
55
+
56
+ def _job_counts(store: FarmStore) -> dict[str, Any]:
57
+ counts: dict[str, int] = {}
58
+ by_formation: dict[str, Counter] = defaultdict(Counter)
59
+ by_task: dict[str, Counter] = defaultdict(Counter)
60
+ by_generation: dict[str, Counter] = defaultdict(Counter)
61
+ sample_failures = []
62
+
63
+ for state in JOB_STATES:
64
+ paths = sorted((store.root / "runs").glob(f"*/generations/*/jobs/{state}/*.json"))
65
+ counts[state] = len(paths)
66
+ for path in paths:
67
+ data = _read_json(path) or {}
68
+ try:
69
+ job = ShardJob.from_dict(data)
70
+ formation = job.formation
71
+ task = f"{job.task_type}:{job.task_name}"
72
+ generation = f"{job.run_id}:{job.generation}"
73
+ except Exception:
74
+ formation = str(data.get("formation", "unknown"))
75
+ task = str(data.get("task_type", "unknown"))
76
+ generation = "unknown"
77
+ by_formation[formation][state] += 1
78
+ by_task[task][state] += 1
79
+ by_generation[generation][state] += 1
80
+ if state == "failed" and len(sample_failures) < 8:
81
+ sample_failures.append(
82
+ {
83
+ "path": str(path),
84
+ "task": task,
85
+ "message": str(data.get("message") or data.get("error") or "")[:500],
86
+ }
87
+ )
88
+
89
+ return {
90
+ "counts": counts,
91
+ "by_formation": {key: dict(value) for key, value in by_formation.items()},
92
+ "by_task": {key: dict(value) for key, value in by_task.items()},
93
+ "by_generation": {key: dict(value) for key, value in by_generation.items()},
94
+ "sample_failures": sample_failures,
95
+ }
96
+
97
+
98
+ def _result_stats(store: FarmStore) -> dict[str, Any]:
99
+ durations = []
100
+ errored = 0
101
+ total = 0
102
+ by_worker: Counter = Counter()
103
+
104
+ for path in sorted((store.root / "runs").glob("*/generations/*/results/*.json")):
105
+ data = _read_json(path)
106
+ if not data:
107
+ continue
108
+ try:
109
+ result = ShardResult.from_dict(data)
110
+ except Exception:
111
+ continue
112
+ by_worker[result.worker_id] += 1
113
+ for node_result in result.node_results:
114
+ total += 1
115
+ durations.append(float(node_result.duration_ms))
116
+ if node_result.error:
117
+ errored += 1
118
+
119
+ durations.sort()
120
+ p95 = durations[int(len(durations) * 0.95) - 1] if durations else 0.0
121
+ return {
122
+ "result_files": sum(by_worker.values()),
123
+ "node_results": total,
124
+ "node_errors": errored,
125
+ "avg_node_ms": (sum(durations) / len(durations)) if durations else 0.0,
126
+ "p95_node_ms": p95,
127
+ "by_worker": dict(by_worker),
128
+ }
129
+
130
+
131
+ def _pulse_stats(store: FarmStore, policy: FarmPolicy) -> dict[str, Any]:
132
+ now = time.time()
133
+ workers = []
134
+ active = 0
135
+ stale = 0
136
+ pulse_dir = store.worker_pulses_dir()
137
+ for path in sorted(pulse_dir.glob("*.json")) if pulse_dir.exists() else []:
138
+ data = _read_json(path) or {}
139
+ age = max(0.0, now - path.stat().st_mtime)
140
+ is_active = age <= policy.pulse_ttl_seconds
141
+ active += int(is_active)
142
+ stale += int(not is_active)
143
+ workers.append(
144
+ {
145
+ "worker_id": data.get("worker_id") or path.stem,
146
+ "formation": data.get("formation", "cpu-worker"),
147
+ "status": data.get("status", "unknown"),
148
+ "age_seconds": round(age, 3),
149
+ "active": is_active,
150
+ }
151
+ )
152
+
153
+ controller_path = store.controller_pulse_path()
154
+ controller_age = None
155
+ if controller_path.exists():
156
+ controller_age = max(0.0, now - controller_path.stat().st_mtime)
157
+
158
+ return {
159
+ "controller_alive": store.controller_pulse_alive(policy.controller_pulse_ttl_seconds),
160
+ "controller_age_seconds": None if controller_age is None else round(controller_age, 3),
161
+ "active_workers": active,
162
+ "stale_workers": stale,
163
+ "workers": workers,
164
+ }
165
+
166
+
167
+ def _expansion_stats(store: FarmStore) -> dict[str, Any]:
168
+ requests = []
169
+ for path in sorted(store.expansion_dir().glob("*.json")) if store.expansion_dir().exists() else []:
170
+ data = _read_json(path)
171
+ if data:
172
+ requests.append(data)
173
+ return {
174
+ "count": len(requests),
175
+ "latest": requests[-8:],
176
+ }
177
+
178
+
179
+ def _recommendations(signal: dict[str, Any], policy: FarmPolicy) -> list[str]:
180
+ traffic = signal["traffic"]["counts"]
181
+ pulse = signal["pulse"]
182
+ results = signal["results"]
183
+ recommendations = []
184
+
185
+ if not pulse["controller_alive"]:
186
+ recommendations.append("Publish a controller pulse or let workers exit by pulse-loss policy.")
187
+ if traffic.get("failed", 0):
188
+ recommendations.append("Inspect failed shard samples before adding more workers.")
189
+ if traffic.get("pending", 0) > max(1, pulse["active_workers"] * 3):
190
+ recommendations.append("Backlog exceeds active CPU pulse capacity; request expansion within budget.")
191
+ if pulse["active_workers"] > policy.effective_max_workers():
192
+ recommendations.append("Active workers exceed budget-derived ceiling; publish drain intent.")
193
+ if results["node_errors"]:
194
+ recommendations.append("Node-level errors are present; trace handler payload compatibility.")
195
+ if not recommendations:
196
+ recommendations.append("Farm traffic is balanced under current pulse and budget policy.")
197
+ return recommendations
198
+
199
+
200
+ def symbiotic_trace(
201
+ store: FarmStore,
202
+ policy: FarmPolicy | None = None,
203
+ *,
204
+ receipt: bool = True,
205
+ ) -> dict[str, Any]:
206
+ policy = policy or FarmPolicy()
207
+ signal = {
208
+ "surface": "hf_cpu_farm",
209
+ "policy": policy.to_dict(),
210
+ "regulation": store.read_regulation().to_dict(),
211
+ "pulse": _pulse_stats(store, policy),
212
+ "traffic": _job_counts(store),
213
+ "results": _result_stats(store),
214
+ "expansion": _expansion_stats(store),
215
+ }
216
+ signal["recommendations"] = _recommendations(signal, policy)
217
+ cascade = _interpret_with_cascade(signal)
218
+ report = {
219
+ "summary": {
220
+ "pending": signal["traffic"]["counts"].get("pending", 0),
221
+ "claimed": signal["traffic"]["counts"].get("claimed", 0),
222
+ "failed": signal["traffic"]["counts"].get("failed", 0),
223
+ "active_workers": signal["pulse"]["active_workers"],
224
+ "controller_alive": signal["pulse"]["controller_alive"],
225
+ },
226
+ "signal": signal,
227
+ "cascade": cascade,
228
+ }
229
+ if receipt:
230
+ report["receipt"] = observe_event("hf-cpu-farm", "symbiotic_trace", report["summary"])
231
+ return report
key_farm/worker.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import os
5
+ import time
6
+
7
+ from .contracts import (
8
+ ExpansionRequest,
9
+ FarmPolicy,
10
+ RegulationIntent,
11
+ ShardResult,
12
+ WorkerPulse,
13
+ utc_now,
14
+ )
15
+ from .evaluator import evaluate_job
16
+ from .regulation import create_pulse, tick_pulse
17
+ from .store import FarmStore
18
+
19
+
20
+ def maybe_request_expansion(
21
+ store: FarmStore,
22
+ job_run_id: str,
23
+ generation: int,
24
+ policy: FarmPolicy,
25
+ reason: str,
26
+ formation: str,
27
+ ) -> None:
28
+ pending = len(store.pending_jobs(formation))
29
+ active = store.active_worker_count(policy.pulse_ttl_seconds, formation)
30
+ allowed = policy.effective_max_workers()
31
+ if active >= allowed:
32
+ return
33
+ if pending <= max(1, active * 2):
34
+ return
35
+
36
+ requested = min(allowed - active, max(1, pending // 4))
37
+ request = ExpansionRequest(
38
+ run_id=job_run_id,
39
+ generation=generation,
40
+ reason=reason,
41
+ requested_workers=requested,
42
+ active_workers=active,
43
+ pending_jobs=pending,
44
+ policy=policy.to_dict(),
45
+ formation=formation,
46
+ )
47
+ store.write_expansion_request(request)
48
+
49
+
50
+ def primary_formation(policy: FarmPolicy) -> str:
51
+ formations = tuple(policy.allowed_formations or ("cpu-worker",))
52
+ return formations[0] if formations else "cpu-worker"
53
+
54
+
55
+ def worker_tick(
56
+ store: FarmStore,
57
+ worker_id: str,
58
+ policy: FarmPolicy,
59
+ pulse_state: dict[str, float],
60
+ ) -> bool:
61
+ formation = primary_formation(policy)
62
+ claimed = store.claim_next(worker_id, tuple(policy.allowed_formations))
63
+ if claimed is None:
64
+ store.write_worker_pulse(
65
+ WorkerPulse(worker_id, None, None, "idle", formation=formation, pulse=pulse_state)
66
+ )
67
+ return False
68
+
69
+ job, claimed_path = claimed
70
+ store.write_worker_pulse(
71
+ WorkerPulse(
72
+ worker_id,
73
+ job.run_id,
74
+ job.generation,
75
+ "claimed",
76
+ job.shard_id,
77
+ formation=formation,
78
+ pulse=pulse_state,
79
+ )
80
+ )
81
+
82
+ children = job.split(policy)
83
+ if len(children) > 1:
84
+ store.put_jobs(children)
85
+ result = ShardResult(
86
+ run_id=job.run_id,
87
+ generation=job.generation,
88
+ shard_id=job.shard_id,
89
+ worker_id=worker_id,
90
+ status="refracted",
91
+ node_results=[],
92
+ started_at=utc_now(),
93
+ message=f"split into {len(children)} child shards",
94
+ )
95
+ store.complete_job(job, result, claimed_path)
96
+ maybe_request_expansion(
97
+ store, job.run_id, job.generation, policy, "job_refraction", job.formation
98
+ )
99
+ return True
100
+
101
+ result = evaluate_job(job, worker_id)
102
+ store.complete_job(job, result, claimed_path)
103
+ maybe_request_expansion(store, job.run_id, job.generation, policy, "pending_backlog", job.formation)
104
+ return True
105
+
106
+
107
+ def _drain_applies_to_worker(
108
+ store: FarmStore,
109
+ worker_id: str,
110
+ control: RegulationIntent,
111
+ policy: FarmPolicy,
112
+ worker_formation: str,
113
+ ) -> bool:
114
+ if control.target_workers is None:
115
+ return False
116
+ if control.target_workers <= 0:
117
+ return True
118
+
119
+ active = store.active_worker_ids(policy.pulse_ttl_seconds, worker_formation)
120
+ if len(active) <= control.target_workers:
121
+ return False
122
+
123
+ keepers = set(active[: control.target_workers])
124
+ return worker_id not in keepers
125
+
126
+
127
+ def worker_exit_reason(store: FarmStore, worker_id: str, policy: FarmPolicy) -> str | None:
128
+ control = store.read_regulation()
129
+ worker_formation = primary_formation(policy)
130
+ if control.formation is not None and control.formation != worker_formation:
131
+ return None
132
+
133
+ if control.mode == "shutdown":
134
+ return f"shutdown:{control.reason}"
135
+
136
+ if control.mode == "drain" and _drain_applies_to_worker(
137
+ store, worker_id, control, policy, worker_formation
138
+ ):
139
+ return f"drain:{control.reason}"
140
+
141
+ if policy.exit_when_pulse_lost and not store.controller_pulse_alive(
142
+ policy.controller_pulse_ttl_seconds
143
+ ):
144
+ return "controller_pulse_lost"
145
+
146
+ return None
147
+
148
+
149
+ def worker_loop(
150
+ store: FarmStore,
151
+ worker_id: str,
152
+ policy: FarmPolicy,
153
+ *,
154
+ poll_seconds: float = 2.0,
155
+ once: bool = False,
156
+ ) -> None:
157
+ pulse = create_pulse(policy.pulse_rate)
158
+ formation = primary_formation(policy)
159
+ while True:
160
+ pulse_state = tick_pulse(pulse)
161
+ reason = worker_exit_reason(store, worker_id, policy)
162
+ if reason is not None:
163
+ store.write_worker_pulse(
164
+ WorkerPulse(
165
+ worker_id,
166
+ None,
167
+ None,
168
+ f"exiting:{reason}",
169
+ formation=formation,
170
+ pulse=pulse_state,
171
+ )
172
+ )
173
+ store.append_event("farm_worker_exiting", {"worker_id": worker_id, "reason": reason})
174
+ return
175
+ store.requeue_expired_claims(policy)
176
+ did_work = worker_tick(store, worker_id, policy, pulse_state)
177
+ if once:
178
+ return
179
+ if not did_work:
180
+ time.sleep(poll_seconds)
181
+
182
+
183
+ def main() -> None:
184
+ parser = argparse.ArgumentParser(description="KEY CPU farm worker")
185
+ parser.add_argument("--root", default=os.environ.get("KEY_FARM_ROOT", "/data/farm"))
186
+ parser.add_argument("--worker-id", default=os.environ.get("KEY_FARM_WORKER_ID"))
187
+ parser.add_argument("--once", action="store_true")
188
+ parser.add_argument("--poll-seconds", type=float, default=2.0)
189
+ parser.add_argument("--target-batch-size", type=int, default=None)
190
+ parser.add_argument("--max-workers", type=int, default=None)
191
+ parser.add_argument("--budget-hourly-usd", type=float, default=None)
192
+ parser.add_argument("--formation", default=os.environ.get("KEY_FARM_FORMATION", "cpu-worker"))
193
+ args = parser.parse_args()
194
+
195
+ worker_id = args.worker_id or f"worker_{os.getpid()}"
196
+ policy_kwargs = {}
197
+ if args.target_batch_size is not None:
198
+ policy_kwargs["target_batch_size"] = args.target_batch_size
199
+ if args.max_workers is not None:
200
+ policy_kwargs["max_workers"] = args.max_workers
201
+ if args.budget_hourly_usd is not None:
202
+ policy_kwargs["budget_hourly_usd"] = args.budget_hourly_usd
203
+ policy_kwargs["allowed_formations"] = (args.formation,)
204
+
205
+ worker_loop(
206
+ FarmStore(args.root),
207
+ worker_id,
208
+ FarmPolicy(**policy_kwargs),
209
+ poll_seconds=args.poll_seconds,
210
+ once=args.once,
211
+ )
212
+
213
+
214
+ if __name__ == "__main__":
215
+ main()
meshscale/__init__.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ """MeshScale public package.
2
+
3
+ MeshScale is the CPU worker mesh built from the KEY farm primitives.
4
+ """
5
+
6
+ from key_farm import * # noqa: F401,F403
meshscale/cli.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from key_farm.cli import main
4
+
5
+
6
+ if __name__ == "__main__":
7
+ main()
meshscale_space/Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ ENV PYTHONUNBUFFERED=1
4
+ ENV PORT=7860
5
+ ENV KEY_FARM_ROOT=/data/farm
6
+ ENV MESHSCALE_ENABLE_APPLY=0
7
+ ENV MESHSCALE_FULL_KEY_REQUIREMENTS=0
8
+
9
+ WORKDIR /app
10
+ COPY . /app
11
+
12
+ RUN python -m pip install --upgrade pip \
13
+ && python -m pip install --no-cache-dir -r meshscale_space/requirements.txt \
14
+ && if [ "$MESHSCALE_FULL_KEY_REQUIREMENTS" = "1" ] && [ -f requirements.txt ]; then \
15
+ python -m pip install --no-cache-dir -r requirements.txt; \
16
+ fi
17
+
18
+ CMD ["python", "-m", "uvicorn", "meshscale_space.app:app", "--host", "0.0.0.0", "--port", "7860"]
meshscale_space/README.md ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: MeshScale
3
+ emoji: M
4
+ colorFrom: gray
5
+ colorTo: yellow
6
+ sdk: docker
7
+ app_port: 7860
8
+ pinned: false
9
+ ---
10
+
11
+ # MeshScale
12
+
13
+ Bare-metal search surface for the MeshScale CPU worker mesh.
14
+
15
+ Required Space variable:
16
+
17
+ ```text
18
+ KEY_FARM_ROOT=/data/farm
19
+ ```
20
+
21
+ Safe default:
22
+
23
+ ```text
24
+ MESHSCALE_ENABLE_APPLY=0
25
+ ```
26
+
27
+ Set `MESHSCALE_ENABLE_APPLY=1` only on the controller Space that is allowed to
28
+ start or pause Hugging Face CPU worker Spaces.
29
+
30
+ Common variables:
31
+
32
+ ```text
33
+ MESHSCALE_TEMPLATE_SPACE=tostido/meshscale-worker-template
34
+ MESHSCALE_NAMESPACE=tostido
35
+ MESHSCALE_WORKER_PREFIX=meshscale-worker
36
+ MESHSCALE_HARDWARE=cpu-upgrade
37
+ MESHSCALE_MAX_WORKERS=24
38
+ MESHSCALE_BUDGET_HOURLY_USD=0.72
39
+ MESHSCALE_KEY_SPACE_URL=https://huggingface.co/spaces/tostido/Ouroboros
40
+ MESHSCALE_BUCKET_URL=https://huggingface.co/buckets/tostido/Ouroboros-storage
41
+ MESHSCALE_WORKER_TEMPLATE_URL=https://huggingface.co/spaces/tostido/meshscale-worker-template
42
+ ```
43
+
44
+ The UI accepts search commands:
45
+
46
+ ```text
47
+ guide
48
+ status
49
+ trace
50
+ plan 420
51
+ scale dry
52
+ scale apply
53
+ run
54
+ drain 4
55
+ shutdown
56
+ ```
57
+
58
+ The OS route layer returns contextual next steps before acting. For example,
59
+ `scale apply` stops early when the controller pulse is quiet and routes the user
60
+ to KEY instead of blindly scaling workers.
meshscale_space/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ """MeshScale controller Space."""
meshscale_space/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (194 Bytes). View file
 
meshscale_space/__pycache__/app.cpython-310.pyc ADDED
Binary file (6.85 kB). View file
 
meshscale_space/__pycache__/os_routes.cpython-310.pyc ADDED
Binary file (5.46 kB). View file
 
meshscale_space/app.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ from fastapi import FastAPI, Request
8
+ from fastapi.responses import FileResponse
9
+ from fastapi.staticfiles import StaticFiles
10
+
11
+ from key_farm.contracts import FarmPolicy, RegulationIntent
12
+ from key_farm.scaler import MeshScaleScaler, ScalePolicy
13
+ from key_farm.store import FarmStore
14
+ from key_farm.symbiotic_trace import symbiotic_trace
15
+ from .os_routes import default_links, route_query
16
+
17
+
18
+ APP_DIR = Path(__file__).resolve().parent
19
+ STATIC_DIR = APP_DIR / "static"
20
+
21
+ app = FastAPI(title="MeshScale Bare Metal")
22
+ app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
23
+
24
+
25
+ def _int_env(name: str, default: int) -> int:
26
+ try:
27
+ return int(os.environ.get(name, str(default)))
28
+ except ValueError:
29
+ return default
30
+
31
+
32
+ def _float_env(name: str, default: float) -> float:
33
+ try:
34
+ return float(os.environ.get(name, str(default)))
35
+ except ValueError:
36
+ return default
37
+
38
+
39
+ def _bool_env(name: str, default: bool = False) -> bool:
40
+ raw = os.environ.get(name)
41
+ if raw is None:
42
+ return default
43
+ return raw.strip().lower() in {"1", "true", "yes", "on"}
44
+
45
+
46
+ def _farm_root() -> str:
47
+ return os.environ.get("KEY_FARM_ROOT", "/data/farm")
48
+
49
+
50
+ def _store() -> FarmStore:
51
+ return FarmStore(_farm_root())
52
+
53
+
54
+ def _links() -> dict[str, str]:
55
+ links = default_links()
56
+ overrides = {
57
+ "key_space": os.environ.get("MESHSCALE_KEY_SPACE_URL"),
58
+ "bucket": os.environ.get("MESHSCALE_BUCKET_URL"),
59
+ "worker_template": os.environ.get("MESHSCALE_WORKER_TEMPLATE_URL"),
60
+ "storage_docs": os.environ.get("MESHSCALE_STORAGE_DOCS_URL"),
61
+ "space_docs": os.environ.get("MESHSCALE_SPACE_DOCS_URL"),
62
+ }
63
+ return {key: value or links[key] for key, value in overrides.items()}
64
+
65
+
66
+ def _farm_policy(payload: dict[str, Any] | None = None) -> FarmPolicy:
67
+ payload = payload or {}
68
+ return FarmPolicy(
69
+ target_batch_size=int(payload.get("target_batch_size") or _int_env("KEY_FARM_TARGET_BATCH_SIZE", 24)),
70
+ max_workers=int(payload.get("max_workers") or _int_env("KEY_FARM_MAX_WORKERS", 24)),
71
+ budget_hourly_usd=float(
72
+ payload.get("budget_hourly_usd") or _float_env("KEY_FARM_BUDGET_HOURLY_USD", 0.72)
73
+ ),
74
+ cpu_space_hourly_usd=float(
75
+ payload.get("cpu_space_hourly_usd") or _float_env("KEY_FARM_CPU_SPACE_HOURLY_USD", 0.03)
76
+ ),
77
+ )
78
+
79
+
80
+ def _scale_policy(payload: dict[str, Any]) -> ScalePolicy:
81
+ apply_requested = bool(payload.get("apply", False))
82
+ apply_allowed = _bool_env("MESHSCALE_ENABLE_APPLY", False)
83
+ return ScalePolicy(
84
+ template_space=str(
85
+ payload.get("template_space")
86
+ or os.environ.get("MESHSCALE_TEMPLATE_SPACE", "tostido/meshscale-worker-template")
87
+ ),
88
+ namespace=str(payload.get("namespace") or os.environ.get("MESHSCALE_NAMESPACE", "tostido")),
89
+ worker_prefix=str(
90
+ payload.get("worker_prefix") or os.environ.get("MESHSCALE_WORKER_PREFIX", "meshscale-worker")
91
+ ),
92
+ formation=str(payload.get("formation") or os.environ.get("KEY_FARM_FORMATION", "cpu-worker")),
93
+ hardware=str(payload.get("hardware") or os.environ.get("MESHSCALE_HARDWARE", "cpu-upgrade")),
94
+ max_workers=int(payload.get("max_workers") or _int_env("MESHSCALE_MAX_WORKERS", 24)),
95
+ max_starts_per_tick=int(
96
+ payload.get("max_starts_per_tick") or _int_env("MESHSCALE_MAX_STARTS_PER_TICK", 2)
97
+ ),
98
+ start_cooldown_seconds=int(
99
+ payload.get("start_cooldown_seconds") or _int_env("MESHSCALE_START_COOLDOWN_SECONDS", 120)
100
+ ),
101
+ budget_hourly_usd=float(
102
+ payload.get("budget_hourly_usd") or _float_env("MESHSCALE_BUDGET_HOURLY_USD", 0.72)
103
+ ),
104
+ cpu_space_hourly_usd=float(
105
+ payload.get("cpu_space_hourly_usd") or _float_env("MESHSCALE_CPU_SPACE_HOURLY_USD", 0.03)
106
+ ),
107
+ dry_run=not (apply_requested and apply_allowed),
108
+ private=not bool(payload.get("public", False)),
109
+ )
110
+
111
+
112
+ @app.get("/")
113
+ def index() -> FileResponse:
114
+ return FileResponse(STATIC_DIR / "index.html")
115
+
116
+
117
+ @app.get("/api/health")
118
+ def health() -> dict[str, Any]:
119
+ root = _farm_root()
120
+ return {
121
+ "ok": True,
122
+ "farm_root": root,
123
+ "apply_enabled": _bool_env("MESHSCALE_ENABLE_APPLY", False),
124
+ "links": _links(),
125
+ }
126
+
127
+
128
+ @app.get("/api/status")
129
+ def status() -> dict[str, Any]:
130
+ report = symbiotic_trace(_store(), _farm_policy(), receipt=False)
131
+ report["farm_root"] = _farm_root()
132
+ report["apply_enabled"] = _bool_env("MESHSCALE_ENABLE_APPLY", False)
133
+ report["links"] = _links()
134
+ return report
135
+
136
+
137
+ @app.post("/api/route")
138
+ async def route(request: Request) -> dict[str, Any]:
139
+ payload = await request.json()
140
+ query = str(payload.get("query") or "")
141
+ report = symbiotic_trace(_store(), _farm_policy(), receipt=False)
142
+ report["farm_root"] = _farm_root()
143
+ report["apply_enabled"] = _bool_env("MESHSCALE_ENABLE_APPLY", False)
144
+ report["links"] = _links()
145
+ return {
146
+ "query": query,
147
+ "route": route_query(query, report, _links()),
148
+ "summary": report.get("summary", {}),
149
+ }
150
+
151
+
152
+ @app.post("/api/plan")
153
+ async def plan(request: Request) -> dict[str, Any]:
154
+ payload = await request.json()
155
+ pending = int(payload.get("pending_jobs") or 0)
156
+ active = int(payload.get("active_workers") or 0)
157
+ jobs_per_worker = max(1, int(payload.get("jobs_per_worker") or 2))
158
+ policy = _farm_policy(payload)
159
+ needed = max(1, (pending + jobs_per_worker - 1) // jobs_per_worker) if pending else 0
160
+ requested = max(0, min(policy.effective_max_workers(), needed) - active)
161
+ return {
162
+ "pending_jobs": pending,
163
+ "active_workers": active,
164
+ "jobs_per_worker": jobs_per_worker,
165
+ "requested_workers": requested,
166
+ "effective_max_workers": policy.effective_max_workers(),
167
+ "hourly_budget_usd": policy.budget_hourly_usd,
168
+ "cpu_space_hourly_usd": policy.cpu_space_hourly_usd,
169
+ }
170
+
171
+
172
+ @app.post("/api/regulate")
173
+ async def regulate(request: Request) -> dict[str, Any]:
174
+ payload = await request.json()
175
+ mode = str(payload.get("mode") or "run")
176
+ if mode not in {"run", "drain", "shutdown"}:
177
+ return {"ok": False, "error": f"invalid regulation mode: {mode}"}
178
+ target_workers = payload.get("target_workers")
179
+ if target_workers in ("", None):
180
+ target_workers = None
181
+ elif target_workers is not None:
182
+ target_workers = int(target_workers)
183
+ intent = RegulationIntent(
184
+ mode=mode,
185
+ reason=str(payload.get("reason") or "meshscale_operator"),
186
+ target_workers=target_workers,
187
+ formation=str(payload.get("formation") or "") or None,
188
+ issued_by="meshscale-space",
189
+ )
190
+ _store().write_regulation(intent)
191
+ return {"ok": True, "intent": intent.to_dict()}
192
+
193
+
194
+ @app.post("/api/scale-once")
195
+ async def scale_once(request: Request) -> dict[str, Any]:
196
+ payload = await request.json()
197
+ policy = _scale_policy(payload)
198
+ report = MeshScaleScaler(_store(), policy).tick()
199
+ report["apply_requested"] = bool(payload.get("apply", False))
200
+ report["apply_enabled"] = _bool_env("MESHSCALE_ENABLE_APPLY", False)
201
+ return report
meshscale_space/os_routes.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ def _action(kind: str, label: str, target: str, description: str) -> dict[str, str]:
7
+ return {
8
+ "kind": kind,
9
+ "label": label,
10
+ "target": target,
11
+ "description": description,
12
+ }
13
+
14
+
15
+ def default_links() -> dict[str, str]:
16
+ return {
17
+ "key_space": "https://huggingface.co/spaces/tostido/Ouroboros",
18
+ "bucket": "https://huggingface.co/buckets/tostido/Ouroboros-storage",
19
+ "worker_template": "https://huggingface.co/spaces/tostido/meshscale-worker-template",
20
+ "storage_docs": "https://huggingface.co/docs/hub/storage-buckets",
21
+ "space_docs": "https://huggingface.co/docs/huggingface_hub/guides/manage-spaces",
22
+ }
23
+
24
+
25
+ def route_query(query: str, status: dict[str, Any], links: dict[str, str] | None = None) -> dict[str, Any]:
26
+ """Return a human OS route for a MeshScale search query."""
27
+
28
+ links = {**default_links(), **(links or {})}
29
+ q = query.strip().lower()
30
+ summary = status.get("summary", {})
31
+ signal = status.get("signal", {})
32
+ pulse = signal.get("pulse", {})
33
+ traffic = signal.get("traffic", {}).get("counts", {})
34
+ regulation = signal.get("regulation", {})
35
+
36
+ controller_alive = bool(summary.get("controller_alive"))
37
+ pending = int(summary.get("pending") or traffic.get("pending") or 0)
38
+ failed = int(summary.get("failed") or traffic.get("failed") or 0)
39
+ workers = int(summary.get("active_workers") or pulse.get("active_workers") or 0)
40
+ mode = regulation.get("mode", "run")
41
+
42
+ if not q:
43
+ q = "status"
44
+
45
+ if any(token in q for token in ("help", "guide", "where", "what", "?")):
46
+ return {
47
+ "title": "OS guide",
48
+ "message": "Start with status, then follow the next route that matches the pulse and backlog state.",
49
+ "severity": "info",
50
+ "actions": [
51
+ _action("command", "Check status", "status", "Read pulse, workers, backlog, and errors."),
52
+ _action("command", "Plan capacity", "plan 420", "Estimate CPU worker count before scaling."),
53
+ _action("link", "Open KEY Space", links["key_space"], "Start or inspect the controller run."),
54
+ _action("link", "Open bucket", links["bucket"], "Inspect farm files and run artifacts."),
55
+ ],
56
+ }
57
+
58
+ if any(token in q for token in ("scale", "worker", "expand", "farm")):
59
+ if not controller_alive:
60
+ return {
61
+ "title": "Not ready to scale",
62
+ "message": "The controller pulse is quiet. MeshScale will not start workers until KEY is alive.",
63
+ "severity": "warn",
64
+ "actions": [
65
+ _action("link", "Open KEY Space", links["key_space"], "Start or refresh the KEY controller."),
66
+ _action("command", "Check status", "status", "Confirm the controller pulse is live."),
67
+ _action("command", "Dry-run scale", "scale dry", "See the guarded scaler decision."),
68
+ ],
69
+ }
70
+ if pending <= 0:
71
+ return {
72
+ "title": "No backlog yet",
73
+ "message": "Scaling is premature because there are no pending shard jobs in the bucket.",
74
+ "severity": "info",
75
+ "actions": [
76
+ _action("link", "Open bucket", links["bucket"], "Look for pending generation shards."),
77
+ _action("command", "Check trace", "trace", "Inspect traffic and expansion requests."),
78
+ _action("command", "Plan 420", "plan 420", "Model capacity before the run arrives."),
79
+ ],
80
+ }
81
+ return {
82
+ "title": "Scale path ready",
83
+ "message": f"{pending} pending shards are visible with {workers} active workers.",
84
+ "severity": "ok",
85
+ "actions": [
86
+ _action("command", "Plan backlog", f"plan {pending}", "Estimate workers from current backlog."),
87
+ _action("command", "Dry-run scale", "scale dry", "Preview worker starts under policy caps."),
88
+ _action("link", "Worker template", links["worker_template"], "Inspect the CPU worker image."),
89
+ ],
90
+ }
91
+
92
+ if any(token in q for token in ("start", "run", "pulse", "controller")):
93
+ if controller_alive and mode == "run":
94
+ return {
95
+ "title": "Controller is live",
96
+ "message": "The pulse is active and regulation is already in run mode.",
97
+ "severity": "ok",
98
+ "actions": [
99
+ _action("command", "Trace traffic", "trace", "Inspect current farm movement."),
100
+ _action("command", "Plan capacity", f"plan {max(pending, 120)}", "Size the next worker move."),
101
+ ],
102
+ }
103
+ return {
104
+ "title": "Go to controller",
105
+ "message": "You are not at the run step yet. Start the KEY Space or publish run regulation.",
106
+ "severity": "warn",
107
+ "actions": [
108
+ _action("link", "Open KEY Space", links["key_space"], "Start the KEY population run."),
109
+ _action("command", "Set run mode", "run", "Publish MeshScale run regulation."),
110
+ _action("command", "Check status", "status", "Verify the pulse after starting."),
111
+ ],
112
+ }
113
+
114
+ if any(token in q for token in ("error", "fail", "broken", "logs", "debug")):
115
+ if failed > 0:
116
+ return {
117
+ "title": "Failures detected",
118
+ "message": f"{failed} failed shard files are visible. Inspect samples before adding workers.",
119
+ "severity": "warn",
120
+ "actions": [
121
+ _action("command", "Trace failures", "trace", "Show failed shard samples."),
122
+ _action("link", "Open bucket", links["bucket"], "Inspect jobs/failed and result payloads."),
123
+ ],
124
+ }
125
+ return {
126
+ "title": "No shard failures",
127
+ "message": "The current farm trace does not report failed shard jobs.",
128
+ "severity": "ok",
129
+ "actions": [
130
+ _action("command", "Trace", "trace", "Inspect detailed traffic."),
131
+ _action("link", "Bucket", links["bucket"], "Open raw farm storage."),
132
+ ],
133
+ }
134
+
135
+ if any(token in q for token in ("bucket", "storage", "files", "jobs")):
136
+ return {
137
+ "title": "Bucket route",
138
+ "message": "MeshScale state lives in the bucket: pulses, regulation, expansion requests, runs, jobs, and results.",
139
+ "severity": "info",
140
+ "actions": [
141
+ _action("link", "Open bucket", links["bucket"], "Inspect farm IO directly."),
142
+ _action("link", "Storage docs", links["storage_docs"], "Review read-write bucket volumes."),
143
+ _action("command", "Trace", "trace", "Summarize bucket state from here."),
144
+ ],
145
+ }
146
+
147
+ return {
148
+ "title": "Route not matched",
149
+ "message": "The OS route did not find a direct match. Start with status or ask for a guide.",
150
+ "severity": "info",
151
+ "actions": [
152
+ _action("command", "Status", "status", "Read the current farm state."),
153
+ _action("command", "Guide", "guide", "Show the operator route map."),
154
+ _action("command", "Trace", "trace", "Inspect traffic, pulse, and recommendations."),
155
+ ],
156
+ }
meshscale_space/requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ fastapi>=0.110
2
+ uvicorn[standard]>=0.27
3
+ huggingface-hub>=0.34.0,<1.0
4
+ numpy>=1.24,<1.27
5
+ simpleeval>=0.9.13
meshscale_space/static/app.js ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const output = document.querySelector("#output");
2
+ const title = document.querySelector("#result-title");
3
+ const mode = document.querySelector("#result-mode");
4
+ const form = document.querySelector("#command-form");
5
+ const input = document.querySelector("#command");
6
+ const strip = document.querySelector("#status-strip");
7
+ const routeTitle = document.querySelector("#route-title");
8
+ const routeMessage = document.querySelector("#route-message");
9
+ const routeActions = document.querySelector("#route-actions");
10
+
11
+ const state = {
12
+ lastStatus: null,
13
+ defaults: {
14
+ template_space: "tostido/meshscale-worker-template",
15
+ namespace: "tostido",
16
+ worker_prefix: "meshscale-worker",
17
+ hardware: "cpu-upgrade",
18
+ formation: "cpu-worker",
19
+ max_workers: 24,
20
+ budget_hourly_usd: 0.72,
21
+ cpu_space_hourly_usd: 0.03,
22
+ max_starts_per_tick: 2,
23
+ },
24
+ };
25
+
26
+ function show(name, data, statusText = "") {
27
+ title.textContent = name;
28
+ mode.textContent = statusText || (data?.apply_enabled ? "apply enabled" : "dry-run guarded");
29
+ output.textContent = JSON.stringify(data, null, 2);
30
+ }
31
+
32
+ function showError(err) {
33
+ show("Error", { error: String(err?.message || err) }, "check logs");
34
+ }
35
+
36
+ async function getJson(url) {
37
+ const res = await fetch(url);
38
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
39
+ return res.json();
40
+ }
41
+
42
+ async function postJson(url, body) {
43
+ const res = await fetch(url, {
44
+ method: "POST",
45
+ headers: { "content-type": "application/json" },
46
+ body: JSON.stringify(body),
47
+ });
48
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
49
+ return res.json();
50
+ }
51
+
52
+ function updateStrip(report) {
53
+ state.lastStatus = report;
54
+ const summary = report.summary || {};
55
+ const signal = report.signal || {};
56
+ const pulse = signal.pulse || {};
57
+ const root = report.farm_root || "unknown";
58
+ strip.innerHTML = "";
59
+ [
60
+ `root: ${root}`,
61
+ `pulse: ${summary.controller_alive ? "live" : "quiet"}`,
62
+ `workers: ${summary.active_workers ?? 0}`,
63
+ `pending: ${summary.pending ?? 0}`,
64
+ ].forEach((item) => {
65
+ const span = document.createElement("span");
66
+ span.textContent = item;
67
+ if (item.includes("quiet")) span.style.color = "#dfbd62";
68
+ if ((pulse.stale_workers || 0) > 0 && item.startsWith("workers")) span.style.color = "#dfbd62";
69
+ strip.appendChild(span);
70
+ });
71
+ }
72
+
73
+ function runCommandFromButton(command) {
74
+ input.value = command;
75
+ return runCommand(command).catch(showError);
76
+ }
77
+
78
+ function renderRoute(route) {
79
+ if (!route) return;
80
+ routeTitle.textContent = route.title || "OS route";
81
+ routeMessage.textContent = route.message || "";
82
+ routeActions.innerHTML = "";
83
+ (route.actions || []).forEach((action) => {
84
+ if (action.kind === "link") {
85
+ const link = document.createElement("a");
86
+ link.href = action.target;
87
+ link.target = "_blank";
88
+ link.rel = "noreferrer";
89
+ link.textContent = action.label;
90
+ link.title = action.description || "";
91
+ routeActions.appendChild(link);
92
+ return;
93
+ }
94
+ const button = document.createElement("button");
95
+ button.type = "button";
96
+ button.textContent = action.label;
97
+ button.dataset.tip = action.description || "";
98
+ button.addEventListener("click", () => runCommandFromButton(action.target));
99
+ routeActions.appendChild(button);
100
+ });
101
+ }
102
+
103
+ async function fetchRoute(query) {
104
+ const routed = await postJson("/api/route", { query });
105
+ renderRoute(routed.route);
106
+ return routed;
107
+ }
108
+
109
+ function numberAfter(parts, fallback) {
110
+ for (const part of parts) {
111
+ const value = Number(part);
112
+ if (Number.isFinite(value)) return value;
113
+ }
114
+ return fallback;
115
+ }
116
+
117
+ async function runCommand(raw) {
118
+ const command = raw.trim().toLowerCase();
119
+ if (!command) return runCommand("status");
120
+ const parts = command.split(/\s+/);
121
+ const head = parts[0];
122
+ const routed = await fetchRoute(command);
123
+
124
+ if (head === "status" || head === "trace" || head === "inspect" || head === "search") {
125
+ const report = await getJson("/api/status");
126
+ updateStrip(report);
127
+ show(head === "trace" ? "Trace" : "Status", report);
128
+ return;
129
+ }
130
+
131
+ if (head === "guide" || head === "help" || head === "where") {
132
+ show("Guide", routed, "next route");
133
+ return;
134
+ }
135
+
136
+ if (head === "plan") {
137
+ const pending = numberAfter(parts.slice(1), state.lastStatus?.summary?.pending || 120);
138
+ const active = state.lastStatus?.summary?.active_workers || 0;
139
+ const data = await postJson("/api/plan", {
140
+ pending_jobs: pending,
141
+ active_workers: active,
142
+ jobs_per_worker: 2,
143
+ max_workers: state.defaults.max_workers,
144
+ budget_hourly_usd: state.defaults.budget_hourly_usd,
145
+ cpu_space_hourly_usd: state.defaults.cpu_space_hourly_usd,
146
+ });
147
+ show("Plan", data, "capacity");
148
+ return;
149
+ }
150
+
151
+ if (head === "scale") {
152
+ const apply = parts.includes("apply");
153
+ if (apply && routed.route?.severity !== "ok") {
154
+ show("Route blocked", routed, "not there yet");
155
+ return;
156
+ }
157
+ const data = await postJson("/api/scale-once", { ...state.defaults, apply });
158
+ show("Scale", data, apply ? "apply requested" : "dry run");
159
+ return;
160
+ }
161
+
162
+ if (head === "run") {
163
+ const data = await postJson("/api/regulate", {
164
+ mode: "run",
165
+ reason: "meshscale_search_run",
166
+ formation: state.defaults.formation,
167
+ });
168
+ show("Regulation", data, "run");
169
+ await refreshStatus();
170
+ return;
171
+ }
172
+
173
+ if (head === "drain") {
174
+ const target = numberAfter(parts.slice(1), 0);
175
+ const data = await postJson("/api/regulate", {
176
+ mode: "drain",
177
+ target_workers: target,
178
+ reason: "meshscale_search_drain",
179
+ formation: state.defaults.formation,
180
+ });
181
+ show("Regulation", data, `drain ${target}`);
182
+ await refreshStatus();
183
+ return;
184
+ }
185
+
186
+ if (head === "shutdown" || head === "stop") {
187
+ const data = await postJson("/api/regulate", {
188
+ mode: "shutdown",
189
+ reason: "meshscale_search_shutdown",
190
+ formation: state.defaults.formation,
191
+ });
192
+ show("Regulation", data, "shutdown");
193
+ await refreshStatus();
194
+ return;
195
+ }
196
+
197
+ const report = await getJson("/api/status");
198
+ updateStrip(report);
199
+ show("Search", {
200
+ query: raw,
201
+ matched: false,
202
+ commands: ["status", "trace", "plan 420", "scale dry", "scale apply", "run", "drain 4", "shutdown"],
203
+ status: report.summary,
204
+ });
205
+ }
206
+
207
+ async function refreshStatus() {
208
+ try {
209
+ const report = await getJson("/api/status");
210
+ updateStrip(report);
211
+ return report;
212
+ } catch (err) {
213
+ showError(err);
214
+ return null;
215
+ }
216
+ }
217
+
218
+ form.addEventListener("submit", async (event) => {
219
+ event.preventDefault();
220
+ try {
221
+ await runCommand(input.value);
222
+ } catch (err) {
223
+ showError(err);
224
+ }
225
+ });
226
+
227
+ document.querySelectorAll("[data-command]").forEach((button) => {
228
+ button.addEventListener("click", async () => {
229
+ await runCommandFromButton(button.dataset.command || "");
230
+ });
231
+ });
232
+
233
+ function drawMetal() {
234
+ const canvas = document.querySelector("#metal");
235
+ const ctx = canvas.getContext("2d");
236
+ const ratio = Math.max(1, window.devicePixelRatio || 1);
237
+ let width = 0;
238
+ let height = 0;
239
+
240
+ function resize() {
241
+ width = Math.floor(window.innerWidth);
242
+ height = Math.floor(window.innerHeight);
243
+ canvas.width = width * ratio;
244
+ canvas.height = height * ratio;
245
+ canvas.style.width = `${width}px`;
246
+ canvas.style.height = `${height}px`;
247
+ ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
248
+ }
249
+
250
+ function frame(t) {
251
+ ctx.clearRect(0, 0, width, height);
252
+ ctx.globalAlpha = 0.42;
253
+ ctx.strokeStyle = "rgba(210, 220, 220, 0.08)";
254
+ ctx.lineWidth = 1;
255
+ for (let y = 22; y < height; y += 42) {
256
+ ctx.beginPath();
257
+ ctx.moveTo(0, y + Math.sin((t / 1800) + y) * 2);
258
+ ctx.lineTo(width, y + Math.cos((t / 2200) + y) * 2);
259
+ ctx.stroke();
260
+ }
261
+
262
+ ctx.globalAlpha = 0.55;
263
+ for (let i = 0; i < 7; i += 1) {
264
+ const y = (height * (0.18 + i * 0.11) + t * (0.012 + i * 0.0015)) % height;
265
+ const x0 = (width * (0.08 + i * 0.13)) % width;
266
+ ctx.strokeStyle = i % 2 ? "rgba(223, 189, 98, 0.2)" : "rgba(159, 169, 172, 0.14)";
267
+ ctx.lineWidth = i % 2 ? 1.4 : 1;
268
+ ctx.beginPath();
269
+ ctx.moveTo(x0, y);
270
+ ctx.lineTo(Math.min(width, x0 + width * 0.22), y);
271
+ ctx.lineTo(Math.min(width, x0 + width * 0.28), y + 38);
272
+ ctx.stroke();
273
+ }
274
+
275
+ ctx.globalAlpha = 1;
276
+ requestAnimationFrame(frame);
277
+ }
278
+
279
+ resize();
280
+ window.addEventListener("resize", resize);
281
+ requestAnimationFrame(frame);
282
+ }
283
+
284
+ drawMetal();
285
+ refreshStatus().then((report) => {
286
+ show("Ready", report || {}, "bare metal search");
287
+ input.focus();
288
+ });
meshscale_space/static/index.html ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>MeshScale</title>
7
+ <link rel="stylesheet" href="/static/styles.css" />
8
+ </head>
9
+ <body>
10
+ <canvas id="metal" aria-hidden="true"></canvas>
11
+ <main class="shell">
12
+ <section class="search-stage" aria-label="MeshScale command surface">
13
+ <div class="mark">
14
+ <span class="mark-dot"></span>
15
+ <span>MeshScale</span>
16
+ </div>
17
+
18
+ <form id="command-form" class="search-box" autocomplete="off">
19
+ <input
20
+ id="command"
21
+ name="command"
22
+ type="search"
23
+ spellcheck="false"
24
+ placeholder="Search, inspect, plan, regulate, scale"
25
+ aria-label="MeshScale OS search command"
26
+ />
27
+ <button type="submit" aria-label="Run command">
28
+ <span>Run</span>
29
+ </button>
30
+ </form>
31
+
32
+ <div class="quick-row" aria-label="Quick commands">
33
+ <button data-command="status" data-tip="Read pulse, workers, backlog, and errors.">Status</button>
34
+ <button data-command="guide" data-tip="Ask the OS route layer where to go next.">Guide</button>
35
+ <button data-command="trace" data-tip="Inspect traffic, results, pulse, and recommendations.">Trace</button>
36
+ <button data-command="plan 120" data-tip="Estimate workers for a pending shard count.">Plan 120</button>
37
+ <button data-command="scale dry" data-tip="Preview scaling without starting paid workers.">Scale dry</button>
38
+ <button data-command="drain 4" data-tip="Downshift to a target worker count.">Drain 4</button>
39
+ </div>
40
+
41
+ <div class="status-strip" id="status-strip">
42
+ <span>root: checking</span>
43
+ <span>pulse: checking</span>
44
+ <span>workers: checking</span>
45
+ <span>pending: checking</span>
46
+ </div>
47
+ </section>
48
+
49
+ <section class="result-grid" aria-label="MeshScale results">
50
+ <article class="panel output-panel">
51
+ <div class="panel-head">
52
+ <h1 id="result-title">Ready</h1>
53
+ <span id="result-mode">dry-run guarded</span>
54
+ </div>
55
+ <pre id="output">{}</pre>
56
+ </article>
57
+
58
+ <aside class="panel command-panel">
59
+ <div class="panel-head">
60
+ <h2>OS route</h2>
61
+ <span>OS search</span>
62
+ </div>
63
+ <div class="route-card" id="route-card">
64
+ <h3 id="route-title">Guide idle</h3>
65
+ <p id="route-message">Search for a goal and MeshScale will route the next step.</p>
66
+ <div id="route-actions" class="route-actions"></div>
67
+ </div>
68
+ <div class="command-list">
69
+ <button data-command="status" data-tip="Read current farm state.">status</button>
70
+ <button data-command="guide" data-tip="Show route suggestions.">guide</button>
71
+ <button data-command="trace" data-tip="Show full diagnostic trace.">trace</button>
72
+ <button data-command="plan 420" data-tip="Plan capacity for 420 pending jobs.">plan 420</button>
73
+ <button data-command="scale dry" data-tip="Run guarded scale decision.">scale dry</button>
74
+ <button data-command="scale apply" data-tip="Requests real HF actions only when apply is enabled.">scale apply</button>
75
+ <button data-command="run" data-tip="Publish run regulation.">run</button>
76
+ <button data-command="drain 0" data-tip="Stop new work and wind workers down.">drain 0</button>
77
+ <button data-command="shutdown" data-tip="Publish shutdown regulation.">shutdown</button>
78
+ </div>
79
+ </aside>
80
+ </section>
81
+ </main>
82
+ <script src="/static/app.js"></script>
83
+ </body>
84
+ </html>
meshscale_space/static/styles.css ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ color-scheme: dark;
3
+ --bg: #08090a;
4
+ --panel: rgba(18, 19, 19, 0.86);
5
+ --panel-strong: rgba(26, 27, 26, 0.94);
6
+ --line: rgba(229, 197, 112, 0.26);
7
+ --line-cold: rgba(170, 184, 191, 0.22);
8
+ --text: #f4f1e8;
9
+ --muted: #a8aba6;
10
+ --gold: #dfbd62;
11
+ --gold-hot: #f4d98b;
12
+ --steel: #9fa9ac;
13
+ --black: #090a0b;
14
+ font-family:
15
+ Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
16
+ sans-serif;
17
+ }
18
+
19
+ * {
20
+ box-sizing: border-box;
21
+ }
22
+
23
+ html,
24
+ body {
25
+ min-height: 100%;
26
+ margin: 0;
27
+ }
28
+
29
+ body {
30
+ min-width: 320px;
31
+ background:
32
+ linear-gradient(90deg, rgba(255, 255, 255, 0.025) 1px, transparent 1px) 0 0 / 76px 76px,
33
+ repeating-linear-gradient(
34
+ 102deg,
35
+ #070808 0,
36
+ #0d0e0e 2px,
37
+ #090a0a 6px,
38
+ #111211 8px
39
+ );
40
+ color: var(--text);
41
+ overflow-x: hidden;
42
+ }
43
+
44
+ #metal {
45
+ position: fixed;
46
+ inset: 0;
47
+ width: 100vw;
48
+ height: 100vh;
49
+ pointer-events: none;
50
+ opacity: 0.62;
51
+ }
52
+
53
+ .shell {
54
+ position: relative;
55
+ z-index: 1;
56
+ width: min(1120px, calc(100% - 28px));
57
+ margin: 0 auto;
58
+ padding: clamp(28px, 7vh, 76px) 0 40px;
59
+ }
60
+
61
+ .search-stage {
62
+ min-height: min(540px, calc(100vh - 40px));
63
+ display: flex;
64
+ flex-direction: column;
65
+ align-items: center;
66
+ justify-content: center;
67
+ gap: 18px;
68
+ }
69
+
70
+ .mark {
71
+ display: inline-flex;
72
+ align-items: center;
73
+ gap: 10px;
74
+ min-height: 32px;
75
+ color: var(--text);
76
+ font-size: clamp(25px, 5vw, 54px);
77
+ font-weight: 680;
78
+ letter-spacing: 0;
79
+ text-shadow: 0 1px 0 #000, 0 0 24px rgba(244, 217, 139, 0.18);
80
+ }
81
+
82
+ .mark-dot {
83
+ width: 14px;
84
+ height: 14px;
85
+ display: inline-block;
86
+ border: 1px solid rgba(255, 231, 163, 0.8);
87
+ background:
88
+ linear-gradient(135deg, rgba(255, 237, 174, 0.96), rgba(165, 121, 34, 0.96));
89
+ box-shadow: 0 0 20px rgba(223, 189, 98, 0.42);
90
+ }
91
+
92
+ .search-box {
93
+ width: min(760px, 100%);
94
+ min-height: 58px;
95
+ display: grid;
96
+ grid-template-columns: 1fr auto;
97
+ align-items: center;
98
+ padding: 6px;
99
+ border: 1px solid var(--line);
100
+ background:
101
+ linear-gradient(180deg, rgba(255, 255, 255, 0.08), transparent 28%),
102
+ rgba(12, 13, 13, 0.9);
103
+ box-shadow:
104
+ inset 0 1px 0 rgba(255, 255, 255, 0.08),
105
+ 0 22px 80px rgba(0, 0, 0, 0.46),
106
+ 0 0 0 1px rgba(0, 0, 0, 0.7);
107
+ }
108
+
109
+ .search-box:focus-within {
110
+ border-color: rgba(244, 217, 139, 0.7);
111
+ box-shadow:
112
+ inset 0 1px 0 rgba(255, 255, 255, 0.1),
113
+ 0 24px 90px rgba(0, 0, 0, 0.54),
114
+ 0 0 0 3px rgba(223, 189, 98, 0.14);
115
+ }
116
+
117
+ .search-box input {
118
+ min-width: 0;
119
+ width: 100%;
120
+ border: 0;
121
+ outline: 0;
122
+ background: transparent;
123
+ color: var(--text);
124
+ padding: 0 18px;
125
+ font: inherit;
126
+ font-size: 17px;
127
+ }
128
+
129
+ .search-box input::placeholder {
130
+ color: rgba(231, 226, 212, 0.46);
131
+ }
132
+
133
+ button {
134
+ position: relative;
135
+ min-height: 40px;
136
+ border: 1px solid rgba(255, 232, 165, 0.22);
137
+ background:
138
+ linear-gradient(180deg, rgba(255, 255, 255, 0.1), transparent),
139
+ #151615;
140
+ color: var(--text);
141
+ font: inherit;
142
+ font-size: 13px;
143
+ letter-spacing: 0;
144
+ cursor: pointer;
145
+ }
146
+
147
+ button:hover {
148
+ border-color: rgba(244, 217, 139, 0.7);
149
+ color: #fff8dc;
150
+ }
151
+
152
+ button:active {
153
+ transform: translateY(1px);
154
+ }
155
+
156
+ button[data-tip]::after {
157
+ content: attr(data-tip);
158
+ position: absolute;
159
+ left: 50%;
160
+ bottom: calc(100% + 8px);
161
+ z-index: 8;
162
+ width: min(260px, 80vw);
163
+ padding: 8px 10px;
164
+ border: 1px solid rgba(223, 189, 98, 0.34);
165
+ background: rgba(8, 9, 9, 0.96);
166
+ color: #e9e2cf;
167
+ font-size: 12px;
168
+ line-height: 1.35;
169
+ text-align: left;
170
+ white-space: normal;
171
+ box-shadow: 0 12px 38px rgba(0, 0, 0, 0.42);
172
+ opacity: 0;
173
+ transform: translate(-50%, 4px);
174
+ pointer-events: none;
175
+ transition: opacity 140ms ease, transform 140ms ease;
176
+ }
177
+
178
+ button[data-tip]:hover::after,
179
+ button[data-tip]:focus-visible::after {
180
+ opacity: 1;
181
+ transform: translate(-50%, 0);
182
+ }
183
+
184
+ .search-box button {
185
+ min-width: 72px;
186
+ padding: 0 18px;
187
+ background:
188
+ linear-gradient(180deg, rgba(255, 255, 255, 0.18), transparent 44%),
189
+ linear-gradient(135deg, #725719, #d7b75e 54%, #6b4c11);
190
+ color: #100d06;
191
+ font-weight: 720;
192
+ }
193
+
194
+ .quick-row,
195
+ .status-strip {
196
+ width: min(760px, 100%);
197
+ display: flex;
198
+ align-items: center;
199
+ justify-content: center;
200
+ gap: 8px;
201
+ flex-wrap: wrap;
202
+ }
203
+
204
+ .quick-row button {
205
+ min-height: 34px;
206
+ padding: 0 14px;
207
+ color: var(--muted);
208
+ background: rgba(14, 15, 15, 0.74);
209
+ border-color: rgba(170, 184, 191, 0.18);
210
+ }
211
+
212
+ .quick-row button:hover {
213
+ color: var(--text);
214
+ }
215
+
216
+ .status-strip {
217
+ justify-content: center;
218
+ color: var(--muted);
219
+ font-size: 12px;
220
+ }
221
+
222
+ .status-strip span {
223
+ min-height: 28px;
224
+ display: inline-flex;
225
+ align-items: center;
226
+ padding: 0 10px;
227
+ border: 1px solid rgba(170, 184, 191, 0.14);
228
+ background: rgba(6, 7, 7, 0.46);
229
+ }
230
+
231
+ .result-grid {
232
+ display: grid;
233
+ grid-template-columns: minmax(0, 1fr) 280px;
234
+ gap: 14px;
235
+ align-items: start;
236
+ }
237
+
238
+ .panel {
239
+ border: 1px solid var(--line-cold);
240
+ background:
241
+ linear-gradient(180deg, rgba(255, 255, 255, 0.055), transparent 120px),
242
+ var(--panel);
243
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
244
+ }
245
+
246
+ .panel-head {
247
+ min-height: 48px;
248
+ display: flex;
249
+ align-items: center;
250
+ justify-content: space-between;
251
+ gap: 12px;
252
+ padding: 12px 14px;
253
+ border-bottom: 1px solid rgba(170, 184, 191, 0.14);
254
+ }
255
+
256
+ .panel-head h1,
257
+ .panel-head h2 {
258
+ margin: 0;
259
+ font-size: 14px;
260
+ font-weight: 720;
261
+ }
262
+
263
+ .panel-head span {
264
+ color: var(--gold);
265
+ font-size: 12px;
266
+ white-space: nowrap;
267
+ }
268
+
269
+ pre {
270
+ min-height: 320px;
271
+ max-height: 560px;
272
+ margin: 0;
273
+ overflow: auto;
274
+ padding: 16px;
275
+ color: #dce2dc;
276
+ font-family: "JetBrains Mono", "SFMono-Regular", Consolas, monospace;
277
+ font-size: 12px;
278
+ line-height: 1.52;
279
+ white-space: pre-wrap;
280
+ word-break: break-word;
281
+ }
282
+
283
+ .command-list {
284
+ display: grid;
285
+ gap: 8px;
286
+ padding: 12px;
287
+ }
288
+
289
+ .route-card {
290
+ margin: 12px 12px 0;
291
+ padding: 12px;
292
+ border: 1px solid rgba(223, 189, 98, 0.2);
293
+ background: rgba(8, 9, 9, 0.68);
294
+ }
295
+
296
+ .route-card h3 {
297
+ margin: 0 0 7px;
298
+ color: var(--gold-hot);
299
+ font-size: 13px;
300
+ font-weight: 720;
301
+ }
302
+
303
+ .route-card p {
304
+ margin: 0;
305
+ color: var(--muted);
306
+ font-size: 12px;
307
+ line-height: 1.45;
308
+ }
309
+
310
+ .route-actions {
311
+ display: grid;
312
+ gap: 7px;
313
+ margin-top: 12px;
314
+ }
315
+
316
+ .route-actions a,
317
+ .route-actions button {
318
+ min-height: 34px;
319
+ display: flex;
320
+ align-items: center;
321
+ padding: 0 10px;
322
+ border: 1px solid rgba(170, 184, 191, 0.18);
323
+ background: rgba(14, 15, 15, 0.8);
324
+ color: var(--text);
325
+ font-size: 12px;
326
+ text-decoration: none;
327
+ }
328
+
329
+ .route-actions a:hover,
330
+ .route-actions button:hover {
331
+ border-color: rgba(244, 217, 139, 0.62);
332
+ }
333
+
334
+ .command-list button {
335
+ width: 100%;
336
+ text-align: left;
337
+ padding: 0 12px;
338
+ background: rgba(9, 10, 10, 0.72);
339
+ }
340
+
341
+ @media (max-width: 820px) {
342
+ .shell {
343
+ width: min(100% - 18px, 760px);
344
+ padding-top: 24px;
345
+ }
346
+
347
+ .search-stage {
348
+ min-height: auto;
349
+ padding: 34px 0 24px;
350
+ }
351
+
352
+ .search-box {
353
+ grid-template-columns: 1fr;
354
+ gap: 6px;
355
+ }
356
+
357
+ .search-box input {
358
+ min-height: 46px;
359
+ padding: 0 12px;
360
+ }
361
+
362
+ .search-box button {
363
+ width: 100%;
364
+ }
365
+
366
+ .result-grid {
367
+ grid-template-columns: 1fr;
368
+ }
369
+
370
+ .command-panel {
371
+ order: -1;
372
+ }
373
+
374
+ .command-list {
375
+ grid-template-columns: repeat(2, minmax(0, 1fr));
376
+ }
377
+ }