cuemap 0.7.0__tar.gz → 0.7.2__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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuemap
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Summary: CueMap Python SDK - High-performance temporal-associative memory
5
5
  Author-email: Kaan Demirel <hello@cuemap.dev>
6
6
  License: MIT
@@ -42,14 +42,14 @@ Dynamic: license-file
42
42
  CueMap implements a **Continuous Gradient Algorithm** optimized for associative data structures:
43
43
 
44
44
  1. **Intersection (Context Filter)**: Triangulates relevant memories by overlapping cues
45
- 2. **CuePack-Guided Intent Routing**: Uses compiled deterministic rules to add structural facets and weighted intent cues without runtime model calls.
45
+ 2. **Local semantic reranking**: Uses bundled qint8 MiniLM-L3 by default, or q4 MiniLM-L3 with the edge profile, for bounded semantic ranking inside the engine.
46
46
  3. **Recency & Salience (Signal Dynamics)**: Balances fresh data with salient, high-signal events prioritized by an adaptive impact scoring module.
47
47
  4. **Reinforcement (Access-based Learning)**: Frequently accessed memories gain signal strength, remaining highly accessible even as they age.
48
48
  5. **Deterministic Facets & Intent Routing**: Extracts synchronous source, evidence, temporal, type, and entity facets, then uses sparse intent cues and reranking during recall.
49
49
 
50
- As of v0.7.0, CueMap's core path is deterministic and embedding-free. GloVe/Ollama cue generation, WordNet/POS expansion, semantic bridges, pattern completion, external lexicon graphs, context expansion/speculation endpoints, and autonomous consolidation have been removed from the core engine.
50
+ As of v0.7.2, CueMap keeps deterministic lexical candidate discovery and adds bundled qint8 `all-MiniLM-L3-v2` for bounded hybrid semantic and intent reranking. The `edge` engine profile uses a q4 build of the same model. No runtime model download is required, and callers can disable the encoder or provide their own vectors.
51
51
 
52
- v0.7.0 also uses numeric per-project memory IDs everywhere. If callers need deterministic upsert/dedupe identity, pass `source_key`; memory IDs remain compact runtime addresses.
52
+ v0.7.2 also preserves numeric per-project memory IDs everywhere. If callers need deterministic upsert/dedupe identity, pass `source_key`; memory IDs remain compact runtime addresses.
53
53
 
54
54
  Use this SDK to talk to the Rust engine from Python applications.
55
55
 
@@ -134,9 +134,9 @@ print(response["proof"])
134
134
  # Cryptographic proof of context retrieval
135
135
  ```
136
136
 
137
- ### v0.7 Recall Controls
137
+ ### v0.7.2 Recall Controls
138
138
 
139
- CueMap v0.7 adds temporal query intent, CueBridge artifact expansion, and optional reconstruction passes for longer conversational/codebase context.
139
+ CueMap v0.7.2 adds local semantic query signals alongside temporal query intent and optional reconstruction passes for longer conversational/codebase context.
140
140
 
141
141
  ```python
