AbstractIntegratedModule 0.8.5__tar.gz → 0.8.7__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.8.5 → abstractintegratedmodule-0.8.7}/AbstractIntegratedModule.egg-info/PKG-INFO +5 -4
  2. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/AbstractIntegratedModule.py +219 -83
  3. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/AbstractOptimizedModules.c +200 -200
  4. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/PKG-INFO +5 -4
  5. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/README.md +4 -3
  6. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/setup.py +1 -1
  7. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
  8. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
  9. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
  10. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
  11. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/AbstractOptimizedModules.pyx +0 -0
  12. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/MANIFEST.in +0 -0
  13. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/abstract_model_storage/Cargo.toml +0 -0
  14. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/abstract_model_storage/pyproject.toml +0 -0
  15. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/abstract_model_storage/src/lib.rs +0 -0
  16. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
  17. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/abstract_model_storage/target/debug/build/serde_core-ebc15f2e9cad7f5f/out/private.rs +0 -0
  18. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
  19. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
  20. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/abstract_model_storage/target/release/build/serde_core-5cdb76131825e4af/out/private.rs +0 -0
  21. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
  22. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/pyproject.toml +0 -0
  23. {abstractintegratedmodule-0.8.5 → abstractintegratedmodule-0.8.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbstractIntegratedModule
3
- Version: 0.8.5
3
+ Version: 0.8.7
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.8.5 Official Release.
45
+ - Development Stage: 0.8.7 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.
@@ -80,9 +80,10 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
80
80
  - Transformer Optimized using Cython, to reduce Memory overhead and Reduce CPU Usage, With Reduced Training Time.
81
81
  ______________
82
82
  - Changelog:
83
- - v0.8.5:
83
+ - v0.8.7:
84
84
  - [=] New features:
85
- - Added new Optimization module using Cython maths for dynamic weighted ensemble function.
85
+ - Added new continuos predictive correction for MLP class to reduce inflated confidence despite outputting wrong answer.
86
+ - Reduced the frequency of penalty based calibration in advanced prediction method to prevent the models from being too conservative.
86
87
  - Fixed P2P Vulnerabilities:
87
88
  - Empty allowed_ips in PRODUCTION/HARDENED mode now DENIES external IPs instead of allowing all
88
89
  - Startup validation warns about dangerous config combinations
@@ -1716,7 +1716,11 @@ class MLP:
1716
1716
  self.layers2 = []
1717
1717
  self.lr = 0.1
1718
1718
  self.feed_layers = []
1719
-
1719
+
1720
+ self.error_counts = None
1721
+ self.pred_counts = None
1722
+ self.error_decay = None
1723
+
1720
1724
  self.softmax = SoftmaxOutput()
1721
1725
 
1722
1726
 
@@ -1735,7 +1739,7 @@ class MLP:
1735
1739
  def forward(self, x):
1736
1740
  for layer in self.layers:
1737
1741
  x = layer.forward(x)
1738
-
1742
+
1739
1743
  return self.softmax.forward(x)
1740
1744
 
1741
1745
 
@@ -1757,7 +1761,40 @@ class MLP:
1757
1761
 
1758
1762
  return calibration
1759
1763
 
1764
+ def continuos_predictive_correction(self, manager, prob, predicted_index):
1765
+ eps = 1e-5
1760
1766
 
1767
+ error_counts = manager.error_counts
1768
+ pred_counts = manager.pred_counts
1769
+ decay = manager.decay
1770
+ label_map = manager.label_map
1771
+
1772
+ self.error_counts = error_counts
1773
+ self.pred_counts = pred_counts
1774
+ self.error_decay = decay
1775
+
1776
+ try:
1777
+ self.pred_counts[predicted_index] += 1.0
1778
+ n_classes = len(label_map)
1779
+
1780
+ for c in range(n_classes):
1781
+ if self.pred_counts[c] > 0:
1782
+ error_rate = self.error_counts[c] / (self.pred_counts[c] + 1e-8)
1783
+ # sigmoid-shaped dampening — never goes negative
1784
+ # error_rate=0.0 → multiplier=1.0 (no change)
1785
+ # error_rate=0.5 → multiplier≈0.67
1786
+ # error_rate=1.0 → multiplier≈0.5
1787
+ reputation = 1.0 / (1.0 + error_rate)
1788
+ prob[c] *= reputation
1789
+
1790
+ prob_sum = prob.sum()
1791
+ if prob_sum > 1e-8:
1792
+ prob /= prob_sum
1793
+
1794
+ except Exception as e:
1795
+ print(f'[!] Cant check and calibrate probs based on penalty due to: {e}')
1796
+
1797
+ return prob
1761
1798
 
1762
1799
 
1763
1800
  def focused_backward(self, grad, lr, AME, anisotropy):
@@ -2893,7 +2930,7 @@ class WeightedEnsemblePredictor:
2893
2930
  AME = self.pipeline.model2.AME_Encoder(input_ids)
2894
2931
 
2895
2932
  trans_probs, attn_weights = self.pipeline.model2.forward(input_ids, AME=AME, embedded=embedded)
2896
- mlp_probs = self.pipeline.mlp.forward(X_mlp)
2933
+ mlp_probs = self.pipeline.model3.forward(X_mlp)
2897
2934
  lstm_probs, lstm_weight_hint = self._get_lstm_probs(input_ids, X_mlp, label_bins=label_bins)
2898
2935
 
2899
2936
  established_agreement = self.query_node._establish_node_connection("PredictEnsemble")
@@ -8544,6 +8581,7 @@ class IntegratedPipeline:
8544
8581
  self.temperature = 1.0
8545
8582
  self.transformer_lr = 0.1
8546
8583
  self.max_seq_len = 16
8584
+ self.error_decay = 0.85
8547
8585
 
8548
8586
  self.memory_name = memory_name
8549
8587
 
@@ -13517,41 +13555,51 @@ class PipelineAsyncManager:
13517
13555
  self._save_state()
13518
13556
 
13519
13557
 
13520
- def advanced_batch_prediction(self, test_titles, label_map, rules, X=None, y=None, api_key=None, client_ip=None):
13558
+ def advanced_batch_prediction(self, test_titles, label_map, rules,
13559
+ X=None, y=None, api_key=None, client_ip=None):
13521
13560
  try:
13522
- attn_weights = None
13523
- final_idx = None
13561
+ attn_weights = None
13562
+ final_idx = None
13524
13563
  attn_weight_rate = None
13525
- final_idx = None
13564
+ confidence = None
13565
+ chosen_label = None
13526
13566
 
13527
13567
  reverse_label_map = {v: k for k, v in label_map.items()}
13568
+ n_classes = len(label_map)
13528
13569
 
13529
13570
  if 'attn_weights' in self.pipeline.model2.cache:
13530
13571
  attn_weights = self.pipeline.model2.cache['attn_weights']
13531
13572
  if attn_weights is not None:
13532
13573
  attn_weight_growth = 1.0 / (1.0 + np.exp(-attn_weights[0]))
13533
- attn_weight_rate = np.std(attn_weight_growth)
13574
+ attn_weight_rate = float(np.std(attn_weight_growth))
13534
13575
 
13535
- # Batch predictions
13536
- texts = [text[0] for text in test_titles]
13537
- expected_labels = [text[1] for text in test_titles] # Your ground truth
13576
+ texts = [text[0] for text in test_titles]
13577
+ expected_labels = [text[1] for text in test_titles]
13538
13578
  predicted_output = []
13539
13579
 
13540
- text = {"test_titles": test_titles, "label_map": label_map, "rules": rules, "X":X, "y":y, "use_transformer": True}
13580
+ text_payload = {
13581
+ "test_titles" : test_titles,
13582
+ "label_map" : label_map,
13583
+ "rules" : rules,
13584
+ "X" : X,
13585
+ "y" : y,
13586
+ "use_transformer": True
13587
+ }
13541
13588
 
13542
13589
  if not self.pipeline.intents:
13543
- result = self.predict(
13544
- text,
13545
- timeout=self.pipeline.timeout,
13546
- retries=None,
13547
- api_key=api_key,
13548
- client_ip=client_ip,
13549
- ) # regular predict to populate pipelines data
13590
+ result = self.predict(
13591
+ text_payload,
13592
+ timeout=self.pipeline.timeout,
13593
+ retries=None,
13594
+ api_key=api_key,
13595
+ client_ip=client_ip,
13596
+ )
13597
+ confidence = None # no confidence from this path
13550
13598
  else:
13551
13599
  result, chosen_label, confidence = self.advanced_prediction_method(
13552
- self.prediction_manager, test_titles, label_map, rules,
13553
- X=X, y=y, method='Transformer_included')
13554
-
13600
+ self.prediction_manager, test_titles, label_map, rules,
13601
+ X=X, y=y, method='Transformer_included'
13602
+ )
13555
13603
  try:
