AbstractIntegratedModule 0.9.8__tar.gz → 1.0.0__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.8 → abstractintegratedmodule-1.0.0}/AbstractIntegratedModule.egg-info/PKG-INFO +2 -2
  2. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/AbstractIntegratedModule.py +286 -59
  3. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/AbstractOptimizedModules.c +2275 -755
  4. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/AbstractOptimizedModules.pyx +78 -0
  5. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/PKG-INFO +2 -2
  6. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/README.md +1 -1
  7. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/setup.py +1 -1
  8. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
  9. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
  10. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
  11. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
  12. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/MANIFEST.in +0 -0
  13. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/abstract_model_storage/Cargo.toml +0 -0
  14. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/abstract_model_storage/pyproject.toml +0 -0
  15. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/abstract_model_storage/src/lib.rs +0 -0
  16. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
  17. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
  18. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
  19. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
  20. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
  21. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
  22. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/pyproject.toml +0 -0
  23. {abstractintegratedmodule-0.9.8 → abstractintegratedmodule-1.0.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbstractIntegratedModule
3
- Version: 0.9.8
3
+ Version: 1.0.0
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.8 Official Release.
45
+ - Development Stage: 1.0.0 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.
@@ -43,6 +43,7 @@ import psutil
43
43
  from sklearn.preprocessing import StandardScaler
44
44
  import io
45
45
  import concurrent.futures
46
+ import struct
46
47
 
47
48
  from cryptography import x509
48
49
  from cryptography.x509.oid import NameOID
@@ -65,6 +66,7 @@ try:
65
66
  optimized_dynamic_weighted_ensemble,
66
67
  optimized_qkv_weight_grad,
67
68
  optimized_qkv_input_grad,
69
+ optimized_lstm_cell_backward,
68
70
  )
69
71
  _OPT_AVAILABLE = True
70
72
  print('[=] Cython acceleration loaded ✅')
@@ -240,7 +242,7 @@ class Message:
240
242
  return not self.__lt__(other)
241
243
 
242
244
  def __hash__(self):
243
- """Make Message hashable (useful for sets/dicts)."""
245
+ """Message hashable"""
244
246
  return hash(self.id)
245
247
 
246
248
 
@@ -1926,11 +1928,19 @@ class MLP:
1926
1928
  self.pred_counts = pred_counts
1927
1929
  self.error_decay = decay
1928
1930
 
1931
+ if prob is None:
1932
+ print('[!] Probabilities is None! returning the probabilities...')
1933
+ return prob
1934
+
1929
1935
  try:
1930
1936
  self.pred_counts[predicted_index] += 1.0
1931
1937
  n_classes = len(label_map)
1932
1938
 
1933
1939
  self.pred_counts = self.pred_counts[0] if isinstance(self.pred_counts[0], np.ndarray) and self.pred_counts.ndim > 1 else self.pred_counts
1940
+ if len(self.pred_counts) != n_classes:
1941
+ self.pred_counts = np.zeros(n_classes, dtype=np.float64)
1942
+ self.pred_counts[predicted_index] += 1.0
1943
+ self.pred_counts = self.pred_counts[0] if isinstance(self.pred_counts[0], np.ndarray) and self.pred_counts.ndim > 1 else self.pred_counts
1934
1944
 
1935
1945
  for c in range(n_classes):
1936
1946
  if len(self.pred_counts) < c:
@@ -1941,19 +1951,12 @@ class MLP:
1941
1951
  # error_rate=0.5 → multiplier≈0.67
1942
1952
  # error_rate=1.0 → multiplier≈0.5
1943
1953
  reputation = 1.0 / (1.0 + error_rate)
1944
- if c < len(prob):
1945
- prob[c] *= reputation
1946
- else:
1947
- self.pred_counts = np.zeros(n_classes, dtype=np.float64)
1948
- self.pred_counts[predicted_index] += 1.0
1949
- self.pred_counts = self.pred_counts[0] if isinstance(self.pred_counts[0], np.ndarray) and self.pred_counts.ndim > 1 else self.pred_counts
1954
+ if isinstance(error_rate, (list, np.ndarray)):
1955
+ if len(error_rate) != len(prob):
1956
+ reputation = 1.0 / (1.0 + np.mean(error_rate))
1950
1957
 
