commandnet 0.6.2__tar.gz → 0.6.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commandnet
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: A lightweight, Pydantic-powered, distributed event-driven state machine and typed node graph runtime.
5
5
  Author: Christopher Vaz
6
6
  Author-email: christophervaz160@gmail.com
@@ -48,7 +48,12 @@ class Engine:
48
48
  # --- CORE WORKER LOGIC ---
49
49
 
50
50
  async def process_event(self, event: Event):
51
- # 1. Internal Control (Cross-worker Hard Cancel)
51
+ if event.node_name == "__SIGNAL_RELEASE__":
52
+ signal_id = event.payload.get("signal_id")
53
+ signal_data = event.payload.get("data")
54
+ await self._handle_distributed_signal(signal_id, signal_data)
55
+ return
56
+
52
57
  if event.node_name == "__CONTROL__":
53
58
  action = (event.payload or {}).get("action")
54
59
  if action == "HARD_CANCEL" and event.subject_id in self._active_tasks:
@@ -69,6 +74,25 @@ class Engine:
69
74
  finally:
70
75
  self._active_tasks.pop(event.subject_id, None)
71
76
 
77
+ async def _handle_distributed_signal(self, signal_id: str, payload: Any):
78
+ """
79
+ One worker picks up the signal release event and converts
80
+ parked subjects into active events.
81
+ """
82
+ # 1. Find all subjects waiting on this signal in the DB
83
+ waiters = await self.db.get_and_clear_waiters(signal_id)
84
+
85
+ for waiter in waiters:
86
+ subject_id = waiter["subject_id"]
87
+ # 2. For each waiter, trigger a standard resume turn
88
+ # This puts a specific task-event back on the RabbitMQ queue
89
+ await self._apply_target(
90
+ subject_id=subject_id,
91
+ context=waiter["context"],
92
+ target=waiter["next_target"],
93
+ payload=payload
94
+ )
95
+
72
96
  async def _run_node_logic(self, event: Event):
73
97
  subject_id = event.subject_id
74
98
 
@@ -385,20 +409,19 @@ class Engine:
385
409
  await self.db.unlock_subject(subject_id)
386
410
 
387
411
  async def release_signal(self, signal_id: str, payload: Any = None):
388
- """Standard mass-resume for parked subjects."""
389
- waiters = await self.db.get_and_clear_waiters(signal_id)
390
- for waiter in waiters:
391
- subject_id = waiter["subject_id"]
392
- await self.db.lock_and_load(subject_id)
393
- try:
394
- await self._apply_target(
395
- subject_id=subject_id,
396
- context=waiter["context"],
397
- target=waiter["next_target"],
398
- payload=payload,
399
- )
400
- finally:
401
- await self.db.unlock_subject(subject_id)
412
+ """
413
+ NEW: Instead of processing locally, broadcast the signal
414
+ to the entire cluster via the EventBus.
415
+ """
416
+ sig_event = Event(
417
+ subject_id="__GLOBAL__",
418
+ node_name="__SIGNAL_RELEASE__",
419
+ payload={
420
+ "signal_id": signal_id,
421
+ "data": payload
422
+ }
423
+ )
424
+ await self.bus.publish(sig_event)
402
425
 
403
426
  async def cancel_subject(self, subject_id: str, hard: bool = True):
404
427
  await self.db.set_cancel_flag(subject_id, hard)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "commandnet"
3
- version = "0.6.2"
3
+ version = "0.6.3"
4
4
  description = "A lightweight, Pydantic-powered, distributed event-driven state machine and typed node graph runtime."
5
5
  authors = [
6
6
  { name = "Christopher Vaz", email = "christophervaz160@gmail.com" }
File without changes