AbstractIntegratedModule 0.9.3__tar.gz → 0.9.4__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.
Files changed (23) hide show
  1. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.egg-info/PKG-INFO +3 -10
  2. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.py +230 -134
  3. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractOptimizedModules.c +2175 -1230
  4. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractOptimizedModules.pyx +55 -0
  5. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/PKG-INFO +3 -10
  6. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/README.md +2 -9
  7. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/setup.py +1 -1
  8. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
  9. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
  10. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
  11. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
  12. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/MANIFEST.in +0 -0
  13. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/Cargo.toml +0 -0
  14. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/pyproject.toml +0 -0
  15. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/src/lib.rs +0 -0
  16. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
  17. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
  18. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
  19. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
  20. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
  21. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
  22. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/pyproject.toml +0 -0
  23. {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbstractIntegratedModule
3
- Version: 0.9.3
3
+ Version: 0.9.4
4
4
  Summary: Library for Advanced Integrated Non-LLM AI Models - Optimized Backend Framework For Non-LLM AI Agent
5
5
  Author: Micro-Novelty
6
6
  Author-email: hernikpuspita5@gmail.com
@@ -42,7 +42,7 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
42
42
  #### Note: The README here you are reading is a direct copy from my README Repository, to download the necessary files, you can visit my Repository with the provided link above.
43
43
 
44
44
  ### Library Short Description:
45
- - Development Stage: 0.9.3 Official Release.
45
+ - Development Stage: 0.9.4 Official Release.
46
46
  - Maintainer: Micro-Novelty.
47
47
  - library Source-Code is Open-sourced on github.
48
48
  - Purpose: Specifically Designed for providing Non-LLM AI Agent Framework for edge Devices, Optimized for ARM64 architecture.
@@ -79,14 +79,7 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
79
79
  - Robust Advanced prediction capabilities proven effective on ARM64 Using MLP + LSTM Architectures.
80
80
  - Transformer Optimized using Cython, to reduce Memory overhead and Reduce CPU Usage, With Reduced Training Time.
81
81
  ______________
82
- - Changelog:
83
- - v0.9.3:
84
- - [=] New features:
85
- - titles and rules is now Optional in all prediction methods, given if X and y samples are provided during prediction.
86
- - added guards for saving samples to check for ellipsis object.
87
- - Both Asynchronous Prediction and P2P doesnt rely on titles and rules, given if X and y samples are provided.
88
- - refined Transformer and MLP training methods.
89
- - Added new method to generate supported samples from the Provided X samples that matched Transformer token embedding
82
+
90
83
  ____________
91
84
  <img width="1280" height="600" alt="WhatsApp Image 2026-05-27 at 07 16 32" src="https://github.com/user-attachments/assets/4b58a556-45a3-419b-96fd-9c1b76cac574" />
92
85
 
@@ -62,7 +62,9 @@ try:
62
62
  optimized_anisotropy,
63
63
  optimized_cosine_similarity,
64
64
  optimized_softmax_2d,
65
- optimized_dynamic_weighted_ensemble
65
+ optimized_dynamic_weighted_ensemble,
66
+ optimized_qkv_weight_grad,
67
+ optimized_qkv_input_grad,
66
68
  )
67
69
  _OPT_AVAILABLE = True
68
70
  print('[=] Cython acceleration loaded ✅')
@@ -141,7 +143,8 @@ class AsyncRequest:
141
143
  @property
142
144
  def age(self) -> float:
143
145
  return time.time() - self.created_at
144
-
146
+
147
+
145
148
  @property
146
149
  def is_expired(self, timeout: int = 30) -> bool:
147
150
  return self.age > timeout
@@ -169,16 +172,28 @@ class Message:
169
172
  max_retries: int = 3
170
173
  timeout: float = 30.0
171
174
  created_at: float = field(default_factory=time.time)
172
-
175
+ trust: float = 1.0
176
+
173
177
  @property
174
178
  def age(self) -> float:
175
179
  """Age of message in seconds."""
176
180
  return time.time() - self.created_at
177
-
181
+
182
+ @property
183
+ def proper_trust(self) -> bool:
184
+ return self.trust > 0.3
185
+
186
+ @property
187
+ def degrade_trust(self) -> bool:
188
+ self.trust = self.trust - 0.1
189
+
178
190
  @property
179
191
  def is_expired(self) -> bool:
180
192
  """Check if message has expired."""
181
- return self.age > self.timeout
193
+ expired = self.age > self.timeout
194
+ if expired:
195
+ self.degrade_trust()
196
+ return expired
182
197
 
183
198
  # ============ COMPARISON METHODS FOR PRIORITY QUEUE ============
184
199
 
@@ -1212,24 +1227,31 @@ class Transformer:
1212
1227
 
1213
1228
  x = self.cache['x_attn_input']
1214
1229
 
1215
- d_W_q = np.einsum('bsd, bhsm->hdm', x, d_Q)
1216
- d_W_k = np.einsum('bsd, bhsm->hdm', x, d_K)
1217
- d_W_v = np.einsum('bsd, bhsm->hdm', x, d_V)
1230
+ if _OPT_AVAILABLE:
1231
+ B, S = x.shape[0], x.shape[1]
1232
+ H, M = self.n_heads, self.d_model // self.n_heads
1233
+
1234
+ d_W_q = optimized_qkv_weight_grad(x, d_Q, B, S, H, self.d_model, M)
1235
+ d_W_k = optimized_qkv_weight_grad(x, d_K, B, S, H, self.d_model, M)
1236
+ d_W_v = optimized_qkv_weight_grad(x, d_V, B, S, H, self.d_model, M)
1237
+
1238
+ d_x_q = optimized_qkv_input_grad(d_Q, self.W_q, B, S, H, self.d_model, M)
1239
+ d_x_k = optimized_qkv_input_grad(d_K, self.W_k, B, S, H, self.d_model, M)
1240
+ d_x_v = optimized_qkv_input_grad(d_V, self.W_v, B, S, H, self.d_model, M)
1241
+ else:
1242
+ d_W_q = np.einsum('bsd, bhsm->hdm', x, d_Q)
1243
+ d_W_k = np.einsum('bsd, bhsm->hdm', x, d_K)
1244
+ d_W_v = np.einsum('bsd, bhsm->hdm', x, d_V)
1218
1245
 
1219
- d_x_q = np.einsum('bhsm, hdm->bsd', d_Q, self.W_q)
1220
- d_x_k = np.einsum('bhsm, hdm->bsd', d_K, self.W_k)
1221
- d_x_v = np.einsum('bhsm, hdm->bsd', d_V, self.W_v)
1246
+ d_x_q = np.einsum('bhsm, hdm->bsd', d_Q, self.W_q)
1247
+ d_x_k = np.einsum('bhsm, hdm->bsd', d_K, self.W_k)
1248
+ d_x_v = np.einsum('bhsm, hdm->bsd', d_V, self.W_v)
1222
1249
 
1223
1250
  d_x_attn_input = d_x_q + d_x_k + d_x_v
1224
1251
  d_x_total = d_x_attn_input + d_residual
1225
1252
 
1226
1253
  input_ids = self.cache.get('input_ids')
1227
1254
 
1228
- if input_ids is not None:
1229
- flat_ids = input_ids.flatten() # (B*T,)
1230
- flat_grads = d_x_total.reshape(-1, self.d_model) / self.cache['seq_len']
1231
- np.add.at(self.token_embedding, flat_ids, -lr * flat_grads)
1232
-
1233
1255
  # Update weights
1234
1256
  grads = {
1235
1257
  'output': d_Wo,
@@ -1333,6 +1355,9 @@ class Transformer:
1333
1355
  y_true_smoothed_list = []
1334
1356
  d_model = self.d_model
1335
1357
 
1358
+ input_ids_list = self._sanitize_string_chars(input_ids_list)
1359
+ y_true_list = self._sanitize_string_chars(y_true_list)
1360
+
1336
1361
  # W_o geometric init — unchanged
1337
1362
  if not self.encoded:
1338
1363
  self.shaping = GeometricWeightShaping(d_model, d_model)
@@ -1494,8 +1519,19 @@ class Transformer:
1494
1519
  acc = float(np.mean(preds == true))
1495
1520
 
1496
1521
  return loss, acc
1522
+
1523
+ def _sanitize_string_chars(self, x):
1524
+ if isinstance(x, (str, np.str_)):
1525
+ clean_str = str(x).replace('[', '').replace(']', '').replace('...', '').strip()
1526
+ x = np.fromstring(clean_str, sep=' ')
1497
1527
 
1528
+ if isinstance(x, np.ndarray) and np.issubdtype(x.dtype, np.character):
1529
+ # catches arrays filled with string text
1530
+ clean_str = ' '.join(x.astype(str).flatten()).replace('[', '').replace(']', '')
1531
+ skip_values = {"...", "NaN", "null"}
1532
+ x = np.fromiter((v for v in clean_str.split() if v not in skip_values), dtype=float)
1498
1533
 
1534
+ return x
1499
1535
 
1500
1536
  def predict(self, input_ids, embedded=False):
1501
1537
  if not embedded and input_ids.ndim == 1:
@@ -1509,6 +1545,8 @@ class Transformer:
1509
1545
 
1510
1546
 
1511
1547
  def AME_Encoder(self, x):
1548
+ x = self._sanitize_string_chars(x)
1549
+
1512
1550
  # Optimized AME_Encoder for Transformer
1513
1551
  x = np.asarray(x)
1514
1552
  if _OPT_AVAILABLE and np.asarray(x).ndim == 2:
@@ -1527,6 +1565,8 @@ class Transformer:
1527
1565
 
1528
1566
  def anisotropy_measurement(self, x):
1529
1567
  eps = 1e-5
1568
+
1569
+ x = self._sanitize_string_chars(x)
1530
1570
  if _OPT_AVAILABLE:
1531
1571
  x = np.asarray(x)
1532
1572
  x = x.reshape(x.shape[0], -1)
@@ -1576,7 +1616,7 @@ class Transformer:
1576
1616
 
1577
1617
  # AME inline — avoids second np.gradient call in AME_Encoder
1578
1618
  # gradient is already a list of arrays, one per dimension
1579
- # stack into single array for vectorized norm — ARM64 NEON friendly
1619
+ # stacked into single array for vectorized norm — ARM64 NEON friendly
1580
1620
  if AME is None:
1581
1621
  grad_stack = np.stack([g.ravel() for g in gradient]) # (ndim, N)
1582
1622
  grad_norms = np.linalg.norm(grad_stack, axis=1) # (ndim,) — one NEON call
@@ -1674,8 +1714,8 @@ class Dense:
1674
1714
  return x
1675
1715
 
1676
1716
  def multi_modal_linear_transformation(self, x):
1677
-
1678
1717
  x = self._sanitize_string_chars(x)
1718
+
1679
1719
  if len(x.shape) > 1 and x.shape[1] != self.W.shape[0]:
1680
1720
  V1, V2 = x.shape[0], x.shape[1]
1681
1721
  try:
@@ -1721,6 +1761,8 @@ class Dense:
1721
1761
 
1722
1762
 
1723
1763
  def forward(self, x):
1764
+
1765
+ x = self._sanitize_string_chars(x)
1724
1766
  self.x = x
1725
1767
  self.z = self.multi_modal_linear_transformation(x)
1726
1768
 
@@ -1800,6 +1842,19 @@ class MLP:
1800
1842
  x = layer.forward(x)
1801
1843
 
1802
1844
  return self.softmax.forward(x)
1845
+
1846
+ def _sanitize_string_chars(self, x):
1847
+ if isinstance(x, (str, np.str_)):
1848
+ clean_str = str(x).replace('[', '').replace(']', '').replace('...', '').strip()
1849
+ x = np.fromstring(clean_str, sep=' ')
1850
+
1851
+ if isinstance(x, np.ndarray) and np.issubdtype(x.dtype, np.character):
1852
+ # catches arrays filled with string text
1853
+ clean_str = ' '.join(x.astype(str).flatten()).replace('[', '').replace(']', '')
1854
+ skip_values = {"...", "NaN", "null"}
1855
+ x = np.fromiter((v for v in clean_str.split() if v not in skip_values), dtype=float)
1856
+
1857
+ return x
1803
1858
 
1804
1859
 
1805
1860
  def _calibrate_gradient(self, grad, AME, anisotropy):
@@ -1978,6 +2033,9 @@ class MLP:
1978
2033
  return y_pred, y_true
1979
2034
 
1980
2035
  def train(self, X, y, epochs=1000, lr=0.01, verbose=True):
2036
+ X = self._sanitize_string_chars(X)
2037
+ y = self._sanitize_string_chars(y)
2038
+
1981
2039
  AME = self.AME_Encoder(X)
1982
2040
  anisotropy = self.anisotropy_measurement(X)
1983
2041
  focused_fit_condition = len(self.feed_layers) > 0 and anisotropy > 0.25 and AME > 0.25
@@ -5812,88 +5870,85 @@ class ModelStorage:
5812
5870
 
5813
5871
 
5814
5872
  class AsyncMessageQueue:
5815
- # async message queue handler.
5816
-
5817
- def __init__(self, max_size=1000, dead_letter_queue_size=100):
5818
- self.queue = asyncio.PriorityQueue(maxsize=max_size)
5873
+ def __init__(self, max_size=1000, dead_letter_queue_size=100,
5874
+ latency_smoothing=0.2):
5875
+ self.queue = asyncio.PriorityQueue(maxsize=max_size)
5819
5876
  self.pending: Dict[str, asyncio.Future] = {}
5820
5877
  self.results: Dict[str, Any] = {}
5821
5878
  self.handlers: Dict[str, Callable] = {}
5822
5879
  self.dead_letter_queue: deque = deque(maxlen=dead_letter_queue_size)
5823
5880
  self._running = False
5881
+
5824
5882
  self._worker_task: Optional[asyncio.Task] = None
5825
- self._loop: Optional[asyncio.AbstractEventLoop] = None
5826
- self._counter_lock = asyncio.Lock()
5827
- self._start_lock = asyncio.Lock()
5883
+ self._loop: Optional[asyncio.AbstractEventLoop] = None
5884
+ self._start_lock = asyncio.Lock()
5885
+
5828
5886
  self._counter = 0
5887
+ self.alpha = latency_smoothing #EMA weight
5888
+
5829
5889
  self._stats = {
5830
5890
  'messages_processed': 0,
5831
- 'messages_failed': 0,
5832
- 'messages_retried': 0,
5833
- 'messages_expired': 0,
5834
- 'avg_latency': 0.0
5891
+ 'messages_failed' : 0,
5892
+ 'messages_retried' : 0,
5893
+ 'messages_expired' : 0,
5894
+ 'avg_latency' : 0.0,
5895
+ 'messages_untrusted': 0
5835
5896
  }
5836
-
5897
+
5837
5898
  def register_handler(self, message_type: str, handler: Callable):
5838
- # Register a handler for specific message type
5839
5899
  self.handlers[message_type] = handler
5840
-
5841
5900
  logger.info(f"[=] Registered handler for {message_type}")
5842
-
5901
+
5902
+
5843
5903
  async def _ensure_started(self):
5844
- """Start the worker if not already running"""
5904
+ """Single entry point for starting the worker """
5845
5905
  if self._running:
5846
5906
  return
5847
-
5907
+
5848
5908
  async with self._start_lock:
5849
5909
  if self._running:
5850
5910
  return
5851
-
5852
- self._running = True
5911
+ self._running = True
5853
5912
  self._worker_task = asyncio.create_task(self._worker())
5913
+
5854
5914
  logger.info("[=] Async message queue worker started")
5855
-
5856
- # Give worker a moment to initialize
5857
5915
  await asyncio.sleep(0.1)
5858
-
5859
5916
  if self._worker_task.done():
5860
5917
  exc = self._worker_task.exception()
5861
5918
  if exc:
5862
5919
  logger.error(f"[=] Worker failed: {exc}")
5920
+ self._running = False
5863
5921
  raise exc
5864
-
5922
+
5865
5923
 
5866
5924
  async def publish(self, message: Message) -> Any:
5867
- # Publish a message and wait for response.
5868
- # Generate unique counter
5869
5925
  await self._ensure_started()
5870
5926
 
5871
- async with self._counter_lock:
5872
- self._counter += 1
5873
- counter = self._counter
5927
+ # plain increment, no lock needed (no await in critical section)
5928
+ self._counter += 1
5929
+ counter = self._counter
5874
5930
 
5931
+ if self._stats['avg_latency'] > 0.25:
5932
+ message.trust - 0.1
5933
+
5875
5934
  if message.is_expired:
5876
5935
  logger.warning(f"[-] Message {message.id} already expired")
5877
5936
  raise TimeoutError(f"[-] Message {message.id} already expired")
5878
5937
 
5938
+
5939
+ if not message.proper_trust:
5940
+ raise Warning(f"[!] Message is not properly Trusted!")
5941
+
5879
5942
  future = asyncio.Future()
5880
5943
  self.pending[message.id] = future
5881
- logger.info(f"[=] Publishing message, ID: {message.id} of type {message.type} with priority {message.priority.name}")
5944
+ logger.debug(f"[=] Publishing message {message.id} type={message.type} "
5945
+ f"priority={message.priority.name}")
5882
5946
 
5883
- print('=== Queue Status ===')
5884
- logger.info(f"[QueueStatus] Queue size before put: {self.queue.qsize()}")
5885
- logger.info(f"[QueueStatus] Queue maxsize: {self.queue.maxsize}")
5886
- logger.info(f"[QueueStatus] Queue full: {self.queue.full()}")
5887
- logger.info(f"[QueueStatus] Worker running: {self._worker_task is not None and not self._worker_task.done()}")
5888
-
5889
- # Use priority queue (lower number = higher priority)
5947
+ # 3-tuple, matches _worker's unpack exactly as is.
5890
5948
  await self.queue.put((message.priority.value, counter, message))
5891
- logger.debug(f"[=] Message {message.id} enqueued, waiting for response...")
5892
-
5949
+
5893
5950
  try:
5894
- logger.debug(f"[=] Awaiting response for message {message.id} with timeout {message.timeout}s")
5895
5951
  result = await asyncio.wait_for(future, timeout=message.timeout)
5896
- logger.debug(f"[=] Received response for message {message.id}: {result}")
5897
5952
  return result
5898
5953
  except asyncio.TimeoutError:
5899
5954
  self.pending.pop(message.id, None)
@@ -5902,66 +5957,95 @@ class AsyncMessageQueue:
5902
5957
  raise
5903
5958
  except Exception as e:
5904
5959
  self.pending.pop(message.id, None)
5905
- logger.error(f"[-] Error occurred while processing message {message.id}: {e}")
5960
+ logger.error(f"[-] Error processing message {message.id}: {e}")
5906
5961
  raise
5907
-
5962
+
5908
5963
  async def publish_async(self, message: Message, callback: Optional[Callable] = None):
5909
- # Publish without waiting (fire and forget).
5964
+ """Fire and forget, uses consistent 3-tuple format."""
5965
+ await self._ensure_started()
5910
5966
  message.callback = callback
5911
- await self.queue.put((message.priority.value, message))
5912
-
5967
+
5968
+ self._counter += 1
5969
+ counter = self._counter
5970
+ await self.queue.put((message.priority.value, counter, message))
5971
+
5913
5972
  async def _worker(self):
5914
- # Background worker processing messages.
5915
5973
  while self._running:
5916
5974
  try:
5917
- priority, counter, message = await asyncio.wait_for(self.queue.get(), timeout=1.0)
5918
- logger.info(f"[=] Worker picked up message {message.id} (counter: {counter}, priority: {priority})")
5975
+ priority, counter, message = await asyncio.wait_for(
5976
+ self.queue.get(), timeout=1.0
5977
+ )
5978
+ logger.debug(f"[=] Worker picked up {message.id} "
5979
+ f"(counter={counter}, priority={priority})")
5919
5980
 
5920
5981
  start_time = time.time()
5921
-
5982
+
5922
5983
  if message.is_expired:
5923
5984
  self._stats['messages_expired'] += 1
5924
- logger.warning(f"[-] Dropping expired message {message.id}")
5925
5985
  self._handle_orphaned_message(message)
5926
5986
  continue
5927
-
5987
+
5988
+ if not message.proper_trust:
5989
+ self._stats['messages_untrusted'] += 1
5990
+
5991
+ # treat as orphan
5992
+ self._handle_orphaned_message(message)
5993
+ continue
5994
+
5928
5995
  if message.type in self.handlers:
5929
5996
  try:
5930
- # Execute handler
5931
5997
  if asyncio.iscoroutinefunction(self.handlers[message.type]):
5932
5998
  result = await self.handlers[message.type](message)
5933
5999
  else:
5934
6000
  result = self.handlers[message.type](message)
5935
-
5936
- # Calculate latency
6001
+
5937
6002
  latency = time.time() - start_time
5938
6003
  self._update_stats(latency, success=True)
5939
-
5940
- # Handle response
6004
+
6005
+ # pop from pending on success, was leaking before
5941
6006
  if message.id in self.pending:
5942
- self.pending[message.id].set_result(result)
6007
+ future = self.pending.pop(message.id)
6008
+ if not future.done():
6009
+ future.set_result(result)
5943
6010
  elif message.callback:
5944
6011
  message.callback(result)
5945
-
6012
+
5946
6013
  except Exception as e:
5947
6014
  self._stats['messages_failed'] += 1
5948
- logger.error(f"[-] Handler failed for {message.type}: {e}\n{traceback.format_exc()}")
5949
-
6015
+ logger.error(f"[-] Handler failed for {message.type}: {e}\n"
6016
+ f"{traceback.format_exc()}")
6017
+
5950
6018
  if message.retry_count < message.max_retries:
5951
6019
  message.retry_count += 1
5952
6020
  self._stats['messages_retried'] += 1
5953
- logger.info(f"[-] Retrying message {message.id} (attempt {message.retry_count})")
5954
- await self.queue.put((message.priority.value, message))
6021
+ # consistent 3-tuple on retry too
6022
+ self._counter += 1
6023
+ retry_counter = self._counter
6024
+ await self.queue.put(
6025
+ (message.priority.value, retry_counter, message)
6026
+ )
5955
6027
  else:
5956
6028
  self._dead_letter_message(message, e)
6029
+ # popped here.
5957
6030
  if message.id in self.pending:
5958
- self.pending[message.id].set_exception(e)
6031
+ future = self.pending.pop(message.id)
6032
+ if not future.done():
6033
+ future.set_exception(e)
5959
6034
  elif message.callback:
5960
6035
  message.callback(e)
5961
6036
  else:
5962
6037
  logger.warning(f"[-] No handler for message type: {message.type}")
5963
- self._dead_letter_message(message, Exception(f"No handler for {message.type}"))
5964
-
6038
+ self._dead_letter_message(
6039
+ message, Exception(f"[!] No handler for {message.type}")
6040
+ )
6041
+ # pop here too, unhandled message type leaked before
6042
+ if message.id in self.pending:
6043
+ future = self.pending.pop(message.id)
6044
+ if not future.done():
6045
+ future.set_exception(
6046
+ Exception(f"No handler for {message.type}")
6047
+ )
6048
+
5965
6049
  except asyncio.TimeoutError:
5966
6050
  continue
5967
6051
  except asyncio.CancelledError:
@@ -5970,62 +6054,60 @@ class AsyncMessageQueue:
5970
6054
  except Exception as e:
5971
6055
  logger.error(f"[-] Worker error: {e}\n{traceback.format_exc()}")
5972
6056
  await asyncio.sleep(0.1)
5973
-
6057
+
6058
+
5974
6059
  def _update_stats(self, latency: float, success: bool):
5975
- # Update queue statistics
5976
6060
  self._stats['messages_processed'] += 1
5977
6061
  if not success:
5978
6062
  self._stats['messages_failed'] += 1
5979
-
5980
- # Update moving average
5981
- alpha = 0.1 # Smoothing factor
5982
- self._stats['avg_latency'] = alpha * latency + (1 - alpha) * self._stats['avg_latency']
5983
-
6063
+
6064
+ alpha = self.alpha
6065
+ self._stats['avg_latency'] = (
6066
+ alpha * latency + (1 - alpha) * self._stats['avg_latency']
6067
+ )
6068
+
6069
+
5984
6070
  def _dead_letter_message(self, message: Message, error: Exception):
5985
- # Send failed message to dead letter queue
5986
6071
  self.dead_letter_queue.append({
5987
- 'message': message,
5988
- 'error': str(error),
5989
- 'timestamp': datetime.now(),
6072
+ 'message' : message,
6073
+ 'error' : str(error),
6074
+ 'timestamp' : datetime.now(),
5990
6075
  'retry_count': message.retry_count
5991
6076
  })
5992
- logger.error(f"[=] Message {message.id} sent to DLQ after {message.retry_count} retries")
5993
-
6077
+ logger.error(f"[=] Message {message.id} sent to DLQ after "
6078
+ f"{message.retry_count} retries")
6079
+
5994
6080
  def _handle_orphaned_message(self, message: Message):
5995
- # Handle orphaned messages (no pending future, no callback).
5996
6081
  logger.warning(f"[=] Orphaned message {message.id} of type {message.type}")
5997
- # Could store for manual inspection
5998
6082
  self.dead_letter_queue.append({
5999
- 'message': message,
6000
- 'error': 'Orphaned message - no handler or callback',
6083
+ 'message' : message,
6084
+ 'error' : 'Orphaned message - expired before processing',
6001
6085
  'timestamp': datetime.now()
6002
6086
  })
6003
-
6087
+ # orphaned messages with pending futures also leaked before
6088
+ if message.id in self.pending:
6089
+ future = self.pending.pop(message.id)
6090
+ if not future.done():
6091
+ future.set_exception(TimeoutError(f"Message {message.id} expired"))
6092
+
6004
6093
  def get_stats(self) -> Dict:
6005
- # Get queue statistics.
6006
6094
  return {
6007
6095
  **self._stats,
6008
6096
  'pending_count': len(self.pending),
6009
- 'queue_size': self.queue.qsize(),
6010
- 'dlq_size': len(self.dead_letter_queue),
6011
- 'is_running': self._running
6097
+ 'queue_size' : self.queue.qsize(),
6098
+ 'dlq_size' : len(self.dead_letter_queue),
6099
+ 'is_running' : self._running
6012
6100
  }
6013
-
6014
6101
 
6015
-
6016
6102
  async def start(self):
6017
- # Start the async worker (async version).
6018
- if self._running:
6019
- logger.warning("[=] Queue already running")
6020
- return
6021
-
6022
- self._running = True
6023
- self._worker_task = asyncio.create_task(self._worker())
6024
- logger.info("[=] Async message queue started")
6025
-
6103
+ """delegates to _ensure_started, single code path."""
6104
+ try:
6105
+ await self._ensure_started()
6106
+ except Exception as e:
6107
+ print(f'[!] Workers failed to start: {e}')
6108
+
6026
6109
 
6027
6110
  async def stop(self, timeout: float = 5.0):
6028
- # Stop the message queue worker gracefully.
6029
6111
  logger.info("[=] Stopping message queue...")
6030
6112
  self._running = False
6031
6113
  if self._worker_task:
@@ -6034,16 +6116,24 @@ class AsyncMessageQueue:
6034
6116
  except asyncio.TimeoutError:
6035
6117
  self._worker_task.cancel()
6036
6118
  logger.warning("[=] Worker task did not stop gracefully")
6119
+
6120
+ # clean up any still-pending futures on shutdown
6121
+ for msg_id, future in list(self.pending.items()):
6122
+ if not future.done():
6123
+ future.set_exception(RuntimeError("Queue stopped"))
6124
+ self.pending.clear()
6125
+
6037
6126
  logger.info("[=] Async message queue stopped")
6038
-
6127
+
6039
6128
  def get_dead_letter_queue(self) -> List[Dict]:
6040
- # Get copy of dead letter queue for inspection.
6041
- return list(self.dead_letter_queue)
6129
+ if self.dead_letter_queue is not None:
6130
+ return list(self.dead_letter_queue)
6131
+ else:
6132
+ return []
6042
6133
 
6043
6134
 
6044
6135
  class ThreadedMessageQueue:
6045
6136
  # Thread-based message queue for synchronous code
6046
-
6047
6137
  def __init__(self, max_size=1000, worker_threads=4):
6048
6138
  self.queue = queue.Queue(maxsize=max_size)
6049
6139
  self.results = {}
@@ -6077,9 +6167,9 @@ class ThreadedMessageQueue:
6077
6167
  start_time = time.time()
6078
6168
  while not result_container['ready'] and (time.time() - start_time) < timeout:
6079
6169
  time.sleep(0.01)
6080
-
6170
+
6081
6171
  if not result_container['ready']:
6082
- raise TimeoutError(f"Message {message.id} timed out")
6172
+ raise TimeoutError(f"[!] Message {message.id} timed out")
6083
6173
 
6084
6174
  if result_container['error']:
6085
6175
  raise result_container['error']
@@ -8023,13 +8113,17 @@ class AgentDistributedInference:
8023
8113
 
8024
8114
 
8025
8115
  def process_peer_request(self, probs, target_preds, attn_weights, input_ids):
8026
- try:
8027
- response_probs = self.pipeline._calibrate_probs(probs, target_preds, attn_weights, input_ids)
8028
- return response_probs
8029
- except Exception as e:
8030
- print(f"[-] Error in peer request_processing: {e}")
8031
- traceback.print_exc()
8116
+ if probs is not None and target_preds is not None and attn_weights is not None and input_ids is not None:
8117
+ try:
8118
+ response_probs = self.pipeline._calibrate_probs(probs, target_preds, attn_weights, input_ids)
8119
+ return response_probs
8120
+ except Exception as e:
8121
+ print(f"[-] Error in peer request_processing: {e}")
8122
+ return probs
8123
+ else:
8124
+ print('[=] Cannot process peer request due to incomplete Missing samples, returning regular probs!')
8032
8125
  return probs
8126
+
8033
8127
 
8034
8128
 
8035
8129
  # ============ REQUEST HANDLERS ============
@@ -10309,6 +10403,8 @@ class IntegratedPipeline:
10309
10403
  except (TypeError, ValueError):
10310
10404
  return default
10311
10405
 
10406
+
10407
+
10312
10408
  def _batch_prediction_core(self, batch_input_ids: np.ndarray, batch_X: np.ndarray,
10313
10409
  batch_size: Any = None, show_progress: bool = True) -> np.ndarray:
10314
10410
  """
@@ -10418,7 +10514,7 @@ class IntegratedPipeline:
10418
10514
  batch_probs[start_idx:end_idx] = 0
10419
10515
 
10420
10516
  if show_progress:
10421
- print(f"\r✅ Batch complete: {n_samples} samples processed ")
10517
+ print(f"\r✅ Batch complete: {n_samples} samples processed")
10422
10518
 
10423
10519
  return batch_probs if batch_probs is not None else np.array([])
10424
10520
 
@@ -16343,8 +16439,8 @@ class PipelinePredictionManager:
16343
16439
  # error_rate=0.5 → multiplier≈0.67
16344
16440
  # error_rate=1.0 → multiplier≈0.5
16345
16441
  reputation = 1.0 / (1.0 + error_rate)
16346
- if c < len(prob):
16347
- prob[c] *= reputation
16442
+ if c < len(final_probs):
16443
+ final_probs[c] *= reputation
16348
16444
  else:
16349
16445
  self.pred_counts = np.zeros(n_classes, dtype=np.float64)
16350
16446
  self.pred_counts[predicted_index] += 1.0
@@ -16357,8 +16453,8 @@ class PipelinePredictionManager:
16357
16453
  # error_rate=0.5 → multiplier≈0.67
16358
16454
  # error_rate=1.0 → multiplier≈0.5
16359
16455
  reputation = 1.0 / (1.0 + error_rate)
16360
- if c < len(prob):
16361
- prob[c] *= reputation
16456
+ if c < len(final_probs):
16457
+ final_probs[c] *= reputation
16362
16458
 
16363
16459
 
16364
16460
  prob_sum = final_probs.sum()