commandnet 0.6.1__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.1
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
 
@@ -115,7 +139,8 @@ class Engine:
115
139
  result = Interrupt(subject_id=subject_id, hard=True)
116
140
 
117
141
  duration = (asyncio.get_event_loop().time() - start_t) * 1000
118
- await self._apply_target(subject_id, ctx, result, duration)
142
+
143
+ await self._apply_target(subject_id, ctx, result, duration, payload=payload)
119
144
 
120
145
  except Exception as e:
121
146
  await self.observer.on_error(subject_id, event.node_name, e)
@@ -379,25 +404,24 @@ class Engine:
379
404
  )
380
405
 
381
406
  result = await node_inst.on_signal(ctx, signal_id, payload)
382
- await self._apply_target(subject_id, ctx, result)
407
+ await self._apply_target(subject_id, ctx, result, payload=payload)
383
408
  finally:
384
409
  await self.db.unlock_subject(subject_id)
385
410
 
386
411
  async def release_signal(self, signal_id: str, payload: Any = None):
387
- """Standard mass-resume for parked subjects."""
388
- waiters = await self.db.get_and_clear_waiters(signal_id)
389
- for waiter in waiters:
390
- subject_id = waiter["subject_id"]
391
- await self.db.lock_and_load(subject_id)
392
- try:
393
- await self._apply_target(
394
- subject_id=subject_id,
395
- context=waiter["context"],
396
- target=waiter["next_target"],
397
- payload=payload,
398
- )
399
- finally:
400
- 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)
401
425
 
402
426
  async def cancel_subject(self, subject_id: str, hard: bool = True):
403
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.1"
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