lqft-python-engine 0.2.0__tar.gz → 0.3.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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lqft-python-engine
3
- Version: 0.2.0
4
- Summary: LQFT Engine: Full CRUD Support & Enterprise Scaling (v4.5 Stable)
3
+ Version: 0.3.0
4
+ Summary: LQFT Engine: Memory Circuit Breaker & Strict Type Safety (v5.0 Stable)
5
5
  Home-page: https://github.com/ParjadM/Log-Quantum-Fractal-Tree-LQFT-
6
6
  Author: Parjad Minooei
7
7
  License: MIT
@@ -11,24 +11,28 @@
11
11
  #include <stdint.h>
12
12
 
13
13
  /**
14
- * LQFT C-Engine - V4.5 (Full CRUD & Large Payload)
14
+ * LQFT C-Engine - V5.0 (Active ARC Deletion & Full CRUD)
15
15
  * Architect: Parjad Minooei
16
16
  * * CHANGE LOG:
17
- * - Added 'delete' method with bottom-up path reconstruction.
18
- * - Maintains Incremental FNV-1a Hashing for large payloads.
19
- * - Fixed Stack-to-Heap collision for multi-MB data.
17
+ * - Implemented Automatic Reference Counting (ARC).
18
+ * - Added cascading `decref` for immediate RAM reclamation on deletion.
19
+ * - Added Open-Addressing Tombstones to the Global Registry.
20
+ * - Resolved all GCC -Wsign-compare warnings for production-grade builds.
20
21
  */
21
22
 
22
23
  #define BIT_PARTITION 5
23
24
  #define MAX_BITS 64
24
25
  #define MASK 0x1F
25
26
  #define REGISTRY_SIZE 8000009
27
+ #define TOMBSTONE ((LQFTNode*)1) // Special pointer for freed registry slots
26
28
 
27
29
  typedef struct LQFTNode {
28
30
  void* value;
29
31
  uint64_t key_hash;
30
32
  struct LQFTNode* children[32];
31
33
  char struct_hash[17];
34
+ uint64_t full_hash_val; // Cached for O(1) registry removal
35
+ int ref_count; // Tracks active branch dependencies
32
36
  } LQFTNode;
33
37
 
34
38
  static LQFTNode** registry = NULL;
@@ -38,6 +42,9 @@ static LQFTNode* global_root = NULL;
38
42
  const uint64_t FNV_OFFSET_BASIS = 14695981039346656037ULL;
39
43
  const uint64_t FNV_PRIME = 1099511628211ULL;
40
44
 