142
142
  results = client.recall(
@@ -145,11 +145,23 @@ results = client.recall(
145
145
  ordered_reconstruction="auto",
146
146
  evidence_coverage="auto",
147
147
  parent_fusion="auto",
148
- cuepacks=["default"],
148
+ semantic_mode="hybrid",
149
149
  explain=True,
150
150
  )
151
151
  ```
152
152
 
153
+ Use `semantic_mode="lexical"` for a semantic-reranker-disabled comparison, `"semantic"` for vector candidate discovery, or `"hybrid"` (the engine default) to rerank lexical candidates. `query_embedding` supplies a precomputed vector when the application owns the embedding provider.
154
+
155
+ Classify query or memory intent with the same local engine model. Returned scores are ranking signals, not calibrated probabilities:
156
+
157
+ ```python
158
+ classification = client.classify_intent(
159
+ "What did we decide about auth retries?",
160
+ target="query",
161
+ )
162
+ print(classification["primary_intent"], classification["recall_eligible"])
163
+ ```
164
+
153
165
  ### Cloud Backup (v0.6.1)
154
166
 
155
167
  Manage project snapshots in the cloud (S3, GCS, Azure).
@@ -186,6 +198,20 @@ client.ingest_content(
186
198
  )
187
199
  ```
188
200
 
201
+ When an application chunks content itself, pass exactly one vector per produced chunk with `embeddings=[[...], [...]]`.
202
+
203
+ Preview and persist a repository ingestion scope:
204
+
205
+ ```python
206
+ preview = client.preview_directory("/work/my-app", included_paths=["src"])
207
+ client.set_project_watch_dir(
208
+ "repo-my-app",
209
+ "/work/my-app",
210
+ included_paths=["src", "README.md"],
211
+ )
212
+ scope = client.get_project_watch_dir("repo-my-app")
213
+ ```
214
+
189
215
  ### Lexicon Management (v0.6+)
190
216
 
191
217
  Inspect and wire the brain's associations manually.
@@ -208,19 +234,7 @@ Check the progress of background ingestion tasks.
208
234
  ```python
209
235
  status = client.jobs_status()
210
236
  print(f"Ingested: {status['writes_completed']} / {status['writes_total']}")
211
- ```
212
-
213
- ### Advanced Brain Control
214
-
215
- Disable specific brain modules for deterministic debugging.
216
-
217
- ```python
218
- results = client.recall(
219
- "urgent issue",
220
- disable_salience_bias=True,
221
- disable_alias_expansion=True,
222
- disable_cuebridge_artifacts=True,
223
- )
237
+ print(f"Intent ready: {status.get('intent_ready', False)}")
224
238
  ```
225
239
 
226
240
  ## Async Support
@@ -233,11 +247,6 @@ async with AsyncCueMap() as client:
233
247
  await client.recall(cues=["note"])
234
248
  ```
235
249
 
236
- ## Performance
237
-
238
- - **Write Latency**: ~2ms (O(1) complexity)
239
- - **Read Latency**: ~3ms (P99, 1M memories)
240
-
241
250
  ## License
242
251
 
243
252
  MIT
@@ -7,14 +7,14 @@
7
7
  CueMap implements a **Continuous Gradient Algorithm** optimized for associative data structures:
8
8
 
9
9
  1. **Intersection (Context Filter)**: Triangulates relevant memories by overlapping cues
10
- 2. **CuePack-Guided Intent Routing**: Uses compiled deterministic rules to add structural facets and weighted intent cues without runtime model calls.
10
+ 2. **Local semantic reranking**: Uses bundled qint8 MiniLM-L3 by default, or q4 MiniLM-L3 with the edge profile, for bounded semantic ranking inside the engine.
11
11
  3. **Recency & Salience (Signal Dynamics)**: Balances fresh data with salient, high-signal events prioritized by an adaptive impact scoring module.
12
12
  4. **Reinforcement (Access-based Learning)**: Frequently accessed memories gain signal strength, remaining highly accessible even as they age.
13
13
  5. **Deterministic Facets & Intent Routing**: Extracts synchronous source, evidence, temporal, type, and entity facets, then uses sparse intent cues and reranking during recall.
14
14
 
15
- As of v0.7.0, CueMap's core path is deterministic and embedding-free. GloVe/Ollama cue generation, WordNet/POS expansion, semantic bridges, pattern completion, external lexicon graphs, context expansion/speculation endpoints, and autonomous consolidation have been removed from the core engine.
15
+ As of v0.7.2, CueMap keeps deterministic lexical candidate discovery and adds bundled qint8 `all-MiniLM-L3-v2` for bounded hybrid semantic and intent reranking. The `edge` engine profile uses a q4 build of the same model. No runtime model download is required, and callers can disable the encoder or provide their own vectors.
16
16
 
17
- v0.7.0 also uses numeric per-project memory IDs everywhere. If callers need deterministic upsert/dedupe identity, pass `source_key`; memory IDs remain compact runtime addresses.
17
+ v0.7.2 also preserves numeric per-project memory IDs everywhere. If callers need deterministic upsert/dedupe identity, pass `source_key`; memory IDs remain compact runtime addresses.
18
18
 
19
19
  Use this SDK to talk to the Rust engine from Python applications.
20
20
 
@@ -99,9 +99,9 @@ print(response["proof"])
99
99
  # Cryptographic proof of context retrieval
100
100
  ```
101
101
 
102
- ### v0.7 Recall Controls
102
+ ### v0.7.2 Recall Controls
103
103
 
104
- CueMap v0.7 adds temporal query intent, CueBridge artifact expansion, and optional reconstruction passes for longer conversational/codebase context.
104
+ CueMap v0.7.2 adds local semantic query signals alongside temporal query intent and optional reconstruction passes for longer conversational/codebase context.
105
105
 
106
106
  ```python
107
107
  results = client.recall(
@@ -110,11 +110,23 @@ results = client.recall(
110
110
  ordered_reconstruction="auto",
111
111
  evidence_coverage="auto",
112
112
  parent_fusion="auto",
113
- cuepacks=["default"],
113
+ semantic_mode="hybrid",
114
114
  explain=True,
115
115
  )
116
116
  ```
117
117
 
118
+ Use `semantic_mode="lexical"` for a semantic-reranker-disabled comparison, `"semantic"` for vector candidate discovery, or `"hybrid"` (the engine default) to rerank lexical candidates. `query_embedding` supplies a precomputed vector when the application owns the embedding provider.
119
+
120
+ Classify query or memory intent with the same local engine model. Returned scores are ranking signals, not calibrated probabilities:
121
+
122
+ ```python
123
+ classification = client.classify_intent(
124
+ "What did we decide about auth retries?",
125
+ target="query",
126
+ )
127
+ print(classification["primary_intent"], classification["recall_eligible"])
128
+ ```
129
+
118
130
  ### Cloud Backup (v0.6.1)
119
131
 
120
132
  Manage project snapshots in the cloud (S3, GCS, Azure).
@@ -151,6 +163,20 @@ client.ingest_content(
151
163
  )
152
164
  ```
153
165
 
166
+ When an application chunks content itself, pass exactly one vector per produced chunk with `embeddings=[[...], [...]]`.
167
+
168
+ Preview and persist a repository ingestion scope:
169
+
170
+ ```python
171
+ preview = client.preview_directory("/work/my-app", included_paths=["src"])
172
+ client.set_project_watch_dir(
173
+ "repo-my-app",
174
+ "/work/my-app",
175
+ included_paths=["src", "README.md"],
176
+ )
177
+ scope = client.get_project_watch_dir("repo-my-app")
178
+ ```
179
+
154
180
  ### Lexicon Management (v0.6+)
155
181
 
156
182
  Inspect and wire the brain's associations manually.
@@ -173,19 +199,7 @@ Check the progress of background ingestion tasks.
173
199
  ```python
174
200
  status = client.jobs_status()
175
201
  print(f"Ingested: {status['writes_completed']} / {status['writes_total']}")
176
- ```
177
-
178
- ### Advanced Brain Control
179
-
180
- Disable specific brain modules for deterministic debugging.
181
-
182
- ```python
183
- results = client.recall(
184
- "urgent issue",
185
- disable_salience_bias=True,
186
- disable_alias_expansion=True,
187
- disable_cuebridge_artifacts=True,
188
- )
202
+ print(f"Intent ready: {status.get('intent_ready', False)}")
189
203
  ```
190
204
 
191
205
  ## Async Support
@@ -198,11 +212,6 @@ async with AsyncCueMap() as client:
198
212
  await client.recall(cues=["note"])
199
213
  ```
200
214
 
201
- ## Performance
202
-
203
- - **Write Latency**: ~2ms (O(1) complexity)
204
- - **Read Latency**: ~3ms (P99, 1M memories)
205
-
206
215
  ## License
207
216
 
208
217
  MIT
@@ -18,14 +18,17 @@ Example:
18
18
  """
19
19
 
20
20
  from .client import CueMap, AsyncCueMap
21
+ from .embedded import EmbeddedCueMap, resolve_cuemap_binary
21
22
  from .models import Memory, RecallResult
22
23
  from .exceptions import CueMapError, ConnectionError, AuthenticationError
23
24
  from .grounding import CueMapGroundingRetriever, AsyncCueMapGroundingRetriever
24
25
 
25
- __version__ = "0.7.0"
26
+ __version__ = "0.7.2"
26
27
  __all__ = [
27
28
  "CueMap",
28
29
  "AsyncCueMap",
30
+ "EmbeddedCueMap",
31
+ "resolve_cuemap_binary",
29
32
  "Memory",
30
33
  "RecallResult",
31
34
  "CueMapError",
@@ -16,7 +16,8 @@ class CueMap:
16
16
  """
17
17
  Pure CueMap client.
18
18
 
19
- No auto-cue extraction. No semantic matching. Just fast memory storage.
19
+ The engine performs deterministic cue extraction and can use its bundled
20
+ local MiniLM encoder for semantic retrieval when configured.
20
21
 
21
22
  Example:
22
23
  >>> client = CueMap()
@@ -65,7 +66,8 @@ class CueMap:
65
66
  metadata: Optional[Dict[str, Any]] = None,
66
67
  disable_temporal_chunking: bool = False,
67
68
  source_key: Optional[str] = None,
68
- cuepacks: Optional[List[str]] = None,
69
+ event_time: Optional[float] = None,
70
+ embedding: Optional[List[float]] = None,
69
71
  async_ingest: bool = False,
70
72
  minimal_response: bool = False,
71
73
  trace_timing: bool = False,
@@ -77,6 +79,7 @@ class CueMap:
77
79
  content: Memory content
78
80
  cues: Optional list of cues (tags) for retrieval
79
81
  metadata: Optional metadata
82
+ event_time: Original event timestamp as Unix seconds. Defaults to ingestion time.
80
83
 
81
84
  Returns:
82
85
  Memory ID
@@ -95,7 +98,8 @@ class CueMap:
95
98
  "metadata": metadata,
96
99
  "disable_temporal_chunking": disable_temporal_chunking,
97
100
  "source_key": source_key,
98
- "cuepacks": cuepacks,
101
+ "event_time": event_time,
102
+ "embedding": embedding,
99
103
  "async_ingest": async_ingest,
100
104
  "minimal_response": minimal_response,
101
105
  "trace_timing": trace_timing,
@@ -133,6 +137,21 @@ class CueMap:
133
137
  raise CueMapError(f"Failed to add memories: {response.text}")
134
138
 
135
139
  return response.json()
140
+
141
+ def classify_intent(
142
+ self,
143
+ text: str,
144
+ target: str = "query",
145
+ ) -> Dict[str, Any]:
146
+ """Classify query or memory intent with the engine's local model."""
147
+ response = self.client.post(
148
+ "/intent/classify",
149
+ json={"text": text, "target": target},
150
+ headers=self._headers(),
151
+ )
152
+ if response.status_code != 200:
153
+ raise CueMapError(f"Failed to classify intent: {response.text}")
154
+ return response.json()
136
155
 
137
156
  def recall(
138
157
  self,
@@ -149,7 +168,6 @@ class CueMap:
149
168
  disable_alias_expansion: bool = True,
150
169
  trace_timing: bool = False,
151
170
  expansion_depth: int = 1,
152
- cuepacks: Optional[List[str]] = None,
153
171
  parent_fusion: str = "off",
154
172
  parent_fusion_limit: int = 80,
155
173
  parent_fusion_min_chunks: int = 2,
@@ -165,6 +183,8 @@ class CueMap:
165
183
  cuebridge_gap_limit: int = 6,
166
184
  disable_pattern_completion: Optional[bool] = None,
167
185
  disable_systems_consolidation: Optional[bool] = None,
186
+ semantic_mode: str = "hybrid",
187
+ query_embedding: Optional[List[float]] = None,
168
188
  ) -> List[RecallResult]:
169
189
  """
170
190
  Recall memories by cues or natural language.
@@ -209,6 +229,8 @@ class CueMap:
209
229
  "evidence_coverage_max_sessions": evidence_coverage_max_sessions,
210
230
  "disable_cuebridge_artifacts": disable_cuebridge_artifacts,
211
231
  "cuebridge_gap_limit": cuebridge_gap_limit,
232
+ "semantic_mode": semantic_mode,
233
+ "query_embedding": query_embedding,
212
234
  }
213
235
  if cues:
214
236
  payload["cues"] = cues
@@ -220,9 +242,6 @@ class CueMap:
220
242
  payload["min_intersection"] = min_intersection
221
243
  if projects:
222
244
  payload["projects"] = projects
223
- if cuepacks:
224
- payload["cuepacks"] = cuepacks
225
-
226
245
  response = self.client.post(
227
246
  "/recall",
228
247
  json=_clean_payload(payload),
@@ -251,7 +270,6 @@ class CueMap:
251
270
  disable_salience_bias: bool = False,
252
271
  disable_alias_expansion: bool = True,
253
272
  expansion_depth: int = 1,
254
- cuepacks: Optional[List[str]] = None,
255
273
  disable_pattern_completion: Optional[bool] = None,
256
274
  disable_systems_consolidation: Optional[bool] = None,
257
275
  ) -> Dict[str, Any]:
@@ -275,7 +293,6 @@ class CueMap:
275
293
  "disable_salience_bias": disable_salience_bias,
276
294
  "disable_alias_expansion": disable_alias_expansion,
277
295
  "expansion_depth": expansion_depth,
278
- "cuepacks": cuepacks,
279
296
  }),
280
297
  headers=self._headers()
281
298
  )
@@ -365,12 +382,14 @@ class CueMap:
365
382
  watch_dir: str,
366
383
  ignored_patterns: Optional[List[str]] = None,
367
384
  ignored_extensions: Optional[List[str]] = None,
385
+ included_paths: Optional[List[str]] = None,
368
386
  ) -> Dict[str, Any]:
369
387
  """Set the self-learning agent watch directory for a project."""
370
388
  response = self.client.post(
371
389
  f"/projects/{project_id}/watch-dir",
372
390
  json=_clean_payload({
373
391
  "watch_dir": watch_dir,
392
+ "included_paths": included_paths,
374
393
  "ignored_patterns": ignored_patterns,
375
394
  "ignored_extensions": ignored_extensions,
376
395
  }),
@@ -380,6 +399,38 @@ class CueMap:
380
399
  raise CueMapError(f"Failed to set project watch directory: {response.text}")
381
400
  return response.json()
382
401
 
402
+ def get_project_watch_dir(self, project_id: str) -> Dict[str, Any]:
403
+ """Read a project's persisted repository ingestion scope."""
404
+ response = self.client.get(
405
+ f"/projects/{project_id}/watch-dir",
406
+ headers=self._headers(),
407
+ )
408
+ if response.status_code != 200:
409
+ raise CueMapError(f"Failed to get project watch directory: {response.text}")
410
+ return response.json()
411
+
412
+ def preview_directory(
413
+ self,
414
+ watch_dir: str,
415
+ included_paths: Optional[List[str]] = None,
416
+ ignored_patterns: Optional[List[str]] = None,
417
+ ignored_extensions: Optional[List[str]] = None,
418
+ ) -> Dict[str, Any]:
419
+ """Preview supported repository files without ingesting content."""
420
+ response = self.client.post(
421
+ "/ingest/directory/preview",
422
+ json=_clean_payload({
423
+ "watch_dir": watch_dir,
424
+ "included_paths": included_paths,
425
+ "ignored_patterns": ignored_patterns,
426
+ "ignored_extensions": ignored_extensions,
427
+ }),
428
+ headers=self._headers(),
429
+ )
430
+ if response.status_code != 200:
431
+ raise CueMapError(f"Failed to preview directory: {response.text}")
432
+ return response.json()
433
+
383
434
  def add_alias(self, from_cue: str, to_cue: str, weight: float = 1.0) -> bool:
384
435
  """Add an alias (manual cue mapping)."""
385
436
  response = self.client.post(
@@ -574,6 +625,7 @@ class CueMap:
574
625
  source_key: Optional[str] = None,
575
626
  metadata: Optional[Dict[str, Any]] = None,
576
627
  structural_cues: Optional[List[str]] = None,
628
+ embeddings: Optional[List[List[float]]] = None,
577
629
  segmenter: str = "sentence_window",
578
630
  segment_window_size: Optional[int] = None,
579
631
  segment_overlap: Optional[int] = None,
@@ -589,6 +641,7 @@ class CueMap:
589
641
  "source_key": source_key,
590
642
  "metadata": metadata,
591
643
  "structural_cues": structural_cues,
644
+ "embeddings": embeddings,
592
645
  "segmenter": segmenter,
593
646
  "segment_window_size": segment_window_size,
594
647
  "segment_overlap": segment_overlap,
@@ -624,7 +677,6 @@ class CueMap:
624
677
  metadata: Optional[Dict[str, Any]] = None,
625
678
  existing_cues: Optional[List[str]] = None,
626
679
  available_cues: Optional[List[str]] = None,
627
- cuepacks: Optional[List[str]] = None,
628
680
  filename: Optional[str] = None,
629
681
  segmenter: str = "sentence_window",
630
682
  segment_window_size: Optional[int] = None,
@@ -641,7 +693,6 @@ class CueMap:
641
693
  "metadata": metadata,
642
694
  "existing_cues": existing_cues,
643
695
  "available_cues": available_cues,
644
- "cuepacks": cuepacks,
645
696
  "filename": filename,
646
697
  "segmenter": segmenter,
647
698
  "segment_window_size": segment_window_size,
@@ -736,7 +787,8 @@ class AsyncCueMap:
736
787
  metadata: Optional[Dict[str, Any]] = None,
737
788
  disable_temporal_chunking: bool = False,
738
789
  source_key: Optional[str] = None,
739
- cuepacks: Optional[List[str]] = None,
790
+ event_time: Optional[float] = None,
791
+ embedding: Optional[List[float]] = None,
740
792
  async_ingest: bool = False,
741
793
  minimal_response: bool = False,
742
794
  trace_timing: bool = False,
@@ -750,7 +802,8 @@ class AsyncCueMap:
750
802
  "metadata": metadata,
751
803
  "disable_temporal_chunking": disable_temporal_chunking,
752
804
  "source_key": source_key,
753
- "cuepacks": cuepacks,
805
+ "event_time": event_time,
806
+ "embedding": embedding,
754
807
  "async_ingest": async_ingest,
755
808
  "minimal_response": minimal_response,
756
809
  "trace_timing": trace_timing,
@@ -788,6 +841,21 @@ class AsyncCueMap:
788
841
  raise CueMapError(f"Failed to add memories: {response.text}")
789
842
 
790
843
  return response.json()
844
+
845
+ async def classify_intent(
846
+ self,
847
+ text: str,
848
+ target: str = "query",
849
+ ) -> Dict[str, Any]:
850
+ """Classify query or memory intent with the engine's local model (async)."""
851
+ response = await self.client.post(
852
+ "/intent/classify",
853
+ json={"text": text, "target": target},
854
+ headers=self._headers(),
855
+ )
856
+ if response.status_code != 200:
857
+ raise CueMapError(f"Failed to classify intent: {response.text}")
858
+ return response.json()
791
859
 
792
860
  async def recall(
793
861
  self,
@@ -804,7 +872,6 @@ class AsyncCueMap:
804
872
  disable_alias_expansion: bool = True,
805
873
  trace_timing: bool = False,
806
874
  expansion_depth: int = 1,
807
- cuepacks: Optional[List[str]] = None,
808
875
  parent_fusion: str = "off",
809
876
  parent_fusion_limit: int = 80,
810
877
  parent_fusion_min_chunks: int = 2,
@@ -820,6 +887,8 @@ class AsyncCueMap:
820
887
  cuebridge_gap_limit: int = 6,
821
888
  disable_pattern_completion: Optional[bool] = None,
822
889
  disable_systems_consolidation: Optional[bool] = None,
890
+ semantic_mode: str = "hybrid",
891
+ query_embedding: Optional[List[float]] = None,
823
892
  ) -> List[RecallResult]:
824
893
  """Recall memories (async)."""
825
894
  payload = {
@@ -844,6 +913,8 @@ class AsyncCueMap:
844
913
  "evidence_coverage_max_sessions": evidence_coverage_max_sessions,
845
914
  "disable_cuebridge_artifacts": disable_cuebridge_artifacts,
846
915
  "cuebridge_gap_limit": cuebridge_gap_limit,
916
+ "semantic_mode": semantic_mode,
917
+ "query_embedding": query_embedding,
847
918
  }
848
919
  if cues:
849
920
  payload["cues"] = cues
@@ -855,9 +926,6 @@ class AsyncCueMap:
855
926
  payload["min_intersection"] = min_intersection
856
927
  if projects:
857
928
  payload["projects"] = projects
858
- if cuepacks:
859
- payload["cuepacks"] = cuepacks
860
-
861
929
  response = await self.client.post(
862
930
  "/recall",
863
931
  json=_clean_payload(payload),
@@ -886,7 +954,6 @@ class AsyncCueMap:
886
954
  disable_salience_bias: bool = False,
887
955
  disable_alias_expansion: bool = True,
888
956
  expansion_depth: int = 1,
889
- cuepacks: Optional[List[str]] = None,
890
957
  disable_pattern_completion: Optional[bool] = None,
891
958
  disable_systems_consolidation: Optional[bool] = None,
892
959
  ) -> Dict[str, Any]:
@@ -903,7 +970,6 @@ class AsyncCueMap:
903
970
  "disable_salience_bias": disable_salience_bias,
904
971
  "disable_alias_expansion": disable_alias_expansion,
905
972
  "expansion_depth": expansion_depth,
906
- "cuepacks": cuepacks,
907
973
  }),
908
974
  headers=self._headers()
909
975
  )
@@ -993,12 +1059,14 @@ class AsyncCueMap:
993
1059
  watch_dir: str,
994
1060
  ignored_patterns: Optional[List[str]] = None,
995
1061
  ignored_extensions: Optional[List[str]] = None,
1062
+ included_paths: Optional[List[str]] = None,
996
1063
  ) -> Dict[str, Any]:
997
1064
  """Set the self-learning agent watch directory for a project (async)."""
998
1065
  response = await self.client.post(
999
1066
  f"/projects/{project_id}/watch-dir",
1000
1067
  json=_clean_payload({
1001
1068
  "watch_dir": watch_dir,
1069
+ "included_paths": included_paths,
1002
1070
  "ignored_patterns": ignored_patterns,
1003
1071
  "ignored_extensions": ignored_extensions,
1004
1072
  }),
@@ -1008,6 +1076,38 @@ class AsyncCueMap:
1008
1076
  raise CueMapError(f"Failed to set project watch directory: {response.text}")
1009
1077
  return response.json()
1010
1078
 
1079
+ async def get_project_watch_dir(self, project_id: str) -> Dict[str, Any]:
1080
+ """Read a project's persisted repository ingestion scope (async)."""
1081
+ response = await self.client.get(
1082
+ f"/projects/{project_id}/watch-dir",
1083
+ headers=self._headers(),
1084
+ )
1085
+ if response.status_code != 200:
1086
+ raise CueMapError(f"Failed to get project watch directory: {response.text}")
1087
+ return response.json()
1088
+
1089
+ async def preview_directory(
1090
+ self,
1091
+ watch_dir: str,
1092
+ included_paths: Optional[List[str]] = None,
1093
+ ignored_patterns: Optional[List[str]] = None,
1094
+ ignored_extensions: Optional[List[str]] = None,
1095
+ ) -> Dict[str, Any]:
1096
+ """Preview supported repository files without ingesting content (async)."""
1097
+ response = await self.client.post(
1098
+ "/ingest/directory/preview",
1099
+ json=_clean_payload({
1100
+ "watch_dir": watch_dir,
1101
+ "included_paths": included_paths,
1102
+ "ignored_patterns": ignored_patterns,
1103
+ "ignored_extensions": ignored_extensions,
1104
+ }),
1105
+ headers=self._headers(),
1106
+ )
1107
+ if response.status_code != 200:
1108
+ raise CueMapError(f"Failed to preview directory: {response.text}")
1109
+ return response.json()
1110
+
1011
1111
  async def add_alias(self, from_cue: str, to_cue: str, weight: float = 1.0) -> bool:
1012
1112
  """Add an alias (async)."""
1013
1113
  response = await self.client.post(
@@ -1187,6 +1287,7 @@ class AsyncCueMap:
1187
1287
  source_key: Optional[str] = None,
1188
1288
  metadata: Optional[Dict[str, Any]] = None,
1189
1289
  structural_cues: Optional[List[str]] = None,
1290
+ embeddings: Optional[List[List[float]]] = None,
1190
1291
  segmenter: str = "sentence_window",
1191
1292
  segment_window_size: Optional[int] = None,
1192
1293
  segment_overlap: Optional[int] = None,
@@ -1202,6 +1303,7 @@ class AsyncCueMap:
1202
1303
  "source_key": source_key,
1203
1304
  "metadata": metadata,
1204
1305
  "structural_cues": structural_cues,
1306
+ "embeddings": embeddings,
1205
1307
  "segmenter": segmenter,
1206
1308
  "segment_window_size": segment_window_size,
1207
1309
  "segment_overlap": segment_overlap,
@@ -1237,7 +1339,6 @@ class AsyncCueMap:
1237
1339
  metadata: Optional[Dict[str, Any]] = None,
1238
1340
  existing_cues: Optional[List[str]] = None,
1239
1341
  available_cues: Optional[List[str]] = None,
1240
- cuepacks: Optional[List[str]] = None,
1241
1342
  filename: Optional[str] = None,
1242
1343
  segmenter: str = "sentence_window",
1243
1344
  segment_window_size: Optional[int] = None,
@@ -1254,7 +1355,6 @@ class AsyncCueMap:
1254
1355
  "metadata": metadata,
1255
1356
  "existing_cues": existing_cues,
1256
1357
  "available_cues": available_cues,
1257
- "cuepacks": cuepacks,
1258
1358
  "filename": filename,
1259
1359
  "segmenter": segmenter,
1260
1360
  "segment_window_size": segment_window_size,
@@ -0,0 +1,186 @@
1
+ """Lifecycle management for a local CueMap engine process."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import platform
8
+ import shutil
9
+ import socket
10
+ import subprocess
11
+ import time
12
+ from dataclasses import dataclass
13
+ from pathlib import Path
14
+ from typing import Callable, Mapping, Optional
15
+ from urllib.error import HTTPError, URLError
16
+ from urllib.request import Request, urlopen
17
+
18
+
19
+ def _normalize_url(url: str) -> str:
20
+ return url.rstrip("/")
21
+
22
+
23
+ def _inspect_engine(url: str, api_key: Optional[str] = None) -> str:
24
+ headers = {"X-API-Key": api_key} if api_key else {}
25
+ try:
26
+ with urlopen(Request(f"{_normalize_url(url)}/", headers=headers), timeout=0.75) as response:
27
+ payload = json.loads(response.read(16_384))
28
+ return "cuemap" if payload.get("name") == "CueMap Rust Engine" else "occupied"
29
+ except HTTPError:
30
+ return "occupied"
31
+ except (URLError, TimeoutError, OSError, ValueError, json.JSONDecodeError):
32
+ return "closed"
33
+
34
+
35
+ def _free_port() -> int:
36
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listener:
37
+ listener.bind(("127.0.0.1", 0))
38
+ return int(listener.getsockname()[1])
39
+
40
+
41
+ def _platform_package() -> str:
42
+ operating_system = platform.system().lower()
43
+ machine = platform.machine().lower()
44
+ architecture = "arm64" if machine in {"arm64", "aarch64"} else "x64"
45
+ return f"@cuemap-dev/engine-{operating_system}-{architecture}"
46
+
47
+
48
+ def _npm_global_binary() -> Optional[str]:
49
+ npm = shutil.which("npm")
50
+ if not npm:
51
+ return None
52
+ try:
53
+ root = subprocess.run(
54
+ [npm, "root", "--global"],
55
+ check=True,
56
+ capture_output=True,
57
+ text=True,
58
+ timeout=5,
59
+ ).stdout.strip()
60
+ except (OSError, subprocess.SubprocessError):
61
+ return None
62
+ binary_name = "cuemap.exe" if os.name == "nt" else "cuemap"
63
+ candidate = Path(root) / _platform_package() / "bin" / binary_name
64
+ return str(candidate) if candidate.is_file() else None
65
+
66
+
67
+ def resolve_cuemap_binary(explicit_path: Optional[str] = None) -> str:
68
+ """Resolve an engine binary without downloading or mutating the machine."""
69
+
70
+ configured = explicit_path or os.environ.get("CUEMAP_BIN")
71
+ if configured:
72
+ path = Path(configured).expanduser()
73
+ if not path.is_file():
74
+ raise FileNotFoundError(f"CueMap executable does not exist: {path}")
75
+ return str(path)
76
+
77
+ installed = shutil.which("cuemap")
78
+ if installed:
79
+ return installed
80
+
81
+ global_binary = _npm_global_binary()
82
+ if global_binary:
83
+ return global_binary
84
+
85
+ workspace = Path(__file__).resolve().parents[2] / "rust_engine" / "target"
86
+ binary_name = "cuemap.exe" if os.name == "nt" else "cuemap"
87
+ source_binaries = [
88
+ workspace / profile / binary_name
89
+ for profile in ("release", "debug")
90
+ if (workspace / profile / binary_name).is_file()
91
+ ]
92
+ if source_binaries:
93
+ return str(max(source_binaries, key=lambda candidate: candidate.stat().st_mtime))
94
+
95
+ raise FileNotFoundError(
96
+ "Could not find the CueMap engine. Set CUEMAP_BIN, install `cuemap` on PATH, "
97
+ f"or install the native npm package {_platform_package()}."
98
+ )
99
+
100
+
101
+ @dataclass
102
+ class EmbeddedCueMap:
103
+ """A verified engine connection and, when applicable, its owned process."""
104
+
105
+ url: str
106
+ owned: bool
107
+ _process: Optional[subprocess.Popen] = None
108
+ _shutdown_timeout: float = 5.0
109
+
110
+ @classmethod
111
+ def start(
112
+ cls,
113
+ *,
114
+ url: Optional[str] = None,
115
+ bin_path: Optional[str] = None,
116
+ port: int = 8080,
117
+ config_path: Optional[str] = None,
118
+ api_key: Optional[str] = None,
119
+ startup_timeout: float = 15.0,
120
+ shutdown_timeout: float = 5.0,
121
+ env: Optional[Mapping[str, str]] = None,
122
+ logger: Optional[Callable[[str], None]] = None,
123
+ ) -> "EmbeddedCueMap":
124
+ log = logger or (lambda _message: None)
125
+ if url:
126
+ normalized = _normalize_url(url)
127
+ if _inspect_engine(normalized, api_key) != "cuemap":
128
+ raise ConnectionError(f"No CueMap engine is reachable at {normalized}")
129
+ log(f"Attached to CueMap at {normalized}")
130
+ return cls(normalized, False, _shutdown_timeout=shutdown_timeout)
131
+
132
+ preferred_url = f"http://127.0.0.1:{port}"
133
+ status = _inspect_engine(preferred_url, api_key)
134
+ if status == "cuemap":
135
+ log(f"Attached to CueMap at {preferred_url}")
136
+ return cls(preferred_url, False, _shutdown_timeout=shutdown_timeout)
137
+
138
+ selected_port = _free_port() if status == "occupied" else port
139
+ selected_url = f"http://127.0.0.1:{selected_port}"
140
+ executable = resolve_cuemap_binary(bin_path)
141
+ arguments = [executable, "start", "--port", str(selected_port)]
142
+ if config_path:
143
+ arguments.extend(["--config", str(Path(config_path).expanduser())])
144
+ process_env = dict(os.environ)
145
+ if env:
146
+ process_env.update(env)
147
+ process_env["CUEMAP_PORT"] = str(selected_port)
148
+
149
+ log(f"Starting CueMap at {selected_url}")
150
+ process = subprocess.Popen(
151
+ arguments,
152
+ stdin=subprocess.DEVNULL,
153
+ stdout=subprocess.DEVNULL,
154
+ stderr=subprocess.DEVNULL,
155
+ env=process_env,
156
+ )
157
+ deadline = time.monotonic() + startup_timeout
158
+ while time.monotonic() < deadline:
159
+ return_code = process.poll()
160
+ if return_code is not None:
161
+ raise RuntimeError(f"CueMap exited before becoming ready (code={return_code})")
162
+ if _inspect_engine(selected_url, api_key) == "cuemap":
163
+ log(f"CueMap is ready at {selected_url}")
164
+ return cls(selected_url, True, process, shutdown_timeout)
165
+ time.sleep(0.1)
166
+
167
+ process.terminate()
168
+ raise TimeoutError(f"CueMap did not become ready within {startup_timeout:g}s")
169
+
170
+ def stop(self) -> None:
171
+ process = self._process
172
+ self._process = None
173
+ if not self.owned or process is None or process.poll() is not None:
174
+ return
175
+ process.terminate()
176
+ try:
177
+ process.wait(timeout=self._shutdown_timeout)
178
+ except subprocess.TimeoutExpired:
179
+ process.kill()
180
+ process.wait(timeout=self._shutdown_timeout)
181
+
182
+ def __enter__(self) -> "EmbeddedCueMap":
183
+ return self
184
+
185
+ def __exit__(self, _exc_type, _exc_value, _traceback) -> None:
186
+ self.stop()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuemap
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Summary: CueMap Python SDK - High-performance temporal-associative memory
5
5
  Author-email: Kaan Demirel <hello@cuemap.dev>
6
6
  License: MIT
@@ -42,14 +42,14 @@ Dynamic: license-file
42
42
  CueMap implements a **Continuous Gradient Algorithm** optimized for associative data structures:
43
43
 
44
44
  1. **Intersection (Context Filter)**: Triangulates relevant memories by overlapping cues
45
- 2. **CuePack-Guided Intent Routing**: Uses compiled deterministic rules to add structural facets and weighted intent cues without runtime model calls.
45
+ 2. **Local semantic reranking**: Uses bundled qint8 MiniLM-L3 by default, or q4 MiniLM-L3 with the edge profile, for bounded semantic ranking inside the engine.
46
46
  3. **Recency & Salience (Signal Dynamics)**: Balances fresh data with salient, high-signal events prioritized by an adaptive impact scoring module.
47
47
  4. **Reinforcement (Access-based Learning)**: Frequently accessed memories gain signal strength, remaining highly accessible even as they age.
48
48
  5. **Deterministic Facets & Intent Routing**: Extracts synchronous source, evidence, temporal, type, and entity facets, then uses sparse intent cues and reranking during recall.
49
49
 
50
- As of v0.7.0, CueMap's core path is deterministic and embedding-free. GloVe/Ollama cue generation, WordNet/POS expansion, semantic bridges, pattern completion, external lexicon graphs, context expansion/speculation endpoints, and autonomous consolidation have been removed from the core engine.
50
+ As of v0.7.2, CueMap keeps deterministic lexical candidate discovery and adds bundled qint8 `all-MiniLM-L3-v2` for bounded hybrid semantic and intent reranking. The `edge` engine profile uses a q4 build of the same model. No runtime model download is required, and callers can disable the encoder or provide their own vectors.
51
51
 
52
- v0.7.0 also uses numeric per-project memory IDs everywhere. If callers need deterministic upsert/dedupe identity, pass `source_key`; memory IDs remain compact runtime addresses.
52
+ v0.7.2 also preserves numeric per-project memory IDs everywhere. If callers need deterministic upsert/dedupe identity, pass `source_key`; memory IDs remain compact runtime addresses.
53
53
 
54
54
  Use this SDK to talk to the Rust engine from Python applications.
55
55
 
@@ -134,9 +134,9 @@ print(response["proof"])
134
134
  # Cryptographic proof of context retrieval
135
135
  ```
136
136
 
137
- ### v0.7 Recall Controls
137
+ ### v0.7.2 Recall Controls
138
138
 
139
- CueMap v0.7 adds temporal query intent, CueBridge artifact expansion, and optional reconstruction passes for longer conversational/codebase context.
139
+ CueMap v0.7.2 adds local semantic query signals alongside temporal query intent and optional reconstruction passes for longer conversational/codebase context.
140
140
 
141
141
  ```python
142
142
  results = client.recall(
@@ -145,11 +145,23 @@ results = client.recall(
145
145
  ordered_reconstruction="auto",
146
146
  evidence_coverage="auto",
147
147
  parent_fusion="auto",
148
- cuepacks=["default"],
148
+ semantic_mode="hybrid",
149
149
  explain=True,
150
150
  )
151
151
  ```
152
152
 
153
+ Use `semantic_mode="lexical"` for a semantic-reranker-disabled comparison, `"semantic"` for vector candidate discovery, or `"hybrid"` (the engine default) to rerank lexical candidates. `query_embedding` supplies a precomputed vector when the application owns the embedding provider.
154
+
155
+ Classify query or memory intent with the same local engine model. Returned scores are ranking signals, not calibrated probabilities:
156
+
157
+ ```python
158
+ classification = client.classify_intent(
159
+ "What did we decide about auth retries?",
160
+ target="query",
161
+ )
162
+ print(classification["primary_intent"], classification["recall_eligible"])
163
+ ```
164
+
153
165
  ### Cloud Backup (v0.6.1)
154
166
 
155
167
  Manage project snapshots in the cloud (S3, GCS, Azure).
@@ -186,6 +198,20 @@ client.ingest_content(
186
198
  )
187
199
  ```
188
200
 
201
+ When an application chunks content itself, pass exactly one vector per produced chunk with `embeddings=[[...], [...]]`.
202
+
203
+ Preview and persist a repository ingestion scope:
204
+
205
+ ```python
206
+ preview = client.preview_directory("/work/my-app", included_paths=["src"])
207
+ client.set_project_watch_dir(
208
+ "repo-my-app",
209
+ "/work/my-app",
210
+ included_paths=["src", "README.md"],
211
+ )
212
+ scope = client.get_project_watch_dir("repo-my-app")
213
+ ```
214
+
189
215
  ### Lexicon Management (v0.6+)
190
216
 
191
217
  Inspect and wire the brain's associations manually.
@@ -208,19 +234,7 @@ Check the progress of background ingestion tasks.
208
234
  ```python
209
235
  status = client.jobs_status()
210
236
  print(f"Ingested: {status['writes_completed']} / {status['writes_total']}")
211
- ```
212
-
213
- ### Advanced Brain Control
214
-
215
- Disable specific brain modules for deterministic debugging.
216
-
217
- ```python
218
- results = client.recall(
219
- "urgent issue",
220
- disable_salience_bias=True,
221
- disable_alias_expansion=True,
222
- disable_cuebridge_artifacts=True,
223
- )
237
+ print(f"Intent ready: {status.get('intent_ready', False)}")
224
238
  ```
225
239
 
226
240
  ## Async Support
@@ -233,11 +247,6 @@ async with AsyncCueMap() as client:
233
247
  await client.recall(cues=["note"])
234
248
  ```
235
249
 
236
- ## Performance
237
-
238
- - **Write Latency**: ~2ms (O(1) complexity)
239
- - **Read Latency**: ~3ms (P99, 1M memories)
240
-
241
250
  ## License
242
251
 
243
252
  MIT
@@ -4,6 +4,7 @@ README.md
4
4
  pyproject.toml
5
5
  cuemap/__init__.py
6
6
  cuemap/client.py
7
+ cuemap/embedded.py
7
8
  cuemap/exceptions.py
8
9
  cuemap/grounding.py
9
10
  cuemap/models.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cuemap"
7
- version = "0.7.0"
7
+ version = "0.7.2"
8
8
  description = "CueMap Python SDK - High-performance temporal-associative memory"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes