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.
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.egg-info/PKG-INFO +3 -10
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.py +230 -134
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractOptimizedModules.c +2175 -1230
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractOptimizedModules.pyx +55 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/PKG-INFO +3 -10
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/README.md +2 -9
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/setup.py +1 -1
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/MANIFEST.in +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/Cargo.toml +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/pyproject.toml +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/src/lib.rs +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
- {abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/pyproject.toml +0 -0
- {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
|
+
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.
|
|
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
|
-
|
|
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
|
|
{abstractintegratedmodule-0.9.3 → abstractintegratedmodule-0.9.4}/AbstractIntegratedModule.py
RENAMED
|
@@ -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
|
-
|
|
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
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
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
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
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.
|
|
5827
|
-
|
|
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
|
-
"""
|
|
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
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
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.
|
|
5944
|
+
logger.debug(f"[=] Publishing message {message.id} type={message.type} "
|
|
5945
|
+
f"priority={message.priority.name}")
|
|
5882
5946
|
|
|
5883
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
5964
|
+
"""Fire and forget, uses consistent 3-tuple format."""
|
|
5965
|
+
await self._ensure_started()
|
|
5910
5966
|
message.callback = callback
|
|
5911
|
-
|
|
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(
|
|
5918
|
-
|
|
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
|
-
#
|
|
6004
|
+
|
|
6005
|
+
# pop from pending on success, was leaking before
|
|
5941
6006
|
if message.id in self.pending:
|
|
5942
|
-
self.pending
|
|
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
|
|
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
|
-
|
|
5954
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
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
|
|
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 -
|
|
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
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
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
|
-
|
|
6041
|
-
|
|
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
|
-
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
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(
|
|
16347
|
-
|
|
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(
|
|
16361
|
-
|
|
16456
|
+
if c < len(final_probs):
|
|
16457
|
+
final_probs[c] *= reputation
|
|
16362
16458
|
|
|
16363
16459
|
|
|
16364
16460
|
prob_sum = final_probs.sum()
|