AbstractIntegratedModule 0.6.8__tar.gz → 0.7.1__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 (21) hide show
  1. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/AbstractIntegratedModule.egg-info/PKG-INFO +11 -3
  2. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/AbstractIntegratedModule.py +282 -111
  3. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/AbstractOptimizedModules.c +152 -152
  4. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/PKG-INFO +11 -3
  5. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/README.md +10 -2
  6. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/setup.py +2 -9
  7. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/AbstractIntegratedModule.egg-info/SOURCES.txt +0 -0
  8. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/AbstractIntegratedModule.egg-info/dependency_links.txt +0 -0
  9. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/AbstractIntegratedModule.egg-info/requires.txt +0 -0
  10. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/AbstractIntegratedModule.egg-info/top_level.txt +0 -0
  11. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/AbstractOptimizedModules.pyx +0 -0
  12. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/MANIFEST.in +0 -0
  13. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/abstract_model_storage/Cargo.toml +0 -0
  14. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/abstract_model_storage/pyproject.toml +0 -0
  15. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/abstract_model_storage/src/lib.rs +0 -0
  16. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/abstract_model_storage/target/debug/build/libsqlite3-sys-ed07b882cd2aa5e2/out/bindgen.rs +0 -0
  17. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/abstract_model_storage/target/debug/build/target-lexicon-08527f45de28143d/out/host.rs +0 -0
  18. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/abstract_model_storage/target/release/build/libsqlite3-sys-bf0400df4523274c/out/bindgen.rs +0 -0
  19. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/abstract_model_storage/target/release/build/target-lexicon-43eb95a0588bf457/out/host.rs +0 -0
  20. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/pyproject.toml +0 -0
  21. {abstractintegratedmodule-0.6.8 → abstractintegratedmodule-0.7.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AbstractIntegratedModule
3
- Version: 0.6.8
3
+ Version: 0.7.1
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.6.8 Official Release.
45
+ - Development Stage: 0.7.1 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.
@@ -65,9 +65,17 @@ https://github.com/Micro-Novelty/IntegratedPipeline-Specialized-Non-LLM-AI-Agent
65
65
  - Robust Advanced prediction capabilities proven effective on ARM64 Using MLP + LSTM Architectures.
66
66
  - Transformer Modules Optimized using Cython, to reduce Memory overhead and Reduce CPU Usage, With Reduced Training Time.
67
67
  - Changelog:
68
- - v0.6.8:
68
+ - v0.7.1:
69
69
  - [=] New features:
70
70
  - Added Rust specific compiled binary for handling complex weight matrices.
71
+ - added specific optimized weight saving handling with rust optimization.
72
+ - Adding Optimization and refinements, especially bug fixes in:
73
+ - IntegratedPipeline lstm samples creating
74
+ - IntegratedPipeline shape adaptation
75
+ - IntegratedPipeline probability calibration
76
+ - IntegratedPipeline auto generate labels text
77
+ - IntegratedPipeline MLP samples generation.
78
+
71
79
  -----
72
80
 
73
81
  <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" />
@@ -31,6 +31,7 @@ from typing import Any, Callable, Tuple, Optional, Dict, List
31
31
  from datetime import datetime, timedelta
32
32
  from enum import IntEnum, Enum
33
33
  from collections import deque
34
+ from collections import Counter
34
35
  import traceback
35
36
  from concurrent.futures import TimeoutError as FutureTimeoutError
36
37
  import secrets
@@ -62,6 +63,15 @@ except ImportError as e:
62
63
  _OPT_AVAILABLE = False
63
64
  print(f'[=] Cython not available: {e}, using numpy fallback')
64
65
 
66
+ try:
67
+ import abstract_weights_core as wc
68
+ _RUST_MODULE_AVAILABLE = True
69
+ print('[=] Rust weight storage loaded ✅')
70
+ except ImportError as e:
71
+ _RUST_MODULE_AVAILABLE = False
72
+ print(f'[=] Rust weight storage unavailable due to: {e}, using Python sqlite3 fallback')
73
+
74
+
65
75
  # initial Setup logging for AgentDistributedInference and ModelStorage class logger and security logger
66
76
  logger = logging.getLogger(__name__)
67
77
  _integrated_pipeline_lock = threading.Lock()
@@ -2772,7 +2782,6 @@ class WeightedEnsemblePredictor:
2772
2782
  predictions = [r['prediction'] for r in results]
2773
2783
  confidences = [r['confidence'] for r in results]
2774
2784
 
2775
- from collections import Counter
2776
2785
  distribution = Counter(predictions)
2777
2786
 
2778
2787
  print("\n📊 Batch Summary:")
@@ -4885,6 +4894,16 @@ class ModelStorage:
4885
4894
  }
4886
4895
 
4887
4896
  weight_json = json.dumps(weights, default=str)
4897
+ if _RUST_MODULE_AVAILABLE:
4898
+ try:
4899
+ wc.save_lstm_weights(self.db_path, memory_name, weight_json)
4900
+ print('[||] LSTM weights saved using Rust module !')
4901
+ return
4902
+ except Exception as e:
4903
+ print(f'[!] Rust save failed, falling back to Python: {e}')
4904
+ else:
4905
+ print('[=] Rust module unavailable, using python sqlite3.')
4906
+
4888
4907
  try:
4889
4908
  c.execute("""
4890
4909
  INSERT INTO weight_storage
@@ -4949,6 +4968,15 @@ class ModelStorage:
4949
4968
  output_bias = tf.output_bias
4950
4969
  )
4951
4970
  binary_data = buf.getvalue()
4971
+ if _RUST_MODULE_AVAILABLE:
4972
+ try:
4973
+ wc.save_transformer_weights(self.db_path, memory_name, binary_data)
4974
+ print('[||] LSTM weights saved using Rust module for flexibility!')
4975
+ return
4976
+ except Exception as e:
4977
+ print(f'[!] Rust save failed, falling back to Python: {e}')
4978
+ else:
4979
+ print('[=] Rust module unavailable, using python sqlite3.')
4952
4980
 
4953
4981
  try:
4954
4982
  c.execute("""
@@ -4988,21 +5016,48 @@ class ModelStorage:
4988
5016
  except:
4989
5017
  conn = sqlite3.connect(self.db_path)
4990
5018
 
4991
- c = conn.cursor()
5019
+ c = conn.cursor()
5020
+
4992
5021
 
4993
5022
  try:
4994
- c.execute("""
4995
- SELECT weights FROM weight_storage
4996
- WHERE memory_name = ? AND model_type = 'transformer' AND is_active = 1
4997
- ORDER BY id DESC LIMIT 1
4998
- """, (memory_name,))
4999
- row = c.fetchone()
5000
- if not row:
5001
- print(f'[=] No saved transformer weights for {memory_name}')
5002
- return False
5023
+ if _RUST_MODULE_AVAILABLE:
5024
+ try:
5025
+ binary = wc.load_transformer_weights(self.db_path, memory_name)
5026
+
5027
+ buf = io.BytesIO(bytes(binary))
5028
+ data = np.load(buf, allow_pickle=False)
5029
+
5030
+ print('[+] Transformer weights data loaded using Rust module!')
5031
+
5032
+ except Exception as e:
5033
+ print(f'[=] Cant load Transformer weights: {e}, using python sqlite3 to handle weights.')
5034
+ time.sleep(3)
5035
+ c.execute("""
5036
+ SELECT weights FROM weight_storage
5037
+ WHERE memory_name = ? AND model_type = 'transformer' AND is_active = 1
5038
+ ORDER BY id DESC LIMIT 1
5039
+ """, (memory_name,))
5040
+ row = c.fetchone()
5041
+ if not row:
5042
+ print(f'[=] No saved transformer weights for {memory_name}')
5043
+ return False
5044
+
5045
+ buf = io.BytesIO(bytes(row[0]))
5046
+ data = np.load(buf, allow_pickle=False)
5047
+
5048
+ else:
5049
+ c.execute("""
5050
+ SELECT weights FROM weight_storage
5051
+ WHERE memory_name = ? AND model_type = 'transformer' AND is_active = 1
5052
+ ORDER BY id DESC LIMIT 1
5053
+ """, (memory_name,))
5054
+ row = c.fetchone()
5055
+ if not row:
5056
+ print(f'[=] No saved transformer weights for {memory_name}')
5057
+ return False
5003
5058
 
5004
- buf = io.BytesIO(bytes(row[0]))
5005
- data = np.load(buf, allow_pickle=False)
5059
+ buf = io.BytesIO(bytes(row[0]))
5060
+ data = np.load(buf, allow_pickle=False)
5006
5061
 
5007
5062
  t = self.pipeline.model2
5008
5063
  t.token_embedding = data['token_embedding']
@@ -5035,7 +5090,15 @@ class ModelStorage:
5035
5090
 
5036
5091
  def load_weights(self, memory_name):
5037
5092
  """Load weights from database. Returns True if found."""
5038
- result = self.weight_retrieval(memory_name)
5093
+ if _RUST_MODULE_AVAILABLE:
5094
+ try:
5095
+ result = wc.load_lstm_weights(self.db_path, memory_name)
5096
+ print('[+] LSTM weights loaded via Rust module')
5097
+ except Exception as e:
5098
+ result = self.weight_retrieval(memory_name)
5099
+ print(f'[=] Cant load LSTM Weights due to: {e}, using python sqlite3 as fallback.')
5100
+ else:
5101
+ result = self.weight_retrieval(memory_name)
5039
5102
 
5040
5103
  if not result:
5041
5104
  print(f'[=] No saved weights for {memory_name}')
@@ -5072,22 +5135,30 @@ class ModelStorage:
5072
5135
  except:
5073
5136
  db_path = self.get_database_path()
5074
5137
  conn = sqlite3.connect(db_path)
5075
-
5076
- conn = sqlite3.connect(self.db_path)
5077
- c = conn.cursor()
5078
-
5079
- c.execute("""
5080
- SELECT model_data FROM model_attn_storage
5081
- WHERE memory_name = ? AND is_active = 1
5082
- """, (memory_name,))
5083
-
5084
- result = c.fetchone()
5085
- conn.close()
5086
-
5087
- if result:
5088
- return json.loads(result[0])
5138
+
5139
+ if _RUST_MODULE_AVAILABLE:
5140
+ try:
5141
+ data = wc.load_attention_dict(self.db_path, memory_name)
5142
+ print('[+] Transformer attention loaded using Rust module!')
5143
+ return data
5144
+ except Exception as e:
5145
+ print(f'[=]: {e}, Loading transformer attention from python sqlite3...')
5146
+ else:
5147
+ conn = sqlite3.connect(self.db_path)
5148
+ c = conn.cursor()
5149
+
5150
+ c.execute("""
5151
+ SELECT model_data FROM model_attn_storage
5152
+ WHERE memory_name = ? AND is_active = 1
5153
+ """, (memory_name,))
5154
+
5155
+ result = c.fetchone()
5156
+ conn.close()
5157
+
5158
+ if result:
5159
+ return json.loads(result[0])
5089
5160
  except Exception as e:
5090
- print(f'Error handling attention dict: {e}')
5161
+ print(f'[!] Error handling attention dict: {e}')
5091
5162
 
5092
5163
  return None
5093
5164
 
@@ -9836,59 +9907,62 @@ class IntegratedPipeline:
9836
9907
 
9837
9908
  def _calibrate_probs(self, probs, target_preds, attn_weights, input_ids):
9838
9909
  calibrated = probs.copy()
9910
+
9839
9911
  if isinstance(input_ids, list):
9840
9912
  input_ids = np.array(input_ids)
9841
9913
 
9842
- if len(probs.shape) > 1:
9843
- n_classes = probs.shape[1]
9844
- else:
9845
- n_classes = probs.shape[0]
9846
-
9914
+ n_classes = probs.shape[1] if probs.ndim > 1 else probs.shape[0]
9847
9915
  batch_size = len(target_preds)
9848
9916
  eps = 1e-5
9849
9917
 
9850
- try:
9851
- for i in range(batch_size):
9852
- mlp_target = target_preds[i]
9918
+ temperature_accum = []
9853
9919
 
9854
- if attn_weights is None:
9855
- anisotropy = self.anisotropy_measurement(mlp_target)
9856
- else:
9857
- try:
9858
- anisotropy = self.anisotropy_measurement(attn_weights[i])
9859
- except:
9860
- anisotropy = self.anisotropy_measurement(attn_weights)
9920
+ attn_len = len(attn_weights) if attn_weights is not None else 0
9861
9921
 
9862
- if attn_weights is not None and i < len(attn_weights):
9863
- attn = attn_weights[i]
9864
-
9865
- score_quality = np.std(attn) if attn.size > 0 else self.confidence_threshold
9866
- abstract_score = self.confidence_threshold + score_quality * anisotropy
9922
+ for i in range(batch_size):
9923
+ # consistent bound check, no off-by-one
9924
+ mlp_target = target_preds[i] if i < attn_len else target_preds[0]
9925
+
9926
+ # anisotropy needs an array-like input
9927
+ if attn_weights is None:
9928
+ # no attention available
9929
+ anisotropy = eps
9930
+ elif i < attn_len:
9931
+ anisotropy = self.anisotropy_measurement(attn_weights[i])
9932
+ else:
9933
+ anisotropy = self.anisotropy_measurement(attn_weights[0])
9867
9934
 
9935
+ if attn_weights is not None and i < attn_len:
9936
+ attn = attn_weights[i]
9937
+ score_quality = np.std(attn) if attn.size > 0 else self.confidence_threshold
9938
+ abstract_score = self.confidence_threshold + score_quality * anisotropy
9939
+ else:
9940
+ if attn_weights is not None and attn_len > 0:
9941
+ # use last valid index
9942
+ fallback_attn = attn_weights[min(i, attn_len - 1)]
9943
+ score_quality = 1.0 / (1.0 + np.exp(-fallback_attn))
9868
9944
  else:
9869
- if attn_weights is not None:
9870
- if i <= len(attn_weights):
9871
- score_quality = 1.0 / (1.0 + np.exp(-attn_weights[i]))
9872
- else:
9873
- score_quality = 1.0 / (1.0 + np.exp(-attn_weights[0]))
9874
- else:
9875
- score_quality = 1.0 / (1.0 + np.exp(-mlp_target))
9876
-
9877
- abstract_score = (1.0 - score_quality) + eps
9878
-
9879
- self.temperature = (1.0 - abstract_score) + score_quality * anisotropy
9880
- if isinstance(self.temperature, np.ndarray):
9881
- self.temperature = np.clip(np.mean(self.temperature), 1e-5, 5.0)
9945
+ score_quality = self.confidence_threshold # neutral default
9946
+ abstract_score = (1.0 - np.mean(score_quality)) + eps
9947
+
9948
+ temp = (1.0 - abstract_score) + score_quality * anisotropy
9949
+ if isinstance(temp, np.ndarray):
9950
+ temp = float(np.clip(np.mean(temp), 1e-5, 5.0))
9951
+ temperature_accum.append(temp)
9952
+
9953
+ # bounds guard before indexing
9954
+ if 0 <= mlp_target < n_classes and i < calibrated.shape[0]:
9955
+ calibrated[i, mlp_target] = min(
9956
+ calibrated[i, mlp_target] * (1.5 * (1.0 - abstract_score)), 0.95
9957
+ )
9882
9958
 
9883
- try:
9884
- calibrated[i, mlp_target] = min(calibrated[i, mlp_target] * (1.5 * (1.0 - abstract_score)), 0.95)
9885
- except:
9886
- return calibrated
9959
+ row_sum = calibrated[i].sum()
9960
+ if row_sum > eps:
9961
+ calibrated[i] /= row_sum
9962
+ else:
9963
+ calibrated[i] = np.full(n_classes, 1.0 / n_classes)
9887
9964
 
9888
- calibrated[i] /= calibrated[i].sum()
9889
- except Exception as e:
9890
- print(f'[!] cant calibrate probability due to: {e}, returning regular probability.')
9891
- calibrated = calibrated.copy()
9965
+ self.temperature = float(np.mean(temperature_accum)) if temperature_accum else 1.0
9892
9966
 
9893
9967
  return calibrated
9894
9968
 
@@ -10020,10 +10094,9 @@ class IntegratedPipeline:
10020
10094
 
10021
10095
 
10022
10096
  def auto_generate_labels_from_texts(self, rules, texts):
10023
- import re
10024
10097
  y_raw = []
10025
10098
  self.rules = rules
10026
-
10099
+
10027
10100
  for text in texts:
10028
10101
  text_lower = text.lower()
10029
10102
  matched = False
@@ -10032,74 +10105,107 @@ class IntegratedPipeline:
10032
10105
  y_raw.append(label)
10033
10106
  matched = True
10034
10107
  break
10035
-
10036
10108
  if not matched:
10037
10109
  y_raw.append('other')
10038
-
10039
- from collections import Counter
10110
+
10040
10111
  print("\n[📊] Auto-generated label distribution:")
10041
- for label, count in Counter(y_raw).items():
10112
+ for label, count in sorted(Counter(y_raw).items()):
10042
10113
  print(f" {label}: {count} ({count/len(texts)*100:.1f}%)")
10043
-
10044
- return y_raw
10045
10114
 
10115
+ return y_raw
10046
10116
 
10047
10117
 
10048
10118
  def mlp_training_features(self, rules, dataset):
10049
10119
  print("\n[🔄] Preparing MLP data from dataset format")
10050
10120
 
10051
- texts = [item[:-1] for item in dataset]
10052
- labels = [item[-1] for item in dataset]
10053
-
10054
10121
  if isinstance(dataset[0], tuple) and len(dataset[0]) == 2:
10055
- # Format: [(features, label), ...]
10122
+ print('[=] Dataset Type 1: [(features, label), ...]')
10056
10123
  features_list = []
10057
- labels_list = []
10058
- print('[=] Dataset Type 1: [(value), (value)]')
10124
+ labels_list = []
10059
10125
  for item in dataset:
10060
10126
  features, label = item
10061
10127
  features_list.append(features)
10062
10128
  labels_list.append(label)
10063
-
10064
10129
  X_mlp = np.array(features_list)
10065
10130
  y_raw = np.array(labels_list)
10066
-
10131
+
10067
10132
  elif isinstance(dataset[0], (list, np.ndarray)) and len(dataset[0]) > 1:
10068
- print('[=] Dataset Type 2')
10133
+ print('[=] Dataset Type 2: [feature1, feature2, ..., label]')
10134
+ texts = [item[:-1] for item in dataset]
10135
+ labels = [item[-1] for item in dataset]
10069
10136
  X_mlp = np.array(texts)
10070
10137
  y_raw = np.array(labels)
10071
10138
 
10072
- else:
10073
- print('[=] Dataset type 3')
10074
- X_mlp = dataset.copy()
10075
- y_raw = self.auto_generate_labels_from_texts(rules, dataset)
10139
+ else:
10140
+ print('[=] Dataset type 3: raw texts, auto-labeling via rules')
10141
+ X_mlp = dataset.copy()
10142
+ y_raw = self.auto_generate_labels_from_texts(rules, dataset)
10076
10143
 
10077
10144
  unique_labels = sorted(set(y_raw))
10078
- label_to_idx = {l: i for i, l in enumerate(unique_labels)}
10079
- y_indices = np.array([label_to_idx[l] for l in y_raw])
10145
+ label_to_idx = {l: i for i, l in enumerate(unique_labels)}
10146
+ y_indices = np.array([label_to_idx[l] for l in y_raw])
10080
10147
 
10081
10148
  n_classes = len(unique_labels)
10082
- y_onehot = np.zeros((len(y_indices), n_classes))
10149
+ y_onehot = np.zeros((len(y_indices), n_classes))
10083
10150
  y_onehot[np.arange(len(y_indices)), y_indices] = 1
10084
10151
 
10085
-
10086
- if isinstance(X_mlp, np.ndarray):
10087
- input_dim = X_mlp.shape[0]
10152
+ if isinstance(X_mlp, np.ndarray) and X_mlp.ndim > 1:
10153
+ input_dim = X_mlp.shape[1]
10154
+ elif isinstance(X_mlp, np.ndarray):
10155
+ input_dim = 1 # 1D array — single feature per sample
10088
10156
  else:
10089
- input_dim = len(X_mlp)
10157
+ input_dim = len(X_mlp[0]) if len(X_mlp) > 0 else 0
10090
10158
 
10091
10159
  print(f"\n✅ MLP data ready:")
10092
- print(f"[=] X shape: {input_dim}")
10160
+ print(f"[=] X shape: {X_mlp.shape if isinstance(X_mlp, np.ndarray) else len(X_mlp)}")
10161
+ print(f"[=] input_dim: {input_dim}")
10093
10162
  print(f"[=] y shape: {y_onehot.shape}")
10094
- print(f"[=] Classes: {label_to_idx}")
10095
- return X_mlp, y_onehot, n_classes, input_dim
10163
+ print(f"[=] Classes: {label_to_idx}")
10096
10164
 
10097
- def shape_adaptation(self, X, inp):
10098
- tuple_ver = (inp, inp)
10099
- if X.shape != tuple_ver:
10100
- X = X[:inp, :inp]
10165
+ return X_mlp, y_onehot, n_classes, input_dim
10101
10166
 
10102
- return X
10167
+
10168
+ def shape_adaptation(self, X, target_features):
10169
+ """
10170
+ Adapts X's FEATURE dimension (columns) to match target_features.
10171
+ Sample count (rows) is never touched — only column width changes.
10172
+
10173
+ Args:
10174
+ X: (n_samples, n_features) array
10175
+ target_features: desired number of feature columns
10176
+ """
10177
+ try:
10178
+ if X.ndim == 1:
10179
+ X = X.reshape(1, -1)
10180
+
10181
+ n_samples, n_features = X.shape
10182
+
10183
+ if n_features == target_features:
10184
+ return X
10185
+
10186
+ print(f'[⚠️] shape_adaptation: X has {n_features} features, '
10187
+ f'target is {target_features} — adapting columns only '
10188
+ f'(rows={n_samples} unchanged)')
10189
+
10190
+ X_adapted = np.zeros((n_samples, target_features))
10191
+ min_features = min(n_features, target_features)
10192
+ X_adapted[:, :min_features] = X[:, :min_features]
10193
+
10194
+ if n_features > target_features:
10195
+ print(f'[⚠️] shape_adaptation: TRUNCATED {n_features - target_features} '
10196
+ f'feature columns ({n_features} → {target_features})')
10197
+ else:
10198
+ print(f'[=] shape_adaptation: PADDED {target_features - n_features} '
10199
+ f'feature columns with zeros')
10200
+ except Exception as e:
10201
+ print(f'[-] Fallback to primitive shape adaptation due to {e}')
10202
+ print(f'[!] WARNING: This may pad shapes aggressively! ')
10203
+ inp = X.shape[1]
10204
+ tuple_ver = (inp, inp)
10205
+ if X.shape != tuple_ver:
10206
+ X = X[:inp, :inp]
10207
+
10208
+ return X_adapted
10103
10209
 
10104
10210
  def AME_Encoder(self, x):
10105
10211
  X = np.asarray(x)
@@ -10154,9 +10260,40 @@ class IntegratedPipeline:
10154
10260
 
10155
10261
  return X, y, input_dim, n_classes
10156
10262
 
10157
- def _set_lstm_samples(self, X, Y):
10158
- X = np.array(X)[..., np.newaxis]
10159
- Y = np.array(Y)[..., np.newaxis]
10263
+ def _set_lstm_samples(self, X, Y, min_samples_for_split=10, use_cache_augmentation=True):
10264
+ """
10265
+ Reshape X, Y for LSTM input. If sample count is too small for a
10266
+ meaningful train/val split, augment with verified entries from
10267
+ AccurateAnswerCache before reshaping.
10268
+ """
10269
+ try:
10270
+ X = np.array(X)
10271
+ Y = np.array(Y)
10272
+
10273
+ # augment from accurate_cache before reshaping.
10274
+ if use_cache_augmentation and len(X) < min_samples_for_split:
10275
+ if hasattr(self, 'accurate_cache') and self.accurate_cache_lookup.cache:
10276
+ print(f'[=] Only {len(X)} samples — augmenting from accurate_cache '
10277
+ f'(has {len(self.accurate_cache_lookup.cache)} verified entries)')
10278
+
10279
+ cached_X, cached_Y = self._extract_cache_samples_for_lstm(
10280
+ target_count=min_samples_for_split - len(X)
10281
+ )
10282
+
10283
+ if len(cached_X) > 0:
10284
+ X = np.concatenate([X, cached_X], axis=0)
10285
+ Y = np.concatenate([Y, cached_Y], axis=0)
10286
+ print(f'[=] Augmented to {len(X)} samples using '
10287
+ f'{len(cached_X)} verified cache entries')
10288
+ else:
10289
+ print('[=] No suitable cache entries found for augmentation')
10290
+
10291
+ X = X[..., np.newaxis]
10292
+ Y = Y[..., np.newaxis]
10293
+ except Exception as e:
10294
+ print(f'[!] Error in seeting LSTM Samples: {e}, filling gaps with regular newaxis to populate data.')
10295
+ X = X = np.array(X)[..., np.newaxis]
10296
+ Y = np.array(Y)[..., np.newaxis]
10160
10297
 
10161
10298
  print('[=] Successfully set up LSTM Samples:')
10162
10299
  print(f'[=] X.shape: {X.shape}')
@@ -10164,6 +10301,37 @@ class IntegratedPipeline:
10164
10301
 
10165
10302
  return X, Y
10166
10303
 
10304
+
10305
+ def _extract_cache_samples_for_lstm(self, target_count):
10306
+ """
10307
+ Pull verified-correct entries from AccurateAnswerCache to use as
10308
+ additional LSTM training samples — only entries with multiple
10309
+ confirmed hits (hit_count >= 1) to avoid using unverified noise.
10310
+ """
10311
+ if not hasattr(self, 'accurate_cache') or not self.accurate_cache_lookup.cache:
10312
+ return np.array([]), np.array([])
10313
+
10314
+ candidates = [
10315
+ entry for entry in self.accurate_cache_lookup.cache.values()
10316
+ if entry.get('hit_count', 0) >= 1 # only entries confirmed at least once
10317
+ ]
10318
+
10319
+ # prioritize highest-confidence, most-confirmed entries first
10320
+ candidates.sort(key=lambda e: (e['hit_count'], e['confidence']), reverse=True)
10321
+ selected = candidates[:target_count]
10322
+
10323
+ if not selected:
10324
+ return np.array([]), np.array([])
10325
+
10326
+ cached_X = np.array([e['x_mlp'] for e in selected])
10327
+ cached_Y = np.array([
10328
+ e['prediction'] if isinstance(e['prediction'], (int, float))
10329
+ else e['confidence'] # fallback if prediction is a label string
10330
+ for e in selected
10331
+ ])
10332
+
10333
+ return cached_X, cached_Y
10334
+
10167
10335
  def lstm_setup_inference(self, raw_X, raw_Y):
10168
10336
  print("\n" + "=" * 55)
10169
10337
  print("===== LSTM SETUP INFERENCE =====")
@@ -10176,7 +10344,9 @@ class IntegratedPipeline:
10176
10344
  AME = self.AME_Encoder(raw_X) # geometric complexity scalar
10177
10345
  AMR = 1.0 / (1.0 + np.exp(-AME)) # abstract modelling rate
10178
10346
 
10179
- X, Y = self._set_lstm_samples(raw_X, raw_Y)
10347
+ augmentation = AMR > self.confidence_threshold and self.peer_assistance_threshold < 0.15
10348
+ X, Y = self._set_lstm_samples(raw_X, raw_Y, use_cache_augmentation=augmentation)
10349
+
10180
10350
  n_train = int(0.8 * len(X)) # 80% of the data training is used for training
10181
10351
  X_val = X[n_train:]
10182
10352
  Y_val = Y[n_train:]
@@ -12245,7 +12415,8 @@ class PipelineAsyncManager:
12245
12415
  predicted_output.append(advanced_result)
12246
12416
 
12247
12417
  # Get stats
12248
- print(f"[=] Stats: {self.get_stats()}")
12418
+ print(f"[=] Stats: {self.get_stats()}")
12419
+ print('[=] Returning predicted output as list')
12249
12420
  return predicted_output
12250
12421
 
12251
12422
  except Exception as e: