cuemap 0.7.2__tar.gz → 0.7.3__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.
- {cuemap-0.7.2/cuemap.egg-info → cuemap-0.7.3}/PKG-INFO +53 -8
- {cuemap-0.7.2 → cuemap-0.7.3}/README.md +52 -7
- {cuemap-0.7.2 → cuemap-0.7.3}/cuemap/__init__.py +3 -2
- {cuemap-0.7.2 → cuemap-0.7.3}/cuemap/client.py +195 -9
- {cuemap-0.7.2 → cuemap-0.7.3}/cuemap/embedded.py +32 -9
- {cuemap-0.7.2 → cuemap-0.7.3}/cuemap/models.py +8 -0
- {cuemap-0.7.2 → cuemap-0.7.3/cuemap.egg-info}/PKG-INFO +53 -8
- {cuemap-0.7.2 → cuemap-0.7.3}/pyproject.toml +2 -2
- {cuemap-0.7.2 → cuemap-0.7.3}/LICENSE +0 -0
- {cuemap-0.7.2 → cuemap-0.7.3}/MANIFEST.in +0 -0
- {cuemap-0.7.2 → cuemap-0.7.3}/cuemap/exceptions.py +0 -0
- {cuemap-0.7.2 → cuemap-0.7.3}/cuemap/grounding.py +0 -0
- {cuemap-0.7.2 → cuemap-0.7.3}/cuemap.egg-info/SOURCES.txt +0 -0
- {cuemap-0.7.2 → cuemap-0.7.3}/cuemap.egg-info/dependency_links.txt +0 -0
- {cuemap-0.7.2 → cuemap-0.7.3}/cuemap.egg-info/requires.txt +0 -0
- {cuemap-0.7.2 → cuemap-0.7.3}/cuemap.egg-info/top_level.txt +0 -0
- {cuemap-0.7.2 → cuemap-0.7.3}/examples/pure_usage.py +0 -0
- {cuemap-0.7.2 → cuemap-0.7.3}/examples/with_local_llm.py +0 -0
- {cuemap-0.7.2 → cuemap-0.7.3}/examples/with_openai.py +0 -0
- {cuemap-0.7.2 → cuemap-0.7.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cuemap
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: CueMap Python SDK - High-performance temporal-associative memory
|
|
5
5
|
Author-email: Kaan Demirel <hello@cuemap.dev>
|
|
6
6
|
License: MIT
|
|
@@ -33,13 +33,26 @@ Requires-Dist: build>=1.0.0; extra == "dev"
|
|
|
33
33
|
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
34
34
|
Dynamic: license-file
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
<p align="center">
|
|
37
|
+
<img src="https://cuemap.dev/cuemap-logo.PNG" alt="CueMap" width="120">
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
<h1 align="center">CueMap Python SDK</h1>
|
|
41
|
+
|
|
42
|
+
<p align="center">A polished Python client for fast, accurate, and explainable agent memory.</p>
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<a href="https://pypi.org/project/cuemap/"><img src="https://img.shields.io/pypi/v/cuemap?logo=pypi&logoColor=white" alt="PyPI"></a>
|
|
46
|
+
<a href="https://pypi.org/project/cuemap/"><img src="https://img.shields.io/pypi/pyversions/cuemap?logo=python&logoColor=white" alt="Python versions"></a>
|
|
47
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-5e5ce6" alt="License"></a>
|
|
48
|
+
<a href="https://github.com/cuemap-dev/cuemap"><img src="https://img.shields.io/badge/engine-v0.7.3-0f766e" alt="Engine compatibility"></a>
|
|
49
|
+
</p>
|
|
37
50
|
|
|
38
51
|
**High-performance temporal-associative memory store** designed for dynamic contextual retrieval.
|
|
39
52
|
|
|
40
53
|
## Overview
|
|
41
54
|
|
|
42
|
-
CueMap
|
|
55
|
+
CueMap uses **temporal-associative retrieval**: lexical and structural candidate generation, with optional semantic reranking. Its main components are:
|
|
43
56
|
|
|
44
57
|
1. **Intersection (Context Filter)**: Triangulates relevant memories by overlapping cues
|
|
45
58
|
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.
|
|
@@ -47,9 +60,9 @@ CueMap implements a **Continuous Gradient Algorithm** optimized for associative
|
|
|
47
60
|
4. **Reinforcement (Access-based Learning)**: Frequently accessed memories gain signal strength, remaining highly accessible even as they age.
|
|
48
61
|
5. **Deterministic Facets & Intent Routing**: Extracts synchronous source, evidence, temporal, type, and entity facets, then uses sparse intent cues and reranking during recall.
|
|
49
62
|
|
|
50
|
-
As of v0.7.
|
|
63
|
+
As of v0.7.3, CueMap keeps deterministic lexical candidate discovery and adds bundled qint8 `paraphrase-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
64
|
|
|
52
|
-
v0.7.
|
|
65
|
+
v0.7.3 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
66
|
|
|
54
67
|
Use this SDK to talk to the Rust engine from Python applications.
|
|
55
68
|
|
|
@@ -64,7 +77,7 @@ pip install cuemap
|
|
|
64
77
|
### 1. Start the Engine
|
|
65
78
|
|
|
66
79
|
```bash
|
|
67
|
-
docker run -p
|
|
80
|
+
docker run -p 8735:8735 cuemap/engine:latest
|
|
68
81
|
```
|
|
69
82
|
|
|
70
83
|
### 2. Basic Usage
|
|
@@ -134,9 +147,29 @@ print(response["proof"])
|
|
|
134
147
|
# Cryptographic proof of context retrieval
|
|
135
148
|
```
|
|
136
149
|
|
|
137
|
-
###
|
|
150
|
+
### Project memory lifecycle
|
|
138
151
|
|
|
139
|
-
|
|
152
|
+
The engine can unload inactive project contexts while keeping their snapshots
|
|
153
|
+
on disk. Normal project operations demand-load a project when needed, so the
|
|
154
|
+
first request after an unload may take longer. Use the explicit helpers when
|
|
155
|
+
you want to control residency:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
client.unload_project("older-repository")
|
|
159
|
+
client.load_project("older-repository")
|
|
160
|
+
client.save_project("older-repository") # persist without unloading
|
|
161
|
+
|
|
162
|
+
for project in client.list_projects():
|
|
163
|
+
print(project["project_id"], project["loaded"])
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Portable projects use the same four operations as the CLI:
|
|
167
|
+
`pack_project()`, `load_project_package()`, `push_project()`, and `pull_project()`.
|
|
168
|
+
Use `sync_project(project_id, "s3://bucket/team")` for conflict-safe fast-forward sync.
|
|
169
|
+
|
|
170
|
+
### v0.7.3 Recall Controls
|
|
171
|
+
|
|
172
|
+
CueMap v0.7.3 adds local semantic query signals alongside temporal query intent and optional reconstruction passes for longer conversational/codebase context.
|
|
140
173
|
|
|
141
174
|
```python
|
|
142
175
|
results = client.recall(
|
|
@@ -250,3 +283,15 @@ async with AsyncCueMap() as client:
|
|
|
250
283
|
## License
|
|
251
284
|
|
|
252
285
|
MIT
|
|
286
|
+
|
|
287
|
+
### Recall previews
|
|
288
|
+
|
|
289
|
+
The engine's `POST /recall` accepts `response_mode: "preview"` and optional
|
|
290
|
+
`preview_chars` (100–2000 UTF-16 code units, default 200). Full content remains
|
|
291
|
+
the default. Previews replace each hit's `content` with a leading `preview`,
|
|
292
|
+
`content_truncated`, and `content_length`, preserving metadata and ranking.
|
|
293
|
+
Use previews for broad discovery, then fetch a selected memory with
|
|
294
|
+
`GET /memories/{id}?decoded=true` or read its source. Metadata and diagnostics
|
|
295
|
+
are not capped. TypeScript request objects and Python sync/async `recall`
|
|
296
|
+
accept these same options; Python returns `RecallPreviewResult` for ungrouped
|
|
297
|
+
preview results. The updated engine is required.
|
|
@@ -1,10 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://cuemap.dev/cuemap-logo.PNG" alt="CueMap" width="120">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">CueMap Python SDK</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">A polished Python client for fast, accurate, and explainable agent memory.</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://pypi.org/project/cuemap/"><img src="https://img.shields.io/pypi/v/cuemap?logo=pypi&logoColor=white" alt="PyPI"></a>
|
|
11
|
+
<a href="https://pypi.org/project/cuemap/"><img src="https://img.shields.io/pypi/pyversions/cuemap?logo=python&logoColor=white" alt="Python versions"></a>
|
|
12
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-5e5ce6" alt="License"></a>
|
|
13
|
+
<a href="https://github.com/cuemap-dev/cuemap"><img src="https://img.shields.io/badge/engine-v0.7.3-0f766e" alt="Engine compatibility"></a>
|
|
14
|
+
</p>
|
|
2
15
|
|
|
3
16
|
**High-performance temporal-associative memory store** designed for dynamic contextual retrieval.
|
|
4
17
|
|
|
5
18
|
## Overview
|
|
6
19
|
|
|
7
|
-
CueMap
|
|
20
|
+
CueMap uses **temporal-associative retrieval**: lexical and structural candidate generation, with optional semantic reranking. Its main components are:
|
|
8
21
|
|
|
9
22
|
1. **Intersection (Context Filter)**: Triangulates relevant memories by overlapping cues
|
|
10
23
|
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.
|
|
@@ -12,9 +25,9 @@ CueMap implements a **Continuous Gradient Algorithm** optimized for associative
|
|
|
12
25
|
4. **Reinforcement (Access-based Learning)**: Frequently accessed memories gain signal strength, remaining highly accessible even as they age.
|
|
13
26
|
5. **Deterministic Facets & Intent Routing**: Extracts synchronous source, evidence, temporal, type, and entity facets, then uses sparse intent cues and reranking during recall.
|
|
14
27
|
|
|
15
|
-
As of v0.7.
|
|
28
|
+
As of v0.7.3, CueMap keeps deterministic lexical candidate discovery and adds bundled qint8 `paraphrase-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
29
|
|
|
17
|
-
v0.7.
|
|
30
|
+
v0.7.3 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
31
|
|
|
19
32
|
Use this SDK to talk to the Rust engine from Python applications.
|
|
20
33
|
|
|
@@ -29,7 +42,7 @@ pip install cuemap
|
|
|
29
42
|
### 1. Start the Engine
|
|
30
43
|
|
|
31
44
|
```bash
|
|
32
|
-
docker run -p
|
|
45
|
+
docker run -p 8735:8735 cuemap/engine:latest
|
|
33
46
|
```
|
|
34
47
|
|
|
35
48
|
### 2. Basic Usage
|
|
@@ -99,9 +112,29 @@ print(response["proof"])
|
|
|
99
112
|
# Cryptographic proof of context retrieval
|
|
100
113
|
```
|
|
101
114
|
|
|
102
|
-
###
|
|
115
|
+
### Project memory lifecycle
|
|
103
116
|
|
|
104
|
-
|
|
117
|
+
The engine can unload inactive project contexts while keeping their snapshots
|
|
118
|
+
on disk. Normal project operations demand-load a project when needed, so the
|
|
119
|
+
first request after an unload may take longer. Use the explicit helpers when
|
|
120
|
+
you want to control residency:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
client.unload_project("older-repository")
|
|
124
|
+
client.load_project("older-repository")
|
|
125
|
+
client.save_project("older-repository") # persist without unloading
|
|
126
|
+
|
|
127
|
+
for project in client.list_projects():
|
|
128
|
+
print(project["project_id"], project["loaded"])
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Portable projects use the same four operations as the CLI:
|
|
132
|
+
`pack_project()`, `load_project_package()`, `push_project()`, and `pull_project()`.
|
|
133
|
+
Use `sync_project(project_id, "s3://bucket/team")` for conflict-safe fast-forward sync.
|
|
134
|
+
|
|
135
|
+
### v0.7.3 Recall Controls
|
|
136
|
+
|
|
137
|
+
CueMap v0.7.3 adds local semantic query signals alongside temporal query intent and optional reconstruction passes for longer conversational/codebase context.
|
|
105
138
|
|
|
106
139
|
```python
|
|
107
140
|
results = client.recall(
|
|
@@ -215,3 +248,15 @@ async with AsyncCueMap() as client:
|
|
|
215
248
|
## License
|
|
216
249
|
|
|
217
250
|
MIT
|
|
251
|
+
|
|
252
|
+
### Recall previews
|
|
253
|
+
|
|
254
|
+
The engine's `POST /recall` accepts `response_mode: "preview"` and optional
|
|
255
|
+
`preview_chars` (100–2000 UTF-16 code units, default 200). Full content remains
|
|
256
|
+
the default. Previews replace each hit's `content` with a leading `preview`,
|
|
257
|
+
`content_truncated`, and `content_length`, preserving metadata and ranking.
|
|
258
|
+
Use previews for broad discovery, then fetch a selected memory with
|
|
259
|
+
`GET /memories/{id}?decoded=true` or read its source. Metadata and diagnostics
|
|
260
|
+
are not capped. TypeScript request objects and Python sync/async `recall`
|
|
261
|
+
accept these same options; Python returns `RecallPreviewResult` for ungrouped
|
|
262
|
+
preview results. The updated engine is required.
|
|
@@ -19,11 +19,11 @@ Example:
|
|
|
19
19
|
|
|
20
20
|
from .client import CueMap, AsyncCueMap
|
|
21
21
|
from .embedded import EmbeddedCueMap, resolve_cuemap_binary
|
|
22
|
-
from .models import Memory, RecallResult
|
|
22
|
+
from .models import Memory, RecallResult, RecallPreviewResult
|
|
23
23
|
from .exceptions import CueMapError, ConnectionError, AuthenticationError
|
|
24
24
|
from .grounding import CueMapGroundingRetriever, AsyncCueMapGroundingRetriever
|
|
25
25
|
|
|
26
|
-
__version__ = "0.7.
|
|
26
|
+
__version__ = "0.7.3"
|
|
27
27
|
__all__ = [
|
|
28
28
|
"CueMap",
|
|
29
29
|
"AsyncCueMap",
|
|
@@ -31,6 +31,7 @@ __all__ = [
|
|
|
31
31
|
"resolve_cuemap_binary",
|
|
32
32
|
"Memory",
|
|
33
33
|
"RecallResult",
|
|
34
|
+
"RecallPreviewResult",
|
|
34
35
|
"CueMapError",
|
|
35
36
|
"ConnectionError",
|
|
36
37
|
"AuthenticationError",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import httpx
|
|
4
4
|
from typing import List, Optional, Dict, Any
|
|
5
5
|
|
|
6
|
-
from .models import Memory, RecallResult
|
|
6
|
+
from .models import Memory, RecallResult, RecallPreviewResult
|
|
7
7
|
from .exceptions import CueMapError, ConnectionError, AuthenticationError
|
|
8
8
|
|
|
9
9
|
|
|
@@ -27,7 +27,7 @@ class CueMap:
|
|
|
27
27
|
|
|
28
28
|
def __init__(
|
|
29
29
|
self,
|
|
30
|
-
url: str = "http://localhost:
|
|
30
|
+
url: str = "http://localhost:8735",
|
|
31
31
|
api_key: Optional[str] = None,
|
|
32
32
|
project_id: Optional[str] = None,
|
|
33
33
|
timeout: float = 30.0
|
|
@@ -185,6 +185,8 @@ class CueMap:
|
|
|
185
185
|
disable_systems_consolidation: Optional[bool] = None,
|
|
186
186
|
semantic_mode: str = "hybrid",
|
|
187
187
|
query_embedding: Optional[List[float]] = None,
|
|
188
|
+
response_mode: str = "full",
|
|
189
|
+
preview_chars: int = 200,
|
|
188
190
|
) -> List[RecallResult]:
|
|
189
191
|
"""
|
|
190
192
|
Recall memories by cues or natural language.
|
|
@@ -231,6 +233,8 @@ class CueMap:
|
|
|
231
233
|
"cuebridge_gap_limit": cuebridge_gap_limit,
|
|
232
234
|
"semantic_mode": semantic_mode,
|
|
233
235
|
"query_embedding": query_embedding,
|
|
236
|
+
"response_mode": response_mode,
|
|
237
|
+
"preview_chars": preview_chars,
|
|
234
238
|
}
|
|
235
239
|
if cues:
|
|
236
240
|
payload["cues"] = cues
|
|
@@ -257,7 +261,7 @@ class CueMap:
|
|
|
257
261
|
if projects and isinstance(results, list) and len(results) > 0 and "project_id" in results[0]:
|
|
258
262
|
return data
|
|
259
263
|
|
|
260
|
-
return [RecallResult(**r) for r in results]
|
|
264
|
+
return [(RecallPreviewResult if response_mode == "preview" else RecallResult)(**r) for r in results]
|
|
261
265
|
|
|
262
266
|
def recall_grounded(
|
|
263
267
|
self,
|
|
@@ -302,8 +306,12 @@ class CueMap:
|
|
|
302
306
|
|
|
303
307
|
return response.json()
|
|
304
308
|
|
|
305
|
-
def list_projects(self) -> List[str]:
|
|
306
|
-
"""List all projects (multi-tenant only).
|
|
309
|
+
def list_projects(self) -> List[Dict[str, Any]]:
|
|
310
|
+
"""List all projects and their runtime state (multi-tenant only).
|
|
311
|
+
|
|
312
|
+
Each item includes ``loaded`` so callers can distinguish a project
|
|
313
|
+
whose snapshot exists on disk from one currently resident in RAM.
|
|
314
|
+
"""
|
|
307
315
|
response = self.client.get(
|
|
308
316
|
"/projects",
|
|
309
317
|
headers=self._headers()
|
|
@@ -312,6 +320,93 @@ class CueMap:
|
|
|
312
320
|
raise CueMapError(f"Failed to list projects: {response.text}")
|
|
313
321
|
return response.json()
|
|
314
322
|
|
|
323
|
+
def load_project(self, project_id: str) -> Dict[str, Any]:
|
|
324
|
+
"""Load a project's snapshot into engine memory."""
|
|
325
|
+
response = self.client.post(
|
|
326
|
+
f"/projects/{project_id}/load",
|
|
327
|
+
headers=self._headers(),
|
|
328
|
+
)
|
|
329
|
+
if response.status_code != 200:
|
|
330
|
+
raise CueMapError(f"Failed to load project: {response.text}")
|
|
331
|
+
return response.json()
|
|
332
|
+
|
|
333
|
+
def save_project(self, project_id: str) -> Dict[str, Any]:
|
|
334
|
+
"""Persist a current project snapshot without unloading it."""
|
|
335
|
+
response = self.client.post(
|
|
336
|
+
f"/projects/{project_id}/save",
|
|
337
|
+
headers=self._headers(),
|
|
338
|
+
)
|
|
339
|
+
if response.status_code != 200:
|
|
340
|
+
raise CueMapError(f"Failed to save project: {response.text}")
|
|
341
|
+
return response.json()
|
|
342
|
+
|
|
343
|
+
def unload_project(self, project_id: str) -> Dict[str, Any]:
|
|
344
|
+
"""Persist and unload a project from engine memory."""
|
|
345
|
+
response = self.client.post(
|
|
346
|
+
f"/projects/{project_id}/unload",
|
|
347
|
+
headers=self._headers(),
|
|
348
|
+
)
|
|
349
|
+
if response.status_code != 200:
|
|
350
|
+
raise CueMapError(f"Failed to unload project: {response.text}")
|
|
351
|
+
return response.json()
|
|
352
|
+
|
|
353
|
+
def pack_project(self, project_id: str) -> bytes:
|
|
354
|
+
"""Return a ready-to-query project as portable ``.cuemap`` bytes."""
|
|
355
|
+
response = self.client.post(
|
|
356
|
+
f"/projects/{project_id}/pack",
|
|
357
|
+
headers=self._headers(),
|
|
358
|
+
)
|
|
359
|
+
if response.status_code != 200:
|
|
360
|
+
raise CueMapError(f"Failed to pack project: {response.text}")
|
|
361
|
+
return response.content
|
|
362
|
+
|
|
363
|
+
def load_project_package(self, package: bytes) -> Dict[str, Any]:
|
|
364
|
+
"""Install and warm a portable ``.cuemap`` package."""
|
|
365
|
+
response = self.client.post(
|
|
366
|
+
"/projects/load",
|
|
367
|
+
content=package,
|
|
368
|
+
headers={
|
|
369
|
+
**self._headers(),
|
|
370
|
+
"Content-Type": "application/vnd.cuemap.project",
|
|
371
|
+
},
|
|
372
|
+
)
|
|
373
|
+
if response.status_code != 200:
|
|
374
|
+
raise CueMapError(f"Failed to load project package: {response.text}")
|
|
375
|
+
return response.json()
|
|
376
|
+
|
|
377
|
+
def push_project(self, project_id: str, destination: str) -> Dict[str, Any]:
|
|
378
|
+
"""Pack and upload a project using the server's configured AWS CLI."""
|
|
379
|
+
response = self.client.post(
|
|
380
|
+
f"/projects/{project_id}/push",
|
|
381
|
+
json={"destination": destination},
|
|
382
|
+
headers=self._headers(),
|
|
383
|
+
)
|
|
384
|
+
if response.status_code != 200:
|
|
385
|
+
raise CueMapError(f"Failed to push project: {response.text}")
|
|
386
|
+
return response.json()
|
|
387
|
+
|
|
388
|
+
def pull_project(self, source: str) -> Dict[str, Any]:
|
|
389
|
+
"""Download, install, and warm a project using the server's AWS CLI."""
|
|
390
|
+
response = self.client.post(
|
|
391
|
+
"/projects/pull",
|
|
392
|
+
json={"source": source},
|
|
393
|
+
headers=self._headers(),
|
|
394
|
+
)
|
|
395
|
+
if response.status_code != 200:
|
|
396
|
+
raise CueMapError(f"Failed to pull project: {response.text}")
|
|
397
|
+
return response.json()
|
|
398
|
+
|
|
399
|
+
def sync_project(self, project_id: str, remote: str) -> Dict[str, Any]:
|
|
400
|
+
"""Fast-forward a project through its immutable S3 sync history."""
|
|
401
|
+
response = self.client.post(
|
|
402
|
+
f"/projects/{project_id}/sync",
|
|
403
|
+
json={"remote": remote},
|
|
404
|
+
headers=self._headers(),
|
|
405
|
+
)
|
|
406
|
+
if response.status_code != 200:
|
|
407
|
+
raise CueMapError(f"Failed to sync project: {response.text}")
|
|
408
|
+
return response.json()
|
|
409
|
+
|
|
315
410
|
def create_project(self, project_id: str) -> Dict[str, Any]:
|
|
316
411
|
"""Create a project."""
|
|
317
412
|
response = self.client.post(
|
|
@@ -758,7 +853,7 @@ class AsyncCueMap:
|
|
|
758
853
|
|
|
759
854
|
def __init__(
|
|
760
855
|
self,
|
|
761
|
-
url: str = "http://localhost:
|
|
856
|
+
url: str = "http://localhost:8735",
|
|
762
857
|
api_key: Optional[str] = None,
|
|
763
858
|
project_id: Optional[str] = None,
|
|
764
859
|
timeout: float = 30.0
|
|
@@ -889,6 +984,8 @@ class AsyncCueMap:
|
|
|
889
984
|
disable_systems_consolidation: Optional[bool] = None,
|
|
890
985
|
semantic_mode: str = "hybrid",
|
|
891
986
|
query_embedding: Optional[List[float]] = None,
|
|
987
|
+
response_mode: str = "full",
|
|
988
|
+
preview_chars: int = 200,
|
|
892
989
|
) -> List[RecallResult]:
|
|
893
990
|
"""Recall memories (async)."""
|
|
894
991
|
payload = {
|
|
@@ -915,6 +1012,8 @@ class AsyncCueMap:
|
|
|
915
1012
|
"cuebridge_gap_limit": cuebridge_gap_limit,
|
|
916
1013
|
"semantic_mode": semantic_mode,
|
|
917
1014
|
"query_embedding": query_embedding,
|
|
1015
|
+
"response_mode": response_mode,
|
|
1016
|
+
"preview_chars": preview_chars,
|
|
918
1017
|
}
|
|
919
1018
|
if cues:
|
|
920
1019
|
payload["cues"] = cues
|
|
@@ -941,7 +1040,7 @@ class AsyncCueMap:
|
|
|
941
1040
|
if projects and isinstance(results, list) and len(results) > 0 and "project_id" in results[0]:
|
|
942
1041
|
return data
|
|
943
1042
|
|
|
944
|
-
return [RecallResult(**r) for r in results]
|
|
1043
|
+
return [(RecallPreviewResult if response_mode == "preview" else RecallResult)(**r) for r in results]
|
|
945
1044
|
|
|
946
1045
|
async def recall_grounded(
|
|
947
1046
|
self,
|
|
@@ -979,8 +1078,8 @@ class AsyncCueMap:
|
|
|
979
1078
|
|
|
980
1079
|
return response.json()
|
|
981
1080
|
|
|
982
|
-
async def list_projects(self) -> List[str]:
|
|
983
|
-
"""List all projects (async, multi-tenant only)."""
|
|
1081
|
+
async def list_projects(self) -> List[Dict[str, Any]]:
|
|
1082
|
+
"""List all projects and their runtime state (async, multi-tenant only)."""
|
|
984
1083
|
response = await self.client.get(
|
|
985
1084
|
"/projects",
|
|
986
1085
|
headers=self._headers()
|
|
@@ -989,6 +1088,93 @@ class AsyncCueMap:
|
|
|
989
1088
|
raise CueMapError(f"Failed to list projects: {response.text}")
|
|
990
1089
|
return response.json()
|
|
991
1090
|
|
|
1091
|
+
async def load_project(self, project_id: str) -> Dict[str, Any]:
|
|
1092
|
+
"""Load a project's snapshot into engine memory (async)."""
|
|
1093
|
+
response = await self.client.post(
|
|
1094
|
+
f"/projects/{project_id}/load",
|
|
1095
|
+
headers=self._headers(),
|
|
1096
|
+
)
|
|
1097
|
+
if response.status_code != 200:
|
|
1098
|
+
raise CueMapError(f"Failed to load project: {response.text}")
|
|
1099
|
+
return response.json()
|
|
1100
|
+
|
|
1101
|
+
async def save_project(self, project_id: str) -> Dict[str, Any]:
|
|
1102
|
+
"""Persist a current project snapshot without unloading it (async)."""
|
|
1103
|
+
response = await self.client.post(
|
|
1104
|
+
f"/projects/{project_id}/save",
|
|
1105
|
+
headers=self._headers(),
|
|
1106
|
+
)
|
|
1107
|
+
if response.status_code != 200:
|
|
1108
|
+
raise CueMapError(f"Failed to save project: {response.text}")
|
|
1109
|
+
return response.json()
|
|
1110
|
+
|
|
1111
|
+
async def unload_project(self, project_id: str) -> Dict[str, Any]:
|
|
1112
|
+
"""Persist and unload a project from engine memory (async)."""
|
|
1113
|
+
response = await self.client.post(
|
|
1114
|
+
f"/projects/{project_id}/unload",
|
|
1115
|
+
headers=self._headers(),
|
|
1116
|
+
)
|
|
1117
|
+
if response.status_code != 200:
|
|
1118
|
+
raise CueMapError(f"Failed to unload project: {response.text}")
|
|
1119
|
+
return response.json()
|
|
1120
|
+
|
|
1121
|
+
async def pack_project(self, project_id: str) -> bytes:
|
|
1122
|
+
"""Return a ready-to-query project as portable ``.cuemap`` bytes (async)."""
|
|
1123
|
+
response = await self.client.post(
|
|
1124
|
+
f"/projects/{project_id}/pack",
|
|
1125
|
+
headers=self._headers(),
|
|
1126
|
+
)
|
|
1127
|
+
if response.status_code != 200:
|
|
1128
|
+
raise CueMapError(f"Failed to pack project: {response.text}")
|
|
1129
|
+
return response.content
|
|
1130
|
+
|
|
1131
|
+
async def load_project_package(self, package: bytes) -> Dict[str, Any]:
|
|
1132
|
+
"""Install and warm a portable ``.cuemap`` package (async)."""
|
|
1133
|
+
response = await self.client.post(
|
|
1134
|
+
"/projects/load",
|
|
1135
|
+
content=package,
|
|
1136
|
+
headers={
|
|
1137
|
+
**self._headers(),
|
|
1138
|
+
"Content-Type": "application/vnd.cuemap.project",
|
|
1139
|
+
},
|
|
1140
|
+
)
|
|
1141
|
+
if response.status_code != 200:
|
|
1142
|
+
raise CueMapError(f"Failed to load project package: {response.text}")
|
|
1143
|
+
return response.json()
|
|
1144
|
+
|
|
1145
|
+
async def push_project(self, project_id: str, destination: str) -> Dict[str, Any]:
|
|
1146
|
+
"""Pack and upload a project using the server's configured AWS CLI (async)."""
|
|
1147
|
+
response = await self.client.post(
|
|
1148
|
+
f"/projects/{project_id}/push",
|
|
1149
|
+
json={"destination": destination},
|
|
1150
|
+
headers=self._headers(),
|
|
1151
|
+
)
|
|
1152
|
+
if response.status_code != 200:
|
|
1153
|
+
raise CueMapError(f"Failed to push project: {response.text}")
|
|
1154
|
+
return response.json()
|
|
1155
|
+
|
|
1156
|
+
async def pull_project(self, source: str) -> Dict[str, Any]:
|
|
1157
|
+
"""Download, install, and warm a project using the server's AWS CLI (async)."""
|
|
1158
|
+
response = await self.client.post(
|
|
1159
|
+
"/projects/pull",
|
|
1160
|
+
json={"source": source},
|
|
1161
|
+
headers=self._headers(),
|
|
1162
|
+
)
|
|
1163
|
+
if response.status_code != 200:
|
|
1164
|
+
raise CueMapError(f"Failed to pull project: {response.text}")
|
|
1165
|
+
return response.json()
|
|
1166
|
+
|
|
1167
|
+
async def sync_project(self, project_id: str, remote: str) -> Dict[str, Any]:
|
|
1168
|
+
"""Fast-forward a project through its immutable S3 sync history (async)."""
|
|
1169
|
+
response = await self.client.post(
|
|
1170
|
+
f"/projects/{project_id}/sync",
|
|
1171
|
+
json={"remote": remote},
|
|
1172
|
+
headers=self._headers(),
|
|
1173
|
+
)
|
|
1174
|
+
if response.status_code != 200:
|
|
1175
|
+
raise CueMapError(f"Failed to sync project: {response.text}")
|
|
1176
|
+
return response.json()
|
|
1177
|
+
|
|
992
1178
|
async def create_project(self, project_id: str) -> Dict[str, Any]:
|
|
993
1179
|
"""Create a project (async)."""
|
|
994
1180
|
response = await self.client.post(
|
|
@@ -39,9 +39,11 @@ def _free_port() -> int:
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def _platform_package() -> str:
|
|
42
|
-
operating_system = platform.system()
|
|
42
|
+
operating_system = {"Windows": "win32", "Darwin": "darwin", "Linux": "linux"}.get(platform.system())
|
|
43
43
|
machine = platform.machine().lower()
|
|
44
|
-
architecture = "arm64"
|
|
44
|
+
architecture = {"arm64": "arm64", "aarch64": "arm64", "amd64": "x64", "x86_64": "x64"}.get(machine)
|
|
45
|
+
if operating_system is None or architecture is None or (operating_system == "win32" and architecture != "x64"):
|
|
46
|
+
raise RuntimeError(f"Unsupported CueMap platform: {platform.system()} {machine}")
|
|
45
47
|
return f"@cuemap-dev/engine-{operating_system}-{architecture}"
|
|
46
48
|
|
|
47
49
|
|
|
@@ -49,9 +51,16 @@ def _npm_global_binary() -> Optional[str]:
|
|
|
49
51
|
npm = shutil.which("npm")
|
|
50
52
|
if not npm:
|
|
51
53
|
return None
|
|
54
|
+
npm_command = [npm]
|
|
55
|
+
if platform.system() == "Windows":
|
|
56
|
+
node = shutil.which("node")
|
|
57
|
+
npm_cli = Path(npm).parent / "node_modules" / "npm" / "bin" / "npm-cli.js"
|
|
58
|
+
if not node or not npm_cli.is_file():
|
|
59
|
+
return None
|
|
60
|
+
npm_command = [node, str(npm_cli)]
|
|
52
61
|
try:
|
|
53
62
|
root = subprocess.run(
|
|
54
|
-
[
|
|
63
|
+
[*npm_command, "root", "--global"],
|
|
55
64
|
check=True,
|
|
56
65
|
capture_output=True,
|
|
57
66
|
text=True,
|
|
@@ -59,8 +68,8 @@ def _npm_global_binary() -> Optional[str]:
|
|
|
59
68
|
).stdout.strip()
|
|
60
69
|
except (OSError, subprocess.SubprocessError):
|
|
61
70
|
return None
|
|
62
|
-
|
|
63
|
-
candidate =
|
|
71
|
+
package_bin = Path(root) / _platform_package() / "bin"
|
|
72
|
+
candidate = package_bin / ("cuemap-native.exe" if platform.system() == "Windows" else "cuemap")
|
|
64
73
|
return str(candidate) if candidate.is_file() else None
|
|
65
74
|
|
|
66
75
|
|
|
@@ -75,7 +84,7 @@ def resolve_cuemap_binary(explicit_path: Optional[str] = None) -> str:
|
|
|
75
84
|
return str(path)
|
|
76
85
|
|
|
77
86
|
installed = shutil.which("cuemap")
|
|
78
|
-
if installed:
|
|
87
|
+
if installed and Path(installed).suffix.lower() not in {".cmd", ".bat", ".ps1"}:
|
|
79
88
|
return installed
|
|
80
89
|
|
|
81
90
|
global_binary = _npm_global_binary()
|
|
@@ -113,7 +122,7 @@ class EmbeddedCueMap:
|
|
|
113
122
|
*,
|
|
114
123
|
url: Optional[str] = None,
|
|
115
124
|
bin_path: Optional[str] = None,
|
|
116
|
-
port: int =
|
|
125
|
+
port: int = 8735,
|
|
117
126
|
config_path: Optional[str] = None,
|
|
118
127
|
api_key: Optional[str] = None,
|
|
119
128
|
startup_timeout: float = 15.0,
|
|
@@ -138,13 +147,27 @@ class EmbeddedCueMap:
|
|
|
138
147
|
selected_port = _free_port() if status == "occupied" else port
|
|
139
148
|
selected_url = f"http://127.0.0.1:{selected_port}"
|
|
140
149
|
executable = resolve_cuemap_binary(bin_path)
|
|
141
|
-
|
|
150
|
+
command = [executable]
|
|
151
|
+
if platform.system() == "Windows" and Path(executable).suffix.lower() != ".exe":
|
|
152
|
+
if Path(executable).suffix.lower() in {".cmd", ".bat", ".ps1"}:
|
|
153
|
+
raise ValueError("Use the native .exe or the npm package bin/cuemap wrapper, not a shell shim")
|
|
154
|
+
node = shutil.which("node")
|
|
155
|
+
if not node:
|
|
156
|
+
raise FileNotFoundError("Node.js is required to launch the CueMap npm wrapper")
|
|
157
|
+
command = [node, executable]
|
|
158
|
+
arguments = [*command, "start", "--port", str(selected_port)]
|
|
142
159
|
if config_path:
|
|
143
160
|
arguments.extend(["--config", str(Path(config_path).expanduser())])
|
|
144
161
|
process_env = dict(os.environ)
|
|
145
162
|
if env:
|
|
146
163
|
process_env.update(env)
|
|
147
164
|
process_env["CUEMAP_PORT"] = str(selected_port)
|
|
165
|
+
process_env["CUEMAP_HOST"] = "127.0.0.1"
|
|
166
|
+
tokenizer = Path(executable).parent.parent / "assets" / "en_tokenizer.bin"
|
|
167
|
+
if Path(executable).name == "cuemap-native.exe" and tokenizer.is_file():
|
|
168
|
+
process_env.setdefault("TOKENIZER_PATH", str(tokenizer))
|
|
169
|
+
if api_key:
|
|
170
|
+
process_env["CUEMAP_API_KEY"] = api_key
|
|
148
171
|
|
|
149
172
|
log(f"Starting CueMap at {selected_url}")
|
|
150
173
|
process = subprocess.Popen(
|
|
@@ -164,7 +187,7 @@ class EmbeddedCueMap:
|
|
|
164
187
|
return cls(selected_url, True, process, shutdown_timeout)
|
|
165
188
|
time.sleep(0.1)
|
|
166
189
|
|
|
167
|
-
process.
|
|
190
|
+
cls(selected_url, True, process, shutdown_timeout).stop()
|
|
168
191
|
raise TimeoutError(f"CueMap did not become ready within {startup_timeout:g}s")
|
|
169
192
|
|
|
170
193
|
def stop(self) -> None:
|
|
@@ -37,3 +37,11 @@ class RecallResult(BaseModel):
|
|
|
37
37
|
structural_cues: List[str] = Field(default_factory=list)
|
|
38
38
|
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
39
39
|
explain: Optional[Dict[str, Any]] = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class RecallPreviewResult(RecallResult):
|
|
43
|
+
"""Engine-provided leading excerpt; fetch the memory for full evidence."""
|
|
44
|
+
content: Optional[str] = None
|
|
45
|
+
preview: str
|
|
46
|
+
content_truncated: bool
|
|
47
|
+
content_length: int
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cuemap
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: CueMap Python SDK - High-performance temporal-associative memory
|
|
5
5
|
Author-email: Kaan Demirel <hello@cuemap.dev>
|
|
6
6
|
License: MIT
|
|
@@ -33,13 +33,26 @@ Requires-Dist: build>=1.0.0; extra == "dev"
|
|
|
33
33
|
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
34
34
|
Dynamic: license-file
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
<p align="center">
|
|
37
|
+
<img src="https://cuemap.dev/cuemap-logo.PNG" alt="CueMap" width="120">
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
<h1 align="center">CueMap Python SDK</h1>
|
|
41
|
+
|
|
42
|
+
<p align="center">A polished Python client for fast, accurate, and explainable agent memory.</p>
|
|
43
|
+
|
|
44
|
+
<p align="center">
|
|
45
|
+
<a href="https://pypi.org/project/cuemap/"><img src="https://img.shields.io/pypi/v/cuemap?logo=pypi&logoColor=white" alt="PyPI"></a>
|
|
46
|
+
<a href="https://pypi.org/project/cuemap/"><img src="https://img.shields.io/pypi/pyversions/cuemap?logo=python&logoColor=white" alt="Python versions"></a>
|
|
47
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-5e5ce6" alt="License"></a>
|
|
48
|
+
<a href="https://github.com/cuemap-dev/cuemap"><img src="https://img.shields.io/badge/engine-v0.7.3-0f766e" alt="Engine compatibility"></a>
|
|
49
|
+
</p>
|
|
37
50
|
|
|
38
51
|
**High-performance temporal-associative memory store** designed for dynamic contextual retrieval.
|
|
39
52
|
|
|
40
53
|
## Overview
|
|
41
54
|
|
|
42
|
-
CueMap
|
|
55
|
+
CueMap uses **temporal-associative retrieval**: lexical and structural candidate generation, with optional semantic reranking. Its main components are:
|
|
43
56
|
|
|
44
57
|
1. **Intersection (Context Filter)**: Triangulates relevant memories by overlapping cues
|
|
45
58
|
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.
|
|
@@ -47,9 +60,9 @@ CueMap implements a **Continuous Gradient Algorithm** optimized for associative
|
|
|
47
60
|
4. **Reinforcement (Access-based Learning)**: Frequently accessed memories gain signal strength, remaining highly accessible even as they age.
|
|
48
61
|
5. **Deterministic Facets & Intent Routing**: Extracts synchronous source, evidence, temporal, type, and entity facets, then uses sparse intent cues and reranking during recall.
|
|
49
62
|
|
|
50
|
-
As of v0.7.
|
|
63
|
+
As of v0.7.3, CueMap keeps deterministic lexical candidate discovery and adds bundled qint8 `paraphrase-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
64
|
|
|
52
|
-
v0.7.
|
|
65
|
+
v0.7.3 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
66
|
|
|
54
67
|
Use this SDK to talk to the Rust engine from Python applications.
|
|
55
68
|
|
|
@@ -64,7 +77,7 @@ pip install cuemap
|
|
|
64
77
|
### 1. Start the Engine
|
|
65
78
|
|
|
66
79
|
```bash
|
|
67
|
-
docker run -p
|
|
80
|
+
docker run -p 8735:8735 cuemap/engine:latest
|
|
68
81
|
```
|
|
69
82
|
|
|
70
83
|
### 2. Basic Usage
|
|
@@ -134,9 +147,29 @@ print(response["proof"])
|
|
|
134
147
|
# Cryptographic proof of context retrieval
|
|
135
148
|
```
|
|
136
149
|
|
|
137
|
-
###
|
|
150
|
+
### Project memory lifecycle
|
|
138
151
|
|
|
139
|
-
|
|
152
|
+
The engine can unload inactive project contexts while keeping their snapshots
|
|
153
|
+
on disk. Normal project operations demand-load a project when needed, so the
|
|
154
|
+
first request after an unload may take longer. Use the explicit helpers when
|
|
155
|
+
you want to control residency:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
client.unload_project("older-repository")
|
|
159
|
+
client.load_project("older-repository")
|
|
160
|
+
client.save_project("older-repository") # persist without unloading
|
|
161
|
+
|
|
162
|
+
for project in client.list_projects():
|
|
163
|
+
print(project["project_id"], project["loaded"])
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Portable projects use the same four operations as the CLI:
|
|
167
|
+
`pack_project()`, `load_project_package()`, `push_project()`, and `pull_project()`.
|
|
168
|
+
Use `sync_project(project_id, "s3://bucket/team")` for conflict-safe fast-forward sync.
|
|
169
|
+
|
|
170
|
+
### v0.7.3 Recall Controls
|
|
171
|
+
|
|
172
|
+
CueMap v0.7.3 adds local semantic query signals alongside temporal query intent and optional reconstruction passes for longer conversational/codebase context.
|
|
140
173
|
|
|
141
174
|
```python
|
|
142
175
|
results = client.recall(
|
|
@@ -250,3 +283,15 @@ async with AsyncCueMap() as client:
|
|
|
250
283
|
## License
|
|
251
284
|
|
|
252
285
|
MIT
|
|
286
|
+
|
|
287
|
+
### Recall previews
|
|
288
|
+
|
|
289
|
+
The engine's `POST /recall` accepts `response_mode: "preview"` and optional
|
|
290
|
+
`preview_chars` (100–2000 UTF-16 code units, default 200). Full content remains
|
|
291
|
+
the default. Previews replace each hit's `content` with a leading `preview`,
|
|
292
|
+
`content_truncated`, and `content_length`, preserving metadata and ranking.
|
|
293
|
+
Use previews for broad discovery, then fetch a selected memory with
|
|
294
|
+
`GET /memories/{id}?decoded=true` or read its source. Metadata and diagnostics
|
|
295
|
+
are not capped. TypeScript request objects and Python sync/async `recall`
|
|
296
|
+
accept these same options; Python returns `RecallPreviewResult` for ungrouped
|
|
297
|
+
preview results. The updated engine is required.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["setuptools>=61.0"]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cuemap"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.3"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|