45
+ // -------------------------------------------------------------------
46
+ // Core Hashing & Memory Utilities
47
+ // -------------------------------------------------------------------
41
48
  uint64_t fnv1a_update(uint64_t hash, const void* data, size_t len) {
42
49
  const uint8_t* p = (const uint8_t*)data;
43
50
  for (size_t i = 0; i < len; i++) {
@@ -69,10 +76,52 @@ LQFTNode* create_node(void* value, uint64_t key_hash) {
69
76
  if (!node) return NULL;
70
77
  node->value = value;
71
78
  node->key_hash = key_hash;
79
+ node->full_hash_val = 0;
80
+ node->ref_count = 0; // Starts at 0 until adopted by a parent
72
81
  for (int i = 0; i < 32; i++) node->children[i] = NULL;
73
82
  return node;
74
83
  }
75
84
 
85
+ // -------------------------------------------------------------------
86
+ // Active ARC Deletion Logic
87
+ // -------------------------------------------------------------------
88
+ void decref(LQFTNode* node) {
89
+ if (!node) return;
90
+
91
+ node->ref_count--;
92
+
93
+ // If no branches point to this node anymore, obliterate it.
94
+ if (node->ref_count <= 0) {
95
+ // 1. Cascade destruction to children
96
+ for (int i = 0; i < 32; i++) {
97
+ if (node->children[i]) {
98
+ decref(node->children[i]);
99
+ }
100
+ }
101
+
102
+ // 2. Remove from global registry (Replace with Tombstone)
103
+ uint32_t idx = node->full_hash_val % REGISTRY_SIZE;
104
+ uint32_t start_idx = idx;
105
+
106
+ while (registry[idx] != NULL) {
107
+ if (registry[idx] == node) {
108
+ registry[idx] = TOMBSTONE;
109
+ break;
110
+ }
111
+ idx = (idx + 1) % REGISTRY_SIZE;
112
+ if (idx == start_idx) break;
113
+ }
114
+
115
+ // 3. Physically free memory back to the OS
116
+ if (node->value) free(node->value);
117
+ free(node);
118
+ physical_node_count--;
119
+ }
120
+ }
121
+
122
+ // -------------------------------------------------------------------
123
+ // Merkle-DAG Deduplication
124
+ // -------------------------------------------------------------------
76
125
  LQFTNode* get_canonical(void* value, uint64_t key_hash, LQFTNode** children) {
77
126
  if (!init_registry()) return NULL;
78
127
 
@@ -97,27 +146,47 @@ LQFTNode* get_canonical(void* value, uint64_t key_hash, LQFTNode** children) {
97
146
  sprintf(lookup_hash, "%016llx", (unsigned long long)full_hash);
98
147
  uint32_t idx = full_hash % REGISTRY_SIZE;
99
148
  uint32_t start_idx = idx;
149
+ int first_tombstone = -1;
100
150
 
151
+ // Search registry (Skipping Tomestones)
101
152
  while (registry[idx] != NULL) {
102
- if (strcmp(registry[idx]->struct_hash, lookup_hash) == 0) {
103
- if (value) free(value);
153
+ if (registry[idx] == TOMBSTONE) {
154
+ if (first_tombstone == -1) first_tombstone = (int)idx;
155
+ } else if (registry[idx]->full_hash_val == full_hash && strcmp(registry[idx]->struct_hash, lookup_hash) == 0) {
156
+ if (value) free(value); // Free duplicate string payload
104
157
  return registry[idx];
105
158
  }
106
159
  idx = (idx + 1) % REGISTRY_SIZE;
107
160
  if (idx == start_idx) break;
108
161
  }
109
162
 
163
+ // Create a physical new node if identity doesn't exist
110
164
  LQFTNode* new_node = create_node(value, key_hash);
111
165
  if (!new_node) return NULL;
166
+
112
167
  if (children) {
113
- for (int i = 0; i < 32; i++) new_node->children[i] = children[i];
168
+ for (int i = 0; i < 32; i++) {
169
+ new_node->children[i] = children[i];
170
+ // Anchor the child to this new parent
171
+ if (children[i]) children[i]->ref_count++;
172
+ }
114
173
  }
174
+
115
175
  strcpy(new_node->struct_hash, lookup_hash);
116
- registry[idx] = new_node;
176
+ new_node->full_hash_val = full_hash;
177
+
178
+ // Insert into registry (Reusing tombstones if possible)
179
+ // SYSTEM FIX: Explicitly cast first_tombstone to uint32_t to match idx
180
+ uint32_t insert_idx = (first_tombstone != -1) ? (uint32_t)first_tombstone : idx;
181
+ registry[insert_idx] = new_node;
117
182
  physical_node_count++;
183
+
118
184
  return new_node;
119
185
  }
120
186
 
187
+ // -------------------------------------------------------------------
188
+ // Python Wrapper API
189
+ // -------------------------------------------------------------------
121
190
  static PyObject* method_delete(PyObject* self, PyObject* args) {
122
191
  unsigned long long h;
123
192
  if (!PyArg_ParseTuple(args, "K", &h)) return NULL;
@@ -129,7 +198,6 @@ static PyObject* method_delete(PyObject* self, PyObject* args) {
129
198
  LQFTNode* curr = global_root;
130
199
  int bit_depth = 0;
131
200
 
132
- // Trace path to target
133
201
  while (curr != NULL && curr->value == NULL) {
134
202
  uint32_t segment = (h >> bit_depth) & MASK;
135
203
  path_nodes[path_len] = curr;
@@ -139,10 +207,10 @@ static PyObject* method_delete(PyObject* self, PyObject* args) {
139
207
  bit_depth += BIT_PARTITION;
140
208
  }
141
209
 
142
- // If not found, exit
210
+ // Key not found, abort
143
211
  if (curr == NULL || curr->key_hash != h) Py_RETURN_NONE;
144
212
 
145
- // Rebuild bottom-up: start with NULL (the deleted leaf)
213
+ LQFTNode* old_root = global_root;
146
214
  LQFTNode* new_sub_node = NULL;
147
215
 
148
216
  for (int i = path_len - 1; i >= 0; i--) {
@@ -151,7 +219,8 @@ static PyObject* method_delete(PyObject* self, PyObject* args) {
151
219
  LQFTNode* new_children[32];
152
220
  int has_other_children = 0;
153
221
 
154
- for (int j = 0; j < 32; j++) {
222
+ // SYSTEM FIX: Use uint32_t for j to safely compare with segment
223
+ for (uint32_t j = 0; j < 32; j++) {
155
224
  if (j == segment) new_children[j] = new_sub_node;
156
225
  else {
157
226
  new_children[j] = p_node->children[j];
@@ -159,31 +228,15 @@ static PyObject* method_delete(PyObject* self, PyObject* args) {
159
228
  }
160
229
  }
161
230
 
162
- if (!has_other_children && i > 0) {
163
- new_sub_node = NULL; // Contract empty branch
164
- } else {
165
- new_sub_node = get_canonical(NULL, 0, new_children);
166
- }
231
+ if (!has_other_children && i > 0) new_sub_node = NULL;
232
+ else new_sub_node = get_canonical(NULL, 0, new_children);
167
233
  }
168
234
 
235
+ // Anchor new root and release the old one
169
236
  global_root = (new_sub_node) ? new_sub_node : get_canonical(NULL, 0, NULL);
170
- Py_RETURN_NONE;
171
- }
237
+ if (global_root) global_root->ref_count++;
238
+ if (old_root) decref(old_root); // Trigger Active Deletion Cascade
172
239
 
173
- static PyObject* method_free_all(PyObject* self, PyObject* args) {
174
- if (registry != NULL) {
175
- for (int i = 0; i < REGISTRY_SIZE; i++) {
176
- if (registry[i] != NULL) {
177
- if (registry[i]->value) free(registry[i]->value);
178
- free(registry[i]);
179
- registry[i] = NULL;
180
- }
181
- }
182
- free(registry);
183
- registry = NULL;
184
- }
185
- physical_node_count = 0;
186
- global_root = NULL;
187
240
  Py_RETURN_NONE;
188
241
  }
189
242
 
@@ -191,15 +244,20 @@ static PyObject* method_insert(PyObject* self, PyObject* args) {
191
244
  unsigned long long h;
192
245
  char* val_str;
193
246
  if (!PyArg_ParseTuple(args, "Ks", &h, &val_str)) return NULL;
247
+
194
248
  if (!global_root) {
195
249
  if (!init_registry()) return PyErr_NoMemory();
196
250
  global_root = get_canonical(NULL, 0, NULL);
251
+ global_root->ref_count++;
197
252
  }
253
+
254
+ LQFTNode* old_root = global_root;
198
255
  LQFTNode* path_nodes[20];
199
256
  uint32_t path_segs[20];
200
257
  int path_len = 0;
201
258
  LQFTNode* curr = global_root;
202
259
  int bit_depth = 0;
260
+
203
261
  while (curr != NULL && curr->value == NULL) {
204
262
  uint32_t segment = (h >> bit_depth) & MASK;
205
263
  path_nodes[path_len] = curr;
@@ -209,10 +267,13 @@ static PyObject* method_insert(PyObject* self, PyObject* args) {
209
267
  curr = curr->children[segment];
210
268
  bit_depth += BIT_PARTITION;
211
269
  }
270
+
212
271
  LQFTNode* new_sub_node = NULL;
213
- if (curr == NULL) { new_sub_node = get_canonical(portable_strdup(val_str), h, NULL); }
214
- else if (curr->key_hash == h) { new_sub_node = get_canonical(portable_strdup(val_str), h, curr->children); }
215
- else {
272
+ if (curr == NULL) {
273
+ new_sub_node = get_canonical(portable_strdup(val_str), h, NULL);
274
+ } else if (curr->key_hash == h) {
275
+ new_sub_node = get_canonical(portable_strdup(val_str), h, curr->children);
276
+ } else {
216
277
  unsigned long long old_h = curr->key_hash;
217
278
  char* old_val = portable_strdup((char*)curr->value);
218
279
  int temp_depth = bit_depth;
@@ -227,10 +288,16 @@ static PyObject* method_insert(PyObject* self, PyObject* args) {
227
288
  new_children[s_new] = c_new;
228
289
  new_sub_node = get_canonical(NULL, 0, new_children);
229
290
  break;
230
- } else { path_nodes[path_len] = NULL; path_segs[path_len] = s_old; path_len++; temp_depth += BIT_PARTITION; }
291
+ } else {
292
+ path_nodes[path_len] = NULL;
293
+ path_segs[path_len] = s_old;
294
+ path_len++;
295
+ temp_depth += BIT_PARTITION;
296
+ }
231
297
  }
232
298
  if (new_sub_node == NULL) new_sub_node = get_canonical(portable_strdup(val_str), h, curr->children);
233
299
  }
300
+
234
301
  for (int i = path_len - 1; i >= 0; i--) {
235
302
  if (path_nodes[i] == NULL) {
236
303
  LQFTNode* new_children[32] = {NULL};
@@ -245,7 +312,12 @@ static PyObject* method_insert(PyObject* self, PyObject* args) {
245
312
  new_sub_node = get_canonical(p_node->value, p_node->key_hash, new_children);
246
313
  }
247
314
  }
315
+
316
+ // Anchor new root and release the old one
248
317
  global_root = new_sub_node;
318
+ global_root->ref_count++;
319
+ if (old_root) decref(old_root); // Trigger Active Deletion Cascade
320
+
249
321
  Py_RETURN_NONE;
250
322
  }
251
323
 
@@ -253,6 +325,7 @@ static PyObject* method_search(PyObject* self, PyObject* args) {
253
325
  unsigned long long h;
254
326
  if (!PyArg_ParseTuple(args, "K", &h)) return NULL;
255
327
  if (!global_root) Py_RETURN_NONE;
328
+
256
329
  LQFTNode* curr = global_root;
257
330
  int bit_depth = 0;
258
331
  while (curr != NULL && curr->value == NULL) {
@@ -260,17 +333,35 @@ static PyObject* method_search(PyObject* self, PyObject* args) {
260
333
  curr = curr->children[segment];
261
334
  bit_depth += BIT_PARTITION;
262
335
  }
336
+
263
337
  if (curr != NULL && curr->key_hash == h) return PyUnicode_FromString((char*)curr->value);
264
338
  Py_RETURN_NONE;
265
339
  }
266
340
 
341
+ static PyObject* method_free_all(PyObject* self, PyObject* args) {
342
+ if (registry != NULL) {
343
+ for (int i = 0; i < REGISTRY_SIZE; i++) {
344
+ if (registry[i] != NULL && registry[i] != TOMBSTONE) {
345
+ if (registry[i]->value) free(registry[i]->value);
346
+ free(registry[i]);
347
+ }
348
+ registry[i] = NULL;
349
+ }
350
+ free(registry);
351
+ registry = NULL;
352
+ }
353
+ physical_node_count = 0;
354
+ global_root = NULL;
355
+ Py_RETURN_NONE;
356
+ }
357
+
267
358
  static PyObject* method_get_metrics(PyObject* self, PyObject* args) {
268
359
  return Py_BuildValue("{s:i}", "physical_nodes", physical_node_count);
269
360
  }
270
361
 
271
362
  static PyMethodDef LQFTMethods[] = {
272
363
  {"insert", method_insert, METH_VARARGS, "Insert key-value"},
273
- {"delete", method_delete, METH_VARARGS, "Delete key"},
364
+ {"delete", method_delete, METH_VARARGS, "Active ARC Delete key"},
274
365
  {"search", method_search, METH_VARARGS, "Search key"},
275
366
  {"get_metrics", method_get_metrics, METH_VARARGS, "Get engine stats"},
276
367
  {"free_all", method_free_all, METH_VARARGS, "Total memory wipe"},
@@ -1,8 +1,10 @@
1
1
  import hashlib
2
2
  import weakref
3
+ import psutil
4
+ import os
3
5
 
4
6
  # ---------------------------------------------------------
5
- # LEGACY PURE PYTHON LQFT (For reference/fallback/benchmarks)
7
+ # LEGACY PURE PYTHON LQFT (For reference/fallback)
6
8
  # ---------------------------------------------------------
7
9
  class LQFTNode:
8
10
  __slots__ = ['children', 'value', 'key_hash', 'struct_hash', '__weakref__']
@@ -39,7 +41,6 @@ class LQFTNode:
39
41
  return cls._null_cache['null']
40
42
 
41
43
  class LQFT:
42
- """Legacy Pure Python Iterative Implementation."""
43
44
  def __init__(self, bit_partition=5, max_bits=256):
44
45
  self.partition = bit_partition
45
46
  self.max_bits = max_bits
@@ -62,7 +63,7 @@ class LQFT:
62
63
  break
63
64
  curr = curr.children[segment]
64
65
  bit_depth += self.partition
65
-
66
+
66
67
  new_sub_node = None
67
68
  if curr is null_node:
68
69
  new_sub_node = LQFTNode.get_canonical(value, None, h)
@@ -82,7 +83,7 @@ class LQFT:
82
83
  temp_depth += self.partition
83
84
  if new_sub_node is None:
84
85
  new_sub_node = LQFTNode.get_canonical(value, curr.children, h)
85
-
86
+
86
87
  for entry in reversed(path):
87
88
  if entry[0] == "split":
88
89
  new_sub_node = LQFTNode.get_canonical(None, {entry[1]: new_sub_node}, None)
@@ -103,8 +104,9 @@ class LQFT:
103
104
  if bit_depth >= self.max_bits: break
104
105
  return None
105
106
 
107
+
106
108
  # ---------------------------------------------------------
107
- # NEW: ADAPTIVE ENTERPRISE ENGINE (MScAC Portfolio)
109
+ # ADAPTIVE ENTERPRISE ENGINE (v0.3.0)
108
110
  # ---------------------------------------------------------
109
111
  try:
110
112
  import lqft_c_engine
@@ -123,13 +125,32 @@ class AdaptiveLQFT:
123
125
  self.threshold = migration_threshold
124
126
  self.size = 0
125
127
  self.is_native = False
126
-
127
- # The "Mini Version": Python's highly optimized built-in dictionary
128
128
  self._light_store = {}
129
+
130
+ # --- Systems Architecture: Process Memory Limit ---
131
+ self.auto_purge_enabled = True
132
+ # Instead of global OS RAM, we set a hard cap for THIS specific process.
133
+ self.max_memory_mb = 1000.0 # 1 Gigabyte Limit
134
+ self.total_ops = 0
135
+ self._process = psutil.Process(os.getpid())
136
+
137
+ def _validate_type(self, key, value=None):
138
+ """Strict type guarding to prevent arbitrary objects from bleeding into C-Memory."""
139
+ if not isinstance(key, str):
140
+ raise TypeError(f"LQFT keys must be strings. Received: {type(key).__name__}")
141
+ if value is not None and not isinstance(value, str):
142
+ raise TypeError(f"LQFT values must be strings. Received: {type(value).__name__}")
129
143
 
130
144
  def _get_64bit_hash(self, key):
131
145
  """Generates a 64-bit unsigned hash for the C-Engine."""
132
- return int(hashlib.md5(str(key).encode()).hexdigest()[:16], 16)
146
+ return int(hashlib.md5(key.encode()).hexdigest()[:16], 16)
147
+
148
+ def purge(self):
149
+ """Manual Purge Override to instantly free RAM."""
150
+ current_mb = self._process.memory_info().rss / (1024 * 1024)
151
+ if current_mb >= self.max_memory_mb:
152
+ print(f"\n[⚠️ CIRCUIT Breaker] Engine exceeded {self.max_memory_mb} MB limit (Currently {current_mb:.1f} MB). Auto-Purging!")
153
+ self.clear()
133
154
 
134
155
  def _migrate_to_native(self):
135
156
  """The 'Curve Flip' mechanism: moves all data to the Heavy Engine."""
@@ -140,36 +161,36 @@ class AdaptiveLQFT:
140
161
 
141
162
  for key, val in self._light_store.items():
142
163
  h = self._get_64bit_hash(key)
143
- lqft_c_engine.insert(h, str(val))
164
+ lqft_c_engine.insert(h, val)
144
165
 
145
- # Clear the lightweight store to free up memory
146
166
  self._light_store.clear()
147
167
  self.is_native = True
148
168
 
149
169
  def insert(self, key, value):
170
+ # 1. Type Guard (Blocks the Poison Pill test)
171
+ self._validate_type(key, value)
172
+
173
+ # 2. Performance Safety: Process Memory Check
174
+ self.total_ops += 1
175
+ if self.auto_purge_enabled and self.total_ops % 5000 == 0:
176
+ current_mb = self._process.memory_info().rss / (1024 * 1024)
177
+ if current_mb >= self.max_memory_mb:
178
+ self.purge()
179
+
150
180
  if not self.is_native:
151
- # Phase 1: Small Data Operations
152
- if key not in self._light_store:
181
+ if key not in self._light_store:
153
182
  self.size += 1
154
183
  self._light_store[key] = value
155
184
 
156
- # Check for migration threshold
157
- if self.size >= self.threshold:
185
+ if self.size >= self.threshold:
158
186
  self._migrate_to_native()
159
- else:
160
- # Phase 2: Massive Data Operations (Native C-Heap)
161
- h = self._get_64bit_hash(key)
162
- lqft_c_engine.insert(h, str(value))
163
-
164
- def search(self, key):
165
- if not self.is_native:
166
- return self._light_store.get(key, None)
167
187
  else:
168
188
  h = self._get_64bit_hash(key)
169
- return lqft_c_engine.search(h)
189
+ lqft_c_engine.insert(h, value)
170
190
 
171
191
  def remove(self, key):
172
- """Deletes a key from either the light store or the Merkle tree."""
192
+ self._validate_type(key)
193
+
173
194
  if not self.is_native:
174
195
  if key in self._light_store:
175
196
  del self._light_store[key]
@@ -179,35 +200,36 @@ class AdaptiveLQFT:
179
200
  lqft_c_engine.delete(h)
180
201
 
181
202
  def delete(self, key):
182
- """Alias for remove to satisfy all testing suites."""
183
203
  self.remove(key)
184
204
 
205
+ def search(self, key):
206
+ self._validate_type(key)
207
+
208
+ if not self.is_native:
209
+ return self._light_store.get(key, None)
210
+ else:
211
+ h = self._get_64bit_hash(key)
212
+ return lqft_c_engine.search(h)
213
+
185
214
  def clear(self):
186
- """
187
- Memory Reclamation: Manually trigger heap cleanup.
188
- In the Adaptive model, this handles both the Python dict and the C-Registry.
189
- """
190
215
  self._light_store.clear()
191
216
  self.size = 0
192
- if C_ENGINE_READY:
217
+ if C_ENGINE_READY:
193
218
  return lqft_c_engine.free_all()
194
219
  return 0
195
220
 
196
221
  def get_stats(self):
197
- """Fetches memory metrics from the C-Engine if active."""
198
- if self.is_native and C_ENGINE_READY:
222
+ if self.is_native and C_ENGINE_READY:
199
223
  return lqft_c_engine.get_metrics()
200
224
  return {"physical_nodes": 0}
201
225
 
202
226
  def __del__(self):
203
- """Finalizer: Reclaims unmanaged C memory when the Python object is deleted."""
204
- try:
227
+ try:
205
228
  self.clear()
206
- except:
229
+ except:
207
230
  pass
208
231
 
209
232
  def status(self):
210
- """Returns the current state of the engine."""
211
233
  return {
212
234
  "mode": "Native Merkle-DAG" if self.is_native else "Lightweight C-Hash",
213
235
  "items": self.size if not self.is_native else lqft_c_engine.get_metrics().get('physical_nodes', self.size),
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lqft-python-engine
3
- Version: 0.2.0
4
- Summary: LQFT Engine: Full CRUD Support & Enterprise Scaling (v4.5 Stable)
3
+ Version: 0.3.0
4
+ Summary: LQFT Engine: Memory Circuit Breaker & Strict Type Safety (v5.0 Stable)
5
5
  Home-page: https://github.com/ParjadM/Log-Quantum-Fractal-Tree-LQFT-
6
6
  Author: Parjad Minooei
7
7
  License: MIT
@@ -27,8 +27,8 @@ lqft_extension = Extension(
27
27
 
28
28
  setup(
29
29
  name="lqft-python-engine",
30
- version="0.2.0",
31
- description="LQFT Engine: Full CRUD Support & Enterprise Scaling (v4.5 Stable)",
30
+ version="0.3.0",
31
+ description="LQFT Engine: Memory Circuit Breaker & Strict Type Safety (v5.0 Stable)",
32
32
  long_description=long_description,
33
33
  long_description_content_type="text/markdown",
34
34
  author="Parjad Minooei",