13556
13604
  if isinstance(result, list) and len(result) > 0:
13557
13605
  final_idx = result[0].get('index')
@@ -13560,62 +13608,107 @@ class PipelineAsyncManager:
13560
13608
  except (KeyError, IndexError):
13561
13609
  final_idx = None
13562
13610
 
13563
- print(f'[=+=] Local single advanced Prediction result: {result}')
13611
+ print(f'[=+=] Local single advanced prediction: {result}')
13612
+ print('[=] Initiating batch prediction...')
13564
13613
 
13565
- print('[=] Initiating Batch prediction for multiple texts...')
13566
13614
  results = self.predict_batch(
13567
13615
  texts=texts,
13568
13616
  timeout=self.pipeline.timeout,
13569
- api_key=api_key)
13617
+ api_key=api_key
13618
+ )
13619
+
13620
+ # class reputation track: how many times each class
13621
+ # was predicted wrong vs total predictions so far
13622
+ # error_rate[c] ∈ [0,1] — higher = this class is unreliable recently
13623
+ error_counts = np.zeros(n_classes, dtype=np.float64)
13624
+ pred_counts = np.zeros(n_classes, dtype=np.float64)
13625
+ decay = self.pipeline.error_decay # how fast old errors fade — tunable
13570
13626
 
13571
13627
  print("========📊 PREDICTION RESULTS============")
13572
-
13573
- for text, expected, probs in zip(texts, expected_labels, results):
13574
- calibrated_probs = probs.copy()
13575
- if confidence is not None and attn_weight_rate is not None and final_idx:
13576
- if final_idx <= len(probs):
13577
- try:
13578
- calibrated_index = probs[final_idx] + (1.0 - attn_weight_rate) * confidence
13579
- calibrated_probs[final_idx] = calibrated_index
13580
- except IndexError:
13581
- calibrated_probs = probs + (1.0 - attn_weight_rate) * confidence
13628
+
13629
+ for idx, (text, expected, probs) in enumerate(
13630
+ zip(texts, expected_labels, results)
13631
+ ):
13632
+ probs = np.asarray(probs, dtype=np.float64).copy()
13633
+
13634
+ # ── calibrate with single-prediction context ──────────────
13635
+ if (confidence is not None and
13636
+ attn_weight_rate is not None and
13637
+ final_idx is not None):
13638
+
13639
+ if 0 <= final_idx < len(probs):
13640
+ boost = (1.0 - attn_weight_rate) * confidence
13641
+ probs[final_idx] = min(probs[final_idx] + boost, 0.95)
13582
13642
  else:
13583
- try:
13584
- calibrated_probs = probs + (1.0 - attn_weight_rate) * confidence
13585
- except:
13586
- calibrated_probs = calibrated_probs.copy()
13643
+ boost = (1.0 - attn_weight_rate) * confidence
13644
+ probs = np.clip(probs + boost, 0.0, 0.95)
13645
+
13646
+ # ── apply class reputation penalty ────────────────────────
13647
+ # ounded reputation-based penalty
13648
+ # classes with high recent error rate get their probs dampened
13649
+ for c in range(n_classes):
13650
+ if pred_counts[c] > 0:
13651
+ error_rate = error_counts[c] / (pred_counts[c] + 1e-8)
13652
+ # sigmoid-shaped dampening — never goes negative
13653
+ # error_rate=0.0 → multiplier=1.0 (no change)
13654
+ # error_rate=0.5 → multiplier≈0.67
13655
+ # error_rate=1.0 → multiplier≈0.5
13656
+ reputation = 1.0 / (1.0 + error_rate)
13657
+ probs[c] *= reputation
13658
+
13659
+ # renormalize after reputation dampening
13660
+ prob_sum = probs.sum()
13661
+ if prob_sum > 1e-8:
13662
+ probs /= prob_sum
13663
+
13664
+ predicted_index = int(np.argmax(probs))
13665
+ predicted_label = reverse_label_map.get(
13666
+ predicted_index, f"class_{predicted_index}"
13667
+ )
13668
+ pred_conf = float(probs[predicted_index])
13669
+
13670
+ top_3_indices = np.argsort(probs)[-3:][::-1]
13671
+ top_3 = [
13672
+ (reverse_label_map.get(int(i), f"class_{i}"), float(probs[i]))
13673
+ for i in top_3_indices
13674
+ ]
13587
13675
 
13588
- predicted_index = np.argmax(calibrated_probs) # Get index with highest probability
13589
- predicted_label = reverse_label_map.get(predicted_index, f"class_{predicted_index}")
13590
- confidence = calibrated_probs[predicted_index]
13591
-
13592
- # Get top 3 predictions for more insight
13593
- top_3_indices = np.argsort(calibrated_probs)[-3:][::-1]
13594
- top_3 = [(reverse_label_map.get(idx, f"class_{idx}"), calibrated_probs[idx]) for idx in top_3_indices]
13595
-
13596
13676
  print(f"\n📌 Input: '{text}'")
13597
13677
  print(f" [=] Expected: {expected}")
13598
- print(f" 🎯 Predicted: {predicted_label} ({confidence:.1%})")
13599
-
13600
- # Show top 3 possibilities
13678
+ print(f" 🎯 Predicted: {predicted_label} ({pred_conf:.1%})")
13601
13679
  print(f" 🔍 Top possibilities:")
13602
13680
  for label, conf in top_3:
13603
13681
  bar = '█' * int(conf * 20)
13604
13682
  print(f"[•] {label:<25} {bar} {conf:.1%}")
13605
-
13606
- # Match expected vs predicted
13683
+
13607
13684
  print('===== COMPARISON MATCHING =====')
13608
- if predicted_label == expected:
13609
- print(f"[✅] CORRECT LABEL!")
13685
+ is_correct = predicted_label == expected
13686
+
13687
+ # update class reputation — decay old counts first..
13688
+ error_counts *= decay
13689
+ pred_counts *= decay
13690
+
13691
+ pred_counts[predicted_index] += 1.0
13692
+ if not is_correct:
13693
+ print(f"[❌] INCORRECT (expected: {expected})")
13694
+ error_counts[predicted_index] += 1.0
13610
13695
  else:
13611
- print(f"[] INCORRECT LABEL (expected: {expected})")
13696
+ print(f"[] CORRECT!")
13612
13697
 
13613
- predicted_output.append(f'{text} -> {predicted_label} With {confidence:.1%} Confidence')
13698
+ predicted_output.append(
13699
+ f'{text} -> {predicted_label} With {pred_conf:.1%} Confidence'
13700
+ )
13614
13701
 
13615
- # Get stats
13702
+ # batch accuracy summary
13703
+ correct = sum(
13704
+ 1 for text, exp in zip(texts, expected_labels)
13705
+ for po in [predicted_output]
13706
+ if f'-> {exp}' in po
13707
+ )
13708
+ print(f"\n[=] Batch complete")
13616
13709
  print(f"[=] Stats: {self.get_stats()}")
13617
- print('[=] Returning predicted output as list.')
13618
- return predicted_output
13710
+ print('[=] Returning predicted output.')
13711
+ return predicted_output
13619
13712
 
13620
13713
  except Exception as e:
13621
13714
  print(f'[=] Error in advanced batch prediction: {e}')
@@ -13891,7 +13984,14 @@ class PipelinePredictionManager:
13891
13984
  self.titles, self.y_raw, self.label_map = self.load_labels_from_csv(label_csv, target_title, label)
13892
13985
  except Exception as e:
13893
13986
  print(f"Error loading labels: {e}")
13894
- self.titles, self.y_raw, self.label_map = [], [], {}
13987
+ self.titles, self.y_raw, self.label_map = None, None, None
13988
+
13989
+ if self.label_map is not None:
13990
+ self.error_counts = np.zeros(len(self.label_map), dtype=np.float64)
13991
+ self.pred_counts = np.zeros(len(self.label_map), dtype=np.float64)
13992
+ self.decay = self.pipeline.error_decay # how fast old errors fade — tunable
13993
+ else:
13994
+ raise Warning('[!] Label map is None, consider adding label map!')
13895
13995
 
13896
13996
  print(f"✅ Loaded {len(self.titles)} labeled examples")
13897
13997
 
@@ -13907,8 +14007,6 @@ class PipelinePredictionManager:
13907
14007
  4. User home directory
13908
14008
  5. Common data folders (Downloads, Documents, Desktop)
13909
14009
  """
13910
- import os
13911
- import sys
13912
14010
 
13913
14011
  # build candidate paths
13914
14012
  candidates = []
@@ -14638,9 +14736,10 @@ class PipelinePredictionManager:
14638
14736
  print(f'[!] Similarity: {cached['similarity']} is low, Cannot pick label due to low certainty, Initiating advanced prediction...')
14639
14737
  else:
14640
14738
  print('[=] No verified output from cache available that matched samples, starting advanced prediction...')
14641
-
14642
- target_probs = self.pipeline.predict_proba(input_ids, X, type='Hybrid', embedded=True)
14643
- target_probs = target_probs[:mlp_probs.shape[0], :mlp_probs.shape[1]]
14739
+ target_probs = self.pipeline.predict_proba(input_ids, X, type='Hybrid', embedded=True)
14740
+ target_probs = target_probs[:mlp_probs.shape[0], :mlp_probs.shape[1]]
14741
+ target_probs = self.pipeline.model3.continuos_predictive_correction(self, target_probs, mlp_pred_indices)
14742
+
14644
14743
  target_pred_indices = np.argmax(target_probs, axis=1)
14645
14744
 
14646
14745
  if self.pipeline.cache and 'label_bins' in self.pipeline.cache:
@@ -14653,13 +14752,6 @@ class PipelinePredictionManager:
14653
14752
  results = []
14654
14753
  attention_data = [] if return_attention else None
14655
14754
 
14656
- target_probs = self.pipeline.predict_proba(input_ids, X, type='Hybrid', embedded=True)
14657
- target_probs = target_probs[:mlp_probs.shape[0], :mlp_probs.shape[1]]
14658
- target_pred_indices = np.argmax(target_probs, axis=1)
14659
-
14660
- results = []
14661
- attention_data = [] if return_attention else None
14662
-
14663
14755
  for i, title in enumerate(titles):
14664
14756
  # Parse input
14665
14757
  if isinstance(title, tuple):
@@ -14726,7 +14818,8 @@ class PipelinePredictionManager:
14726
14818
  final_probs = mlp_weight * target_probs[i][:len(calibration)] + trans_weight * calibration[i][:len(calibration)] + lstm_weight * calibration[i][:len(calibration)]
14727
14819
  else:
14728
14820
  final_probs = mlp_weight * target_probs[i][:len(calibration)] + trans_weight * calibration[i][:len(calibration)]
14729
-
14821
+
14822
+ final_probs = self.calibration_penalized_check(final_probs, target_class_idx)
14730
14823
  final_class_idx = target_class_idx
14731
14824
  try:
14732
14825
  final_confidence = final_probs[final_class_idx]
@@ -14740,6 +14833,8 @@ class PipelinePredictionManager:
14740
14833
  agreement = mlp_class_idx == trans_class_idx
14741
14834
  else:
14742
14835
  final_probs = mlp_probs[i]
14836
+ final_probs = self.calibration_penalized_check(final_probs, mlp_class_idx)
14837
+
14743
14838
  final_class_idx = mlp_class_idx
14744
14839
  final_confidence = mlp_confidence[0] if isinstance(mlp_confidence, np.ndarray) else mlp_confidence
14745
14840
  if isinstance(final_confidence, np.ndarray) or isinstance(final_confidence, list):
@@ -14843,7 +14938,8 @@ class PipelinePredictionManager:
14843
14938
  print("\n[⚠️] Disagreement detected between MLP and Transformer predictions. Using calibrated probabilities for final decision.")
14844
14939
  if not self.pipeline.autonomous and need_peer_condition:
14845
14940
  print('|| Uncertain advanced prediction, requesting peer assistance if allowed...')
14846
- final_probs = self.pipeline._handle_distributed_connections(final_probs, attn_weights, input_ids, agreement)
14941
+ final_probs = self.pipeline._handle_distributed_connections(final_probs, attn_weights, input_ids, agreement)
14942
+
14847
14943
  final_idx = final_probs[0].argmax()
14848
14944
  original_idx = final_idx
14849
14945
 
@@ -14865,8 +14961,9 @@ class PipelinePredictionManager:
14865
14961
 
14866
14962
  print('[||] Iniating local peer output search in database for best output...')
14867
14963
  final_probs = self.pipeline.distribution._handle_peer_agent_request(final_probs, attn_weights, input_ids, type='DevicePeer', agreement=agreement)
14964
+
14868
14965
  final_idx = final_probs[0].argmax()
14869
- original_idx = final_idx
14966
+ original_idx = final_idx
14870
14967
 
14871
14968
  if final_idx > len(reverse_map):
14872
14969
  final_idx = int(np.argmax(final_probs[:len(reverse_map)-1]))
@@ -14884,9 +14981,10 @@ class PipelinePredictionManager:
14884
14981
  if final_confidence is not None and confidence < self.pipeline.confidence_threshold:
14885
14982
  print("\n[⚠️] Low confidence detected, but both models don't agree. Using calibrated probabilities for final decision to ensure robustness.")
14886
14983
  final_probs = self.pipeline.hybrid_prediction(rules, input_ids, dataset)
14984
+
14887
14985
  final_idx = final_probs[0].argmax()
14888
14986
  original_idx = final_idx
14889
-
14987
+
14890
14988
  if final_idx > len(reverse_map):
14891
14989
  final_idx = int(np.argmax(final_probs[:len(reverse_map)-1]))
14892
14990
  print(f"[⚠️] Clamping {final_idx} → {final_idx}")
@@ -14894,9 +14992,9 @@ class PipelinePredictionManager:
14894
14992
  else:
14895
14993
  print('[🎯] Stable confidence established, But both Models doesnt Agree, Re-evaluating...')
14896
14994
  final_probs = self.pipeline.hybrid_prediction(rules, input_ids, dataset)
14995
+
14897
14996
  final_idx = final_probs[0].argmax()
14898
14997
  original_idx = final_idx
14899
-
14900
14998
  if final_idx > len(reverse_map):
14901
14999
  final_idx = int(np.argmax(final_probs[:len(reverse_map)-1]))
14902
15000
  print(f"[⚠️] Clamping {final_idx} → {final_idx}")
@@ -14912,6 +15010,7 @@ class PipelinePredictionManager:
14912
15010
  print("\n[⚠️] Uncertain confidence and disagreement detected. Using ensemble method for final decision.")
14913
15011
  input_forward = sequence_ids if sequence_ids is not None else input_ids
14914
15012
  final_probs, details = self.pipeline.ensemble.predict_ensemble(input_forward, X, y, method='dynamic', embedded=True)
15013
+
14915
15014
  final_idx = final_probs[0].argmax()
14916
15015
  original_idx = final_idx
14917
15016
 
@@ -14928,7 +15027,7 @@ class PipelinePredictionManager:
14928
15027
  else:
14929
15028
  input_forward = sequence_ids if sequence_ids is not None else input_ids
14930
15029
  final_idx = final_probs[0].argmax() if final_probs is not None else target_probs[0].argmax()
14931
-
15030
+
14932
15031
  original_idx = final_idx
14933
15032
 
14934
15033
  if final_idx > len(reverse_map):
@@ -15010,7 +15109,8 @@ class PipelinePredictionManager:
15010
15109
  confidence = float(final_probs[0][final_idx])
15011
15110
  except:
15012
15111
  confidence = float(final_probs[0][len(reverse_map)-1]) if isinstance(final_probs[0], (float, int)) else 0.0
15013
-
15112
+
15113
+
15014
15114
  if isinstance(chosen_label, str) and chosen_label.startswith("unknown") or float(confidence) < self.pipeline.confidence_threshold:
15015
15115
  if chosen_label.startswith("unknown"):
15016
15116
  chosen_label = 'Unknown'
@@ -15084,7 +15184,7 @@ class PipelinePredictionManager:
15084
15184
  'X_samples': X,
15085
15185
  'input_ids': input_ids
15086
15186
  }
15087
- correct, sec_correct = self.display_hybrid_results(payload, results, top_k, verbose=True)
15187
+ correct, sec_correct = self.display_hybrid_results(payload, final_class_idx, results, top_k, verbose=True)
15088
15188
  if sec_chosen_label and sec_correct > correct:
15089
15189
  print(f'[⚡] Second Prediction: {sec_chosen_label} has higher accuracies, relying on: {sec_chosen_label} as final label.')
15090
15190
  chosen_label = sec_chosen_label # overrides previous chosen label if accuracy is higher
@@ -15097,18 +15197,48 @@ class PipelinePredictionManager:
15097
15197
  self.pipeline.cache.clear()
15098
15198
 
15099
15199
  return results, chosen_label, confidence
15200
+
15201
+
15202
+ def calibration_penalized_check(self, final_probs, predicted_index):
15203
+ # update class reputation.
15204
+ try:
15205
+ decay = self.pipeline.error_decay
15206
+ self.error_counts *= decay
15207
+ self.pred_counts *= decay
15208
+
15209
+ self.pred_counts[predicted_index] += 1.0
15210
+ n_classes = len(self.label_map)
15211
+
15212
+ for c in range(n_classes):
15213
+ if self.pred_counts[c] > 0:
15214
+ error_rate = self.error_counts[c] / (self.pred_counts[c] + 1e-8)
15215
+ # sigmoid-shaped dampening — never goes negative
15216
+ # error_rate=0.0 → multiplier=1.0 (no change)
15217
+ # error_rate=0.5 → multiplier≈0.67
15218
+ # error_rate=1.0 → multiplier≈0.5
15219
+ reputation = 1.0 / (1.0 + error_rate)
15220
+ final_probs[c] *= reputation
15221
+
15222
+ prob_sum = final_probs.sum()
15223
+ if prob_sum > 1e-8:
15224
+ final_probs /= prob_sum
15225
+
15226
+ except Exception as e:
15227
+ print(f'[!] Cant check and calibrate probs based on penalty due to: {e}')
15100
15228
 
15229
+ return final_probs
15230
+
15101
15231
 
15102
- def display_hybrid_results(self, payload, results, top_k=3, verbose=False):
15232
+ def display_hybrid_results(self, payload, predicted_index, results, top_k=3, verbose=False):
15103
15233
  print("\n" + "="*80)
15104
15234
  print("[🎯] == PREDICTION RESULTS == ")
15105
15235
  print("="*80)
15106
-
15236
+
15107
15237
  correct = 0
15108
15238
  sec_correct = 0
15109
15239
  total_with_expected = 0
15110
15240
  X_samples, input_ids = payload['X_samples'], payload['input_ids']
15111
-
15241
+
15112
15242
  for idx, result in enumerate(results):
15113
15243
  print(f"\n{idx+1}. 📌 '{result['title']}'")
15114
15244
 
@@ -15116,6 +15246,7 @@ class PipelinePredictionManager:
15116
15246
  total_with_expected += 1
15117
15247
  status = ": ✅" if result['predicted'] == result['expected'] else ": ❌"
15118
15248
  print(f"[=] First Expectation: {result['expected']} || Model Answer: {status}")
15249
+
15119
15250
  if 'sec_predicted' in result:
15120
15251
  sec_status = ": ✅" if result['sec_predicted'] == result['expected'] else ": ❌"
15121
15252
  print(f"[=] Second Expectation: {result['expected']} || Model Answer: {sec_status}")
@@ -15126,6 +15257,8 @@ class PipelinePredictionManager:
15126
15257
  source='automatic_verified')
15127
15258
 
15128
15259
  sec_correct += 1
15260
+ else:
15261
+ self.error_counts[predicted_index] += 1.0
15129
15262
 
15130
15263
  if result['predicted'] == result['expected']:
15131
15264
  if result['sec_index'] is None:
@@ -15137,6 +15270,9 @@ class PipelinePredictionManager:
15137
15270
  source='automatic_verified')
15138
15271
 
15139
15272
  correct += 1
15273
+ else:
15274
+ self.error_counts[predicted_index] += 1.0
15275
+
15140
15276
 
15141
15277
  # Agreement indicator
15142
15278
  agree_symbol = "✓" if result.get('models_agree', True) else "⚠️"