1951
- if isinstance(self.pred_counts[c], (int, float)) and self.pred_counts[c] > 0:
1952
- error_rate = self.error_counts[c] / (self.pred_counts[c] + 1e-8)
1953
-
1954
- reputation = 1.0 / (1.0 + error_rate)
1955
1958
  if c < len(prob):
1956
- prob[c] *= reputation
1959
+ prob[c] *= reputation
1957
1960
 
1958
1961
  prob_sum = prob.sum()
1959
1962
  if prob_sum > 1e-8:
@@ -2195,15 +2198,27 @@ class LSTMCell:
2195
2198
  # ________ backward method for Cell class __________
2196
2199
  def backward(self, dhs: np.ndarray, cache,
2197
2200
  dh_next=None, dc_next=None, T_limit=None):
2198
- # T_limit avoids slicing cache list externally
2201
+ # T_limit avoids slicing cache list externally Later.
2199
2202
  T = T_limit if T_limit is not None else len(cache)
2200
2203
  H = self.hidden_size
2201
2204
 
2202
2205
  dW = np.zeros_like(self.W)
2203
2206
  db = np.zeros_like(self.b)
2204
- dh = np.zeros(H) if dh_next is None else dh_next.copy()
2205
- dc = np.zeros(H) if dc_next is None else dc_next.copy()
2207
+ dh = np.zeros(H) if dh_next is None else np.ascontiguousarray(dh_next.copy())
2208
+ dc = np.zeros(H) if dc_next is None else np.ascontiguousarray(dc_next.copy())
2206
2209
  dx_seq = np.zeros((T, self.input_size))
2210
+ if _OPT_AVAILABLE:
2211
+ grads, dx_seq, dh, dc = optimized_lstm_cell_backward(
2212
+ np.ascontiguousarray(dhs, dtype=np.float64),
2213
+ cache,
2214
+ np.ascontiguousarray(self.W, dtype=np.float64),
2215
+ self.input_size,
2216
+ self.hidden_size,
2217
+ dh,
2218
+ dc,
2219
+ T
2220
+ )
2221
+ return grads, dx_seq, dh, dc
2207
2222
 
2208
2223
  # preallocate dz buffer once
2209
2224
  dz = np.empty(4 * H)
@@ -2960,7 +2975,7 @@ class WeightedEnsemblePredictor:
2960
2975
  target_class = int(np.clip(round(raw_score), 0, n_classes - 1))
2961
2976
  logits[target_class] = mc_conf_last
2962
2977
 
2963
- # softmax
2978
+ # softmax here
2964
2979
  logits -= logits.max()
2965
2980
  row = np.exp(logits)
2966
2981
  row /= row.sum()
@@ -3834,7 +3849,7 @@ class ExplainabilityModule:
3834
3849
  self.pipeline.focused_mlp.train(X, y_onehot, epochs=1000, lr=self.pipeline.mlp.lr, verbose=True)
3835
3850
  time.sleep(5)
3836
3851
 
3837
- self.pipeline.mlp.lr = old_lr # Restore LR
3852
+ self.pipeline.mlp.lr = old_lr # Restore old LR
3838
3853
 
3839
3854
  # 4. train transformer for efficient processing later tho.
3840
3855
  if self.pipeline.model2:
@@ -4545,6 +4560,7 @@ class ModelStorage:
4545
4560
  self.setup_agent_table()
4546
4561
  self.setup_node_table()
4547
4562
  self.setup_weight_table()
4563
+ self.setup_accurate_cache_table()
4548
4564
 
4549
4565
  self.memory_name = memory_name
4550
4566
 
@@ -4612,7 +4628,6 @@ class ModelStorage:
4612
4628
  conn.close()
4613
4629
 
4614
4630
 
4615
-
4616
4631
  def setup_storage_table(self):
4617
4632
  try:
4618
4633
  try:
@@ -4663,6 +4678,7 @@ class ModelStorage:
4663
4678
  conn.commit()
4664
4679
  conn.close()
4665
4680
 
4681
+
4666
4682
  def get_database_path(self):
4667
4683
  db_filename= self.db_path
4668
4684
  if getattr(sys, 'frozen', False):
@@ -4678,6 +4694,7 @@ class ModelStorage:
4678
4694
 
4679
4695
  return db_path
4680
4696
 
4697
+
4681
4698
  def setup_explainable_table(self):
4682
4699
  try:
4683
4700
  try:
@@ -4856,6 +4873,83 @@ class ModelStorage:
4856
4873
  conn.commit()
4857
4874
  conn.close()
4858
4875
 
4876
+
4877
+ def setup_accurate_cache_table(self):
4878
+ try:
4879
+ try:
4880
+ db_path = self.get_database_path()
4881
+ conn = sqlite3.connect(db_path)
4882
+ except:
4883
+ conn = sqlite3.connect(self.db_path)
4884
+
4885
+ c = conn.cursor()
4886
+
4887
+ c.execute('''CREATE TABLE IF NOT EXISTS accurate_cache_storage
4888
+ (id INTEGER PRIMARY KEY AUTOINCREMENT,
4889
+ memory_name TEXT,
4890
+ cache TEXT,
4891
+ is_active INTEGER DEFAULT 0,
4892
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
4893
+
4894
+ conn.commit()
4895
+ conn.close()
4896
+ print('|| Update cached to database! ')
4897
+
4898
+ except Exception as e:
4899
+ print(f'|| Cant Update Database: {e}')
4900
+ filepath = input('|| Insert Database filepath: ')
4901
+ if filepath:
4902
+ conn = sqlite3.connect(filepath)
4903
+ else:
4904
+ print('|| Skipping Database Modification...')
4905
+ pass
4906
+ c = conn.cursor()
4907
+ c.execute('''CREATE TABLE IF NOT EXISTS accurate_cache_storage
4908
+ (id INTEGER PRIMARY KEY AUTOINCREMENT,
4909
+ memory_name TEXT,
4910
+ cache TEXT,
4911
+ is_active INTEGER DEFAULT 0,
4912
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
4913
+
4914
+
4915
+ conn.commit()
4916
+ conn.close()
4917
+
4918
+ def save_accurate_cache_dict(self, memory_name, payload, model_type='Pipeline'):
4919
+ try:
4920
+ db_path = self.get_database_path()
4921
+ conn = sqlite3.connect(db_path)
4922
+ except:
4923
+ conn = sqlite3.connect(self.db_path)
4924
+
4925
+ c = conn.cursor()
4926
+
4927
+ cache = json.dumps(payload, default=str)
4928
+
4929
+ try:
4930
+ c.execute("""
4931
+ INSERT INTO accurate_cache_storage
4932
+ (memory_name, cache, is_active)
4933
+ VALUES (?, ?, ?)
4934
+ """, (memory_name, cache, 1))
4935
+
4936
+ c.execute("""
4937
+ UPDATE accurate_cache_storage
4938
+ SET is_active = 0
4939
+ WHERE memory_name = ? AND id != last_insert_rowid()
4940
+ """, (memory_name,))
4941
+
4942
+ conn.commit()
4943
+ conn.close()
4944
+
4945
+ print('|| Accurate cache saved!')
4946
+
4947
+ except Exception as e:
4948
+ print(f'[-] Cant save accurate cache memory due to: {e}')
4949
+ pass
4950
+
4951
+
4952
+
4859
4953
  def save_model_dict(self, memory_name, model_dict, type=None, model_type='mlp'):
4860
4954
  try:
4861
4955
  db_path = self.get_database_path()
@@ -4928,6 +5022,8 @@ class ModelStorage:
4928
5022
  if isinstance(obj, (list, tuple)):
4929
5023
  return [self._prepare_for_serialization(item) for item in obj]
4930
5024
  return obj
5025
+
5026
+
4931
5027
 
4932
5028
  def load_model_dict(self, memory_name):
4933
5029
  conn = None
@@ -5293,6 +5389,31 @@ class ModelStorage:
5293
5389
  return result
5294
5390
 
5295
5391
 
5392
+ def load_accurate_cache(self, memory_name):
5393
+ try:
5394
+ try:
5395
+ db_path = self.get_database_path()
5396
+ conn = sqlite3.connect(db_path)
5397
+ except:
5398
+ conn = sqlite3.connect(self.db_path)
5399
+
5400
+ c = conn.cursor()
5401
+
5402
+ c.execute("""
5403
+ SELECT weights FROM accurate_cache_storage
5404
+ WHERE memory_name = ? AND is_active = 1
5405
+ """, (memory_name,))
5406
+
5407
+ result = c.fetchone()
5408
+ conn.close()
5409
+
5410
+ if result:
5411
+ return json.loads(result[0])
5412
+ except Exception as e:
5413
+ print(f'[!] Error handling cache dict: {e}')
5414
+ return None
5415
+
5416
+
5296
5417
  def _load_weights(self, memory_name, type=None):
5297
5418
  try:
5298
5419
  try:
@@ -5716,6 +5837,7 @@ class ModelStorage:
5716
5837
  print(f'[-] Cant save model memory due to: {e}')
5717
5838
  pass
5718
5839
 
5840
+
5719
5841
  def load_peer_request_dict(self, memory_name, agent_id):
5720
5842
  print(f'|| Peer request with Agent')
5721
5843
  try:
@@ -5860,6 +5982,17 @@ class ModelStorage:
5860
5982
  exists = result is not None
5861
5983
  print(f"|| Retrieved Peer Memory: {memory_name}")
5862
5984
 
5985
+ elif type == 'Accurate-Cache':
5986
+ c = conn.cursor()
5987
+
5988
+ c.execute("""
5989
+ SELECT 1 FROM accurate_cache_storage
5990
+ WHERE memory_name = ? and is_active = 1
5991
+ LIMIT 1""", (memory_name, ))
5992
+
5993
+ result = c.fetchone()
5994
+ exists = result is not None
5995
+ print(f"|| Retrieved Accurate Fact Cache Memory for memory: {memory_name}")
5863
5996
  else:
5864
5997
  c = conn.cursor()
5865
5998
 
@@ -8844,7 +8977,7 @@ class AutoBatcherAutomation:
8844
8977
  self.result_events = {} # per-request Event
8845
8978
  self.next_id = 0
8846
8979
 
8847
- self._state_lock = threading.Lock() # FIX 1 — guards processing flag + next_id
8980
+ self._state_lock = threading.Lock() # guards processing flag + next_id
8848
8981
 
8849
8982
  def add_request(self, text, callback=None):
8850
8983
  with self._state_lock:
@@ -8869,6 +9002,9 @@ class AutoBatcherAutomation:
8869
9002
  if should_start:
8870
9003
  self._start_processing()
8871
9004
 
9005
+ result = self.get_result(request_id)
9006
+ self.cleanup_stale()
9007
+
8872
9008
  return request_id
8873
9009
 
8874
9010
  def _start_processing(self):
@@ -9014,6 +9150,8 @@ class IntegratedPipeline:
9014
9150
  self.shared_auth_token = shared_auth_token
9015
9151
  self.manager = None
9016
9152
 
9153
+ self.memory_name = memory_name
9154
+
9017
9155
  self.client_ssl_context = None
9018
9156
  self.ssl_context = None
9019
9157
 
@@ -9021,11 +9159,13 @@ class IntegratedPipeline:
9021
9159
  self.hidden = 32
9022
9160
  self.output_size = 1
9023
9161
  self.dropout_rate = 0.1
9024
- self.transformer_training_epochs = 100
9025
9162
  self.max_size = 500
9026
9163
  self.error_decay = 0.85
9027
9164
  self.performance_result = 1.0
9028
9165
 
9166
+ self.mlp_training_epochs = 2000
9167
+ self.transformer_training_epochs = 100
9168
+
9029
9169
  # Main component setup
9030
9170
  self.standard_scaler = StandardScaler()
9031
9171
  self.tfidf = TfidfVectorizer(max_features=70)
@@ -10891,7 +11031,11 @@ class IntegratedPipeline:
10891
11031
 
10892
11032
  def predict_async(self, text, callback=None):
10893
11033
  try:
10894
- return self.batcher.add_request(text, callback)
11034
+ id_req = self.batcher.add_request(text, callback)
11035
+ result = self.batcher.get_result(id_req, timeout=10)
11036
+ self.batcher.cleanup_stale()
11037
+ return id_req
11038
+
10895
11039
  except Exception as e:
10896
11040
  print(f'[=] error in automatic batcher: {e}')
10897
11041
  return None
@@ -12624,7 +12768,7 @@ class IntegratedPipeline:
12624
12768
 
12625
12769
  self.lstm_setup_inference(X, y)
12626
12770
  self.initialize_model_(X, input_dim, n_classes)
12627
- self.model3.train(X, y, epochs=1000, lr=0.1)
12771
+ self.model3.train(X, y, epochs=self.mlp_training_epochs, lr=0.1)
12628
12772
 
12629
12773
  if self.lstm_engine:
12630
12774
  self.storage.save_weights(self.memory_name, model_type='Pipeline')
@@ -12696,11 +12840,17 @@ class IntegratedPipeline:
12696
12840
  class AccurateAnswerCache:
12697
12841
  def __init__(self, pipeline, similarity_threshold=0.85, max_size=500):
12698
12842
  self.pipeline = pipeline
12843
+ self.memory_name = self.pipeline.memory_name
12699
12844
  self.similarity_threshold = similarity_threshold
12700
12845
  self.max_size = max_size
12701
12846
  self.max_threshold = 0.7
12702
12847
 
12703
- self.cache = {}
12848
+ self.memory_exist = self.pipeline.storage.memory_exists(self.memory_name, type='Accurate-cache')
12849
+ if self.memory_exist:
12850
+ self.cache = self.pipeline.storage.load_accurate_cache(self.memory_name)
12851
+ else:
12852
+ self.cache = {}
12853
+
12704
12854
  self.exact_hash_index = {} # O(1) exact match lookup
12705
12855
 
12706
12856
  def _flatten_indices(self, input_ids):
@@ -12780,9 +12930,14 @@ class AccurateAnswerCache:
12780
12930
  self._evict_lru()
12781
12931
 
12782
12932
  print(f'[💎] Verified answer cached: {prediction} (source={source})')
12933
+
12934
+ if self.cache[key]['source'] != 'automatic_verified' and not source.startswith('automatic'):
12935
+ self.pipeline.storage.save_accurate_cache_dict(self.memory_name, self.cache)
12936
+
12783
12937
  except Exception as e:
12784
12938
  print(f'[!] Failed to add samples and answer to Answer cache due to: {e}')
12785
12939
 
12940
+
12786
12941
  def lookup(self, x_mlp, input_ids=None):
12787
12942
  try:
12788
12943
  confidence_threshold = self.pipeline.confidence_threshold
@@ -12805,7 +12960,21 @@ class AccurateAnswerCache:
12805
12960
  if ids_hash in self.exact_hash_index:
12806
12961
  key = self.exact_hash_index[ids_hash]
12807
12962
  entry = self.cache.get(key)
12808
- if entry:
12963
+ if isinstance(entry, dict) and entry['source'] != 'automatic_verified' and not entry['source'].startswith('automatic'):
12964
+
12965
+ entry['hit_count'] += 1
12966
+ entry['last_hit'] = datetime.now().isoformat()
12967
+ return {
12968
+ 'prediction' : entry['prediction'],
12969
+ 'confidence' : entry['confidence'],
12970
+ 'index' : entry['index'],
12971
+ 'similarity' : 1.0,
12972
+ 'source' : entry['source'],
12973
+ 'hit_count' : entry['hit_count'],
12974
+ 'match_type' : 'exact_ids'
12975
+ }
12976
+
12977
+ if entry is not None and isinstance(entry, dict):
12809
12978
  entry['hit_count'] += 1
12810
12979
  entry['last_hit'] = datetime.now().isoformat()
12811
12980
  return {
@@ -15359,6 +15528,7 @@ class PipelinePredictionManager:
15359
15528
  use_embedded = False
15360
15529
  attn_weights = None
15361
15530
  trans_probs = None
15531
+ mlp_probs = None
15362
15532
 
15363
15533
  print(f"\n[🚀] Regular Prediction Initiated...")
15364
15534
  self.pipeline.titles = titles
@@ -15395,6 +15565,9 @@ class PipelinePredictionManager:
15395
15565
  if self.pipeline.cache and 'label_bins' in self.pipeline.cache:
15396
15566
  print('[=] label_bins cache found!')
15397
15567
  label_bins = self.pipeline.cache['label_bins']
15568
+ if X_gen is None:
15569
+ X_gen = X
15570
+
15398
15571
  lstm_probs, _ = self.pipeline.ensemble._get_lstm_probs(input_ids, X_gen, label_bins=label_bins)
15399
15572
  else:
15400
15573
  lstm_probs = None
@@ -15590,6 +15763,9 @@ class PipelinePredictionManager:
15590
15763
  n_samples = mlp_probs.shape[0]
15591
15764
 
15592
15765
  lstm_pred_indices = np.argmax(lstm_probs, axis=1) if lstm_probs is not None else None
15766
+ if mlp_probs is None:
15767
+ logits = self.pipeline.mlp.forward(X) if X is not None else X_tfidf
15768
+ mlp_probs = self.pipeline._softmax(logits)
15593
15769
 
15594
15770
  target_probs = self.calibration_penalized_check(mlp_probs, mlp_pred_indices)
15595
15771
  target_pred_indices = np.argmax(target_probs, axis=1)
@@ -15997,21 +16173,38 @@ class PipelinePredictionManager:
15997
16173
  is_valid_index = 0 <= mlp_class_idx < num_classes
15998
16174
 
15999
16175
  if not is_valid_index:
16000
- return {
16001
- 'predicted' : None,
16002
- 'confidence' : 0.0,
16003
- 'mlp_class' : None,
16004
- 'is_valid' : False,
16005
- 'error' : f'class_index_out_of_range(idx={mlp_class_idx}, num_classes={num_classes})'
16006
- }
16007
-
16008
- mlp_confidence = float(mlp_probs[i][mlp_class_idx])
16176
+ try:
16177
+ mlp_class_idx = int(np.argmax(mlp_probs[:len(reverse_map)-1]))
16178
+ mlp_confidence = float(mlp_probs[:len(reverse_map)-1][mlp_class_idx])
16179
+ mlp_label = reverse_map.get(mlp_class_idx, f"unknown_{mlp_class_idx}")
16180
+ return {
16181
+ 'predicted' : mlp_label,
16182
+ 'confidence' : mlp_confidence,
16183
+ 'mlp_class' : mlp_class_idx,
16184
+ 'index' : mlp_class_idx,
16185
+ 'models_agree': models_agree,
16186
+ 'final_probs' : mlp_probs[:len(reverse_map)-1],
16187
+ 'is_valid' : True,
16188
+ }
16189
+ except:
16190
+ return {
16191
+ 'predicted' : None,
16192
+ 'confidence' : 0.0,
16193
+ 'mlp_class' : 0,
16194
+ 'index' : 0,
16195
+ 'models_agree': False,
16196
+ 'final_probs' : mlp_probs,
16197
+ 'is_valid' : False,
16198
+ 'error' : f'class_index_out_of_range(idx={mlp_class_idx}, num_classes={num_classes})'
16199
+ }
16200
+
16201
+ mlp_confidence = float(mlp_probs[i][mlp_class_idx])
16009
16202
  mlp_label = reverse_map.get(mlp_class_idx, f"unknown_{mlp_class_idx}")
16010
16203
 
16011
16204
  # ── LSTM ────────────────────────────────────────────
16012
16205
  # lstm_pred_indices passed in precomputed, not recomputed per-sample
16013
16206
  if lstm_probs is not None and lstm_pred_indices is not None:
16014
- lstm_class_idx = int(lstm_pred_indices[i])
16207
+ lstm_class_idx = int(lstm_pred_indices[i])
16015
16208
  lstm_confidence = float(lstm_probs[i][lstm_class_idx])
16016
16209
  else:
16017
16210
  # explicit default so lstm_class_idx is always defined, even if LSTM is not used
@@ -16128,7 +16321,7 @@ class PipelinePredictionManager:
16128
16321
  return {
16129
16322
  'is_valid' : True,
16130
16323
  'predicted' : reverse_map.get(final_class_idx, f"unknown_{final_class_idx}"),
16131
- 'predicted_idx' : final_class_idx,
16324
+ 'index' : final_class_idx,
16132
16325
  'confidence' : float(final_confidence),
16133
16326
  'mlp_class' : mlp_class_idx,
16134
16327
  'mlp_prediction' : mlp_label,
@@ -16621,9 +16814,37 @@ class PipelinePredictionManager:
16621
16814
  verbose = False
16622
16815
  if float(results[0]['confidence']) < self.pipeline.confidence_threshold:
16623
16816
  verbose = True
16624
-
16625
- chosen_label = results[0]['predicted'] if results else None
16626
- confidence = results[0]['confidence'] if results else None
16817
+
16818
+ chosen_label = results[0].get('predicted') if results else None
16819
+ confidence = results[0].get('confidence') if results else 0.0
16820
+ if chosen_label is None:
16821
+ for i in range(num_classes):
16822
+ mlp_class_idx = int(mlp_pred_indices[i])
16823
+ is_valid_index = 0 <= mlp_class_idx < num_classes
16824
+
16825
+ if mlp_class_idx > len(reverse_map) or not is_valid_index:
16826
+ mlp_class_idx = int(np.argmax(mlp_probs[:len(reverse_map)-1]))
16827
+ print(f"[⚠️] Clamping index {mlp_class_idx} → {mlp_class_idx}")
16828
+
16829
+ if is_valid_index:
16830
+ mlp_confidence = float(mlp_probs[i][mlp_class_idx])
16831
+ else:
16832
+ mlp_confidence = float(mlp_probs[:len(reverse_map)-1][mlp_class_idx])
16833
+
16834
+ mlp_label = reverse_map.get(mlp_class_idx, f"unknown_{mlp_class_idx}")
16835
+ results.append({
16836
+ 'predicted' : mlp_label,
16837
+ 'confidence' : mlp_confidence,
16838
+ 'mlp_class' : mlp_class_idx,
16839
+ 'index' : mlp_class_idx,
16840
+ 'models_agree': False,
16841
+ 'final_probs' : mlp_probs,
16842
+ 'is_valid' : True,
16843
+ })
16844
+
16845
+ chosen_label = results[0].get('predicted') if results else None
16846
+ confidence = results[0].get('confidence') if results else None
16847
+
16627
16848
  if isinstance(chosen_label, int) or isinstance(chosen_label, np.integer):
16628
16849
  chosen_label = str(chosen_label)
16629
16850
 
@@ -16654,6 +16875,9 @@ class PipelinePredictionManager:
16654
16875
  final_idx = int(final_idx)
16655
16876
 
16656
16877
  chosen_label = reverse_map.get(final_idx, f"unknown_{final_idx}")
16878
+ if chosen_label.startswith('unknown'):
16879
+ final_idx = int(np.argmax(final_probs[:len(reverse_map)-1]))
16880
+
16657
16881
  try:
16658
16882
  print(final_probs)
16659
16883
  confidence = float(final_probs[final_idx])
@@ -16730,6 +16954,9 @@ class PipelinePredictionManager:
16730
16954
  except:
16731
16955
  confidence = float(final_probs[0][len(reverse_map)-1]) if isinstance(final_probs[0], (float, int)) else 0.0
16732
16956
  else:
16957
+ if final_probs is None:
16958
+ final_probs = mlp_probs
16959
+
16733
16960
  final_probs = self.calibration_penalized_check(final_probs, target_pred_indices[0])
16734
16961
 
16735
16962
  final_idx = final_probs[0].argmax() if final_probs is not None else target_probs[0].argmax()
@@ -16925,6 +17152,10 @@ class PipelinePredictionManager:
16925
17152
  self.error_counts *= decay
16926
17153
  self.pred_counts *= decay
16927
17154
 
17155
+ if final_probs is None:
17156
+ print('[!] Warning final probabilities is None! returning the probabilities...')
17157
+ return final_probs
17158
+
16928
17159
  try:
16929
17160
 
16930
17161
  self.pred_counts[predicted_index] += 1.0
@@ -16932,6 +17163,11 @@ class PipelinePredictionManager:
16932
17163
 
16933
17164
  self.pred_counts = self.pred_counts[0] if isinstance(self.pred_counts[0], np.ndarray) and self.pred_counts.ndim > 1 else self.pred_counts
16934
17165
 
17166
+ if len(self.pred_counts) != n_classes:
17167
+ self.pred_counts = np.zeros(n_classes, dtype=np.float64)
17168
+ self.pred_counts[predicted_index] += 1.0
17169
+ self.pred_counts = self.pred_counts[0] if isinstance(self.pred_counts[0], np.ndarray) and self.pred_counts.ndim > 1 else self.pred_counts
17170
+
16935
17171
  for c in range(n_classes):
16936
17172
  if len(self.pred_counts) < c:
16937
17173
  if isinstance(self.pred_counts[c], (int, float)) and self.pred_counts[c] > 0:
@@ -16941,24 +17177,13 @@ class PipelinePredictionManager:
16941
17177
  # error_rate=0.5 → multiplier≈0.67
16942
17178
  # error_rate=1.0 → multiplier≈0.5
16943
17179
  reputation = 1.0 / (1.0 + error_rate)
16944
- if c < len(final_probs):
16945
- final_probs[c] *= reputation
16946
- else:
16947
- self.pred_counts = np.zeros(n_classes, dtype=np.float64)
16948
- self.pred_counts[predicted_index] += 1.0
16949
- self.pred_counts = self.pred_counts[0] if isinstance(self.pred_counts[0], np.ndarray) and self.pred_counts.ndim > 1 else self.pred_counts
17180
+ if isinstance(error_rate, (list, np.ndarray)):
17181
+ if len(error_rate) != len(final_probs):
17182
+ reputation = 1.0 / (1.0 + np.mean(error_rate))
16950
17183
 
16951
- if isinstance(self.pred_counts[c], (int, float)) and self.pred_counts[c] > 0:
16952
- error_rate = self.error_counts[c] / (self.pred_counts[c] + 1e-8)
16953
- # sigmoid-shaped dampening — never goes negative
16954
- # error_rate=0.0 → multiplier=1.0 (no change)
16955
- # error_rate=0.5 → multiplier≈0.67
16956
- # error_rate=1.0 → multiplier≈0.5
16957
- reputation = 1.0 / (1.0 + error_rate)
16958
17184
  if c < len(final_probs):
16959
- final_probs[c] *= reputation
16960
-
16961
-
17185
+ final_probs[c] *= reputation
17186
+
16962
17187
  prob_sum = final_probs.sum()
16963
17188
  if prob_sum > 1e-8:
16964
17189
  final_probs /= prob_sum
@@ -16983,6 +17208,7 @@ class PipelinePredictionManager:
16983
17208
 
16984
17209
  correct = 0
16985
17210
  sec_correct = 0
17211
+ error = 0
16986
17212
  total_with_expected = 0
16987
17213
  X_samples, input_ids = payload['X_samples'], payload['input_ids']
16988
17214
 
@@ -17006,8 +17232,9 @@ class PipelinePredictionManager:
17006
17232
 
17007
17233
  sec_correct += 1
17008
17234
  else:
17009
- if isinstance(self.error_counts[predicted_index], (int, float)):
17010
- self.error_counts[predicted_index] += 1.0
17235
+ error += 1
17236
+ if isinstance(self.error_counts[result.get('index')], (int, float)):
17237
+ self.error_counts[result.get('index')] += 1.0
17011
17238
 
17012
17239
  if result['predicted'] == result['expected']:
17013
17240
  if result['sec_index'] is None:
@@ -17020,10 +17247,10 @@ class PipelinePredictionManager:
17020
17247
 
17021
17248
  correct += 1
17022
17249
  else:
17023
- if isinstance(self.error_counts[predicted_index], (int, float)):
17024
- self.error_counts[predicted_index] += 1.0
17250
+ error += 1
17251
+ if isinstance(self.error_counts[result.get('index')], (int, float)):
17252
+ self.error_counts[result.get('index')] += 1.0
17025
17253
 
17026
-
17027
17254
  # Agreement indicator
17028
17255
  agree_symbol = "✓" if result.get('models_agree', True) else "⚠️"
17029
17256
  print(f"[=] {agree_symbol} FINAL: {result['predicted']} ({result['confidence']:.1%})")