smartmemory-client 0.5.0__py3-none-any.whl
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.
- smartmemory_client/__init__.py +61 -0
- smartmemory_client/client.py +3279 -0
- smartmemory_client/models/__init__.py +10 -0
- smartmemory_client/models/conversation.py +29 -0
- smartmemory_client/models/memory_item.py +94 -0
- smartmemory_client-0.5.0.dist-info/METADATA +547 -0
- smartmemory_client-0.5.0.dist-info/RECORD +10 -0
- smartmemory_client-0.5.0.dist-info/WHEEL +5 -0
- smartmemory_client-0.5.0.dist-info/licenses/LICENSE +21 -0
- smartmemory_client-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SmartMemory Client Models
|
|
3
|
+
|
|
4
|
+
Pydantic-style dataclass models for type-safe API interactions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from smartmemory_client.models.memory_item import MemoryItem
|
|
8
|
+
from smartmemory_client.models.conversation import ConversationContextModel
|
|
9
|
+
|
|
10
|
+
__all__ = ["MemoryItem", "ConversationContextModel"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import List, Dict, Any, Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class ConversationContextModel:
|
|
8
|
+
"""
|
|
9
|
+
Client-side representation of conversation context.
|
|
10
|
+
Used to enable conversation-aware extraction with entity tracking,
|
|
11
|
+
coreference resolution, and speaker relations.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
conversation_id: Optional[str] = None
|
|
15
|
+
participant_id: Optional[str] = (
|
|
16
|
+
None # Who is participating (see contracts/conversation.json)
|
|
17
|
+
)
|
|
18
|
+
topics: List[str] = field(default_factory=list)
|
|
19
|
+
entities: List[Dict[str, Any]] = field(default_factory=list)
|
|
20
|
+
turn_history: List[Dict[str, Any]] = field(default_factory=list)
|
|
21
|
+
sentiment: Optional[str] = None
|
|
22
|
+
active_threads: List[str] = field(default_factory=list)
|
|
23
|
+
extra: Optional[Dict[str, Any]] = None
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ConversationContextModel":
|
|
27
|
+
"""Build from a plain dict, ignoring unknown keys."""
|
|
28
|
+
known = {f.name for f in dataclasses.fields(cls)}
|
|
29
|
+
return cls(**{k: v for k, v in data.items() if k in known})
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from dataclasses import dataclass, field, asdict
|
|
2
|
+
from typing import Dict, Any, Optional, List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class MemoryItem:
|
|
7
|
+
"""
|
|
8
|
+
Client-side representation of a MemoryItem.
|
|
9
|
+
|
|
10
|
+
Designed to match the core smartmemory.models.MemoryItem interface
|
|
11
|
+
for portable code between core library and client.
|
|
12
|
+
|
|
13
|
+
Supports both attribute access and dict-like access:
|
|
14
|
+
item.content # attribute
|
|
15
|
+
item["content"] # dict-like
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
item_id: str
|
|
19
|
+
content: str
|
|
20
|
+
memory_type: str = "semantic"
|
|
21
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
22
|
+
score: Optional[float] = None
|
|
23
|
+
created_at: Optional[str] = None
|
|
24
|
+
updated_at: Optional[str] = None
|
|
25
|
+
embedding: Optional[List[float]] = None
|
|
26
|
+
|
|
27
|
+
# Tenancy fields from core MemoryItem
|
|
28
|
+
user_id: Optional[str] = None
|
|
29
|
+
workspace_id: Optional[str] = None
|
|
30
|
+
tenant_id: Optional[str] = None
|
|
31
|
+
tags: List[str] = field(default_factory=list)
|
|
32
|
+
|
|
33
|
+
# Bi-temporal fields (aligned with core SDK)
|
|
34
|
+
valid_start_time: Optional[str] = None # ISO format datetime
|
|
35
|
+
valid_end_time: Optional[str] = None # ISO format datetime
|
|
36
|
+
transaction_time: Optional[str] = None # ISO format datetime
|
|
37
|
+
|
|
38
|
+
# Extracted data (populated by ingestion pipeline)
|
|
39
|
+
entities: Optional[List[Dict[str, Any]]] = None
|
|
40
|
+
relations: Optional[List[Dict[str, Any]]] = None
|
|
41
|
+
|
|
42
|
+
def __getitem__(self, key: str) -> Any:
|
|
43
|
+
"""Dict-like access for compatibility."""
|
|
44
|
+
return getattr(self, key)
|
|
45
|
+
|
|
46
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
47
|
+
"""Dict-like assignment for compatibility."""
|
|
48
|
+
setattr(self, key, value)
|
|
49
|
+
|
|
50
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
51
|
+
"""Dict-like get with default."""
|
|
52
|
+
return getattr(self, key, default)
|
|
53
|
+
|
|
54
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
55
|
+
"""Convert to dictionary."""
|
|
56
|
+
return asdict(self)
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_dict(cls, data: Dict[str, Any]) -> "MemoryItem":
|
|
60
|
+
"""
|
|
61
|
+
Create MemoryItem from API response dict.
|
|
62
|
+
|
|
63
|
+
Handles various field name conventions from the service.
|
|
64
|
+
"""
|
|
65
|
+
# Handle id vs item_id
|
|
66
|
+
item_id = data.get("item_id") or data.get("id") or ""
|
|
67
|
+
|
|
68
|
+
return cls(
|
|
69
|
+
item_id=item_id,
|
|
70
|
+
content=data.get("content", ""),
|
|
71
|
+
memory_type=data.get("memory_type", data.get("type", "semantic")),
|
|
72
|
+
metadata=data.get("metadata", {}),
|
|
73
|
+
score=data.get("score"),
|
|
74
|
+
created_at=data.get("created_at"),
|
|
75
|
+
updated_at=data.get("updated_at"),
|
|
76
|
+
embedding=data.get("embedding"),
|
|
77
|
+
user_id=data.get("user_id"),
|
|
78
|
+
workspace_id=data.get("workspace_id"),
|
|
79
|
+
tenant_id=data.get("tenant_id"),
|
|
80
|
+
tags=data.get("tags", []),
|
|
81
|
+
# Bi-temporal fields
|
|
82
|
+
valid_start_time=data.get("valid_start_time"),
|
|
83
|
+
valid_end_time=data.get("valid_end_time"),
|
|
84
|
+
transaction_time=data.get("transaction_time"),
|
|
85
|
+
# Extracted data
|
|
86
|
+
entities=data.get("entities"),
|
|
87
|
+
relations=data.get("relations"),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
def __repr__(self) -> str:
|
|
91
|
+
content_preview = (
|
|
92
|
+
self.content[:50] + "..." if len(self.content) > 50 else self.content
|
|
93
|
+
)
|
|
94
|
+
return f"MemoryItem(item_id='{self.item_id}', content='{content_preview}', type='{self.memory_type}')"
|
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: smartmemory-client
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Python client for SmartMemory Service API
|
|
5
|
+
Author-email: SmartMemory Team <team@smartmemory.dev>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/smartmemory/smart-memory-client
|
|
8
|
+
Project-URL: Documentation, https://docs.smartmemory.dev
|
|
9
|
+
Project-URL: Repository, https://github.com/smartmemory/smart-memory-client
|
|
10
|
+
Project-URL: Issues, https://github.com/smartmemory/smart-memory-client/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/smartmemory/smart-memory-client/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: smartmemory,memory,ai,knowledge-graph,semantic-memory
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: httpx>=0.20.0
|
|
25
|
+
Requires-Dist: pydantic>=2.0.0
|
|
26
|
+
Requires-Dist: python-dateutil>=2.8.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
31
|
+
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.4.0; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# SmartMemory Python Client
|
|
36
|
+
|
|
37
|
+
Official Python client for the [SmartMemory Service](https://github.com/smartmemory/smart-memory-service) API.
|
|
38
|
+
|
|
39
|
+
[](https://badge.fury.io/py/smartmemory-client)
|
|
40
|
+
[](https://www.python.org/downloads/)
|
|
41
|
+
[](https://opensource.org/licenses/MIT)
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
- ✅ **Type-safe API** with Pydantic models
|
|
46
|
+
- ✅ **Automatic JWT authentication** with token handling
|
|
47
|
+
- ✅ **Full API coverage** (CRUD, ingestion, search, links, temporal, etc.)
|
|
48
|
+
- ✅ **Comprehensive error handling**
|
|
49
|
+
- ✅ **Clean, manually-maintained code** (no code generation complexity)
|
|
50
|
+
- ✅ **Automatic scoping** - tenant/workspace/user derived from JWT token
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install smartmemory-client
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Development Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/smartmemory/smart-memory-client.git
|
|
62
|
+
cd smart-memory-client
|
|
63
|
+
pip install -e ".[dev]"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from smartmemory_client import SmartMemoryClient
|
|
70
|
+
|
|
71
|
+
# Initialize client with authentication
|
|
72
|
+
client = SmartMemoryClient(
|
|
73
|
+
base_url="http://localhost:9001",
|
|
74
|
+
api_key="your_jwt_token" # Or set SMARTMEMORY_API_KEY env var
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Add a memory
|
|
78
|
+
item_id = client.add("This is a test memory")
|
|
79
|
+
print(f"Added memory: {item_id}")
|
|
80
|
+
|
|
81
|
+
# Search memories
|
|
82
|
+
results = client.search("test", top_k=5)
|
|
83
|
+
for memory in results:
|
|
84
|
+
print(f"{memory.item_id}: {memory.content}")
|
|
85
|
+
|
|
86
|
+
# Ingest with full pipeline
|
|
87
|
+
result = client.ingest(
|
|
88
|
+
content="Complex content to process",
|
|
89
|
+
extractor_name="llm",
|
|
90
|
+
context={"source": "user", "timestamp": "2025-11-10"}
|
|
91
|
+
)
|
|
92
|
+
print(f"Ingested: {result['item_id']}")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Authentication
|
|
96
|
+
|
|
97
|
+
The client requires a JWT token for authentication. You can provide it in two ways:
|
|
98
|
+
|
|
99
|
+
### 1. Pass as parameter
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
client = SmartMemoryClient(
|
|
103
|
+
base_url="http://localhost:9001",
|
|
104
|
+
api_key="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2. Set environment variable
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
export SMARTMEMORY_API_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
import os
|
|
116
|
+
client = SmartMemoryClient(
|
|
117
|
+
base_url="http://localhost:9001",
|
|
118
|
+
api_key=os.getenv("SMARTMEMORY_API_KEY")
|
|
119
|
+
)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Getting a JWT Token
|
|
123
|
+
|
|
124
|
+
SmartMemory uses Clerk for authentication (PLAT-SSO-IDP-1). `/auth/signup` and
|
|
125
|
+
`/auth/login` have been removed. Tokens are obtained via the Clerk-hosted login
|
|
126
|
+
flow or, for programmatic/service access, via the Clerk session exchange endpoint:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Exchange a Clerk session token for a SmartMemory JWT + cookies
|
|
130
|
+
curl -X POST "http://localhost:9001/auth/clerk/session" \
|
|
131
|
+
-H "Authorization: Bearer <clerk_session_token>"
|
|
132
|
+
# Response sets sm_access_token cookie and returns {"access_token": "eyJ..."}
|
|
133
|
+
|
|
134
|
+
# Verify / inspect the token
|
|
135
|
+
curl "http://localhost:9001/auth/me" \
|
|
136
|
+
-H "Authorization: Bearer <sm_access_token>"
|
|
137
|
+
# Response: {"id": "...", "email": "...", "default_team_id": "..."}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
For local E2E / integration tests, set `SM_E2E_TOKEN` and `SM_E2E_TEAM_ID` env vars
|
|
141
|
+
(captured from a real Clerk login or the `sm_access_token` cookie) and the test
|
|
142
|
+
fixtures will inject them automatically.
|
|
143
|
+
|
|
144
|
+
## API Reference
|
|
145
|
+
|
|
146
|
+
### Memory Operations
|
|
147
|
+
|
|
148
|
+
#### Add Memory
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
# Simple add
|
|
152
|
+
item_id = client.add("Remember this")
|
|
153
|
+
|
|
154
|
+
# With metadata
|
|
155
|
+
item_id = client.add(
|
|
156
|
+
"Important fact",
|
|
157
|
+
metadata={"source": "user", "priority": "high"},
|
|
158
|
+
use_pipeline=True # Run full extraction pipeline
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Fast add (no pipeline)
|
|
162
|
+
item_id = client.add("Quick note", use_pipeline=False)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### Search Memories
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
# Simple search
|
|
169
|
+
results = client.search("AI concepts", top_k=10)
|
|
170
|
+
|
|
171
|
+
# Search with SSG for better multi-hop reasoning
|
|
172
|
+
results = client.search("AI concepts", top_k=10, use_ssg=True)
|
|
173
|
+
|
|
174
|
+
# Search specific memory type
|
|
175
|
+
results = client.search("conversation", memory_type="episodic")
|
|
176
|
+
|
|
177
|
+
# Process results
|
|
178
|
+
for memory in results:
|
|
179
|
+
print(f"{memory.item_id}: {memory.content}")
|
|
180
|
+
print(f"Score: {memory.score}")
|
|
181
|
+
print(f"Metadata: {memory.metadata}")
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Advanced Search (SSG)
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
# Best for general queries (query_traversal algorithm)
|
|
188
|
+
results = client.search_advanced(
|
|
189
|
+
query="What are neural networks?",
|
|
190
|
+
algorithm="query_traversal",
|
|
191
|
+
max_results=15
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Best for high precision (triangulation_fulldim algorithm)
|
|
195
|
+
results = client.search_advanced(
|
|
196
|
+
query="Specific technical fact",
|
|
197
|
+
algorithm="triangulation_fulldim",
|
|
198
|
+
max_results=10
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Process results
|
|
202
|
+
for memory in results:
|
|
203
|
+
print(f"{memory.item_id}: {memory.content}")
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**SSG Benefits:**
|
|
207
|
+
- ✅ Superior multi-hop reasoning across related memories
|
|
208
|
+
- ✅ Better contextual retrieval (0.91 precision/recall vs 0.88)
|
|
209
|
+
- ✅ Higher faithfulness (facts vs opinions)
|
|
210
|
+
- ✅ Improved conversation and knowledge graph traversal
|
|
211
|
+
|
|
212
|
+
*Based on: Eric Lester. (2025). Novel Semantic Similarity Graph Traversal Algorithms for Semantic Retrieval Augmented Generation Systems.*
|
|
213
|
+
|
|
214
|
+
#### Get Memory
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
memory = client.get("item_123")
|
|
218
|
+
if memory:
|
|
219
|
+
print(f"Content: {memory.content}")
|
|
220
|
+
print(f"Type: {memory.memory_type}")
|
|
221
|
+
print(f"Metadata: {memory.metadata}")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### Update Memory
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
# Update content
|
|
228
|
+
client.update("item_123", content="Updated content")
|
|
229
|
+
|
|
230
|
+
# Update metadata
|
|
231
|
+
client.update("item_123", metadata={"updated": True})
|
|
232
|
+
|
|
233
|
+
# Update both
|
|
234
|
+
client.update("item_123", content="New content", metadata={"version": 2})
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### Delete Memory
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
if client.delete("item_123"):
|
|
241
|
+
print("Memory deleted")
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Ingestion
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
# Ingest with full pipeline
|
|
248
|
+
# Note: user_id, tenant_id, workspace_id are automatically derived from JWT token
|
|
249
|
+
result = client.ingest(
|
|
250
|
+
content="User: Hello\nAssistant: Hi there!",
|
|
251
|
+
extractor_name="llm",
|
|
252
|
+
context={
|
|
253
|
+
"conversation_id": "123",
|
|
254
|
+
"timestamp": "2025-11-10",
|
|
255
|
+
"source": "chat_interface"
|
|
256
|
+
}
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
print(f"Ingested: {result['item_id']}")
|
|
260
|
+
print(f"Queued for processing: {result['queued']}")
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Links and Relationships
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
# Create link between memories
|
|
267
|
+
client.link("concept_1", "concept_2", link_type="RELATED")
|
|
268
|
+
client.link("cause_id", "effect_id", link_type="CAUSES")
|
|
269
|
+
|
|
270
|
+
# Get neighbors
|
|
271
|
+
neighbors = client.get_neighbors("item_123")
|
|
272
|
+
for neighbor in neighbors:
|
|
273
|
+
print(f"{neighbor['id']}: {neighbor['relation']}")
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Code Search
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
# Semantic search over indexed code entities
|
|
280
|
+
results = client.code_search("authentication functions", semantic=True)
|
|
281
|
+
for entity in results:
|
|
282
|
+
print(f"{entity['name']} ({entity['entity_type']}) — {entity['file_path']}:{entity['line_number']}")
|
|
283
|
+
print(f" Score: {entity['score']}")
|
|
284
|
+
|
|
285
|
+
# Filter by entity type and repo
|
|
286
|
+
results = client.code_search(
|
|
287
|
+
"payment handler",
|
|
288
|
+
semantic=True,
|
|
289
|
+
entity_type="function",
|
|
290
|
+
repo="my-service"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Non-semantic (Cypher substring match, default)
|
|
294
|
+
results = client.code_search("authenticate")
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Enrichment
|
|
298
|
+
|
|
299
|
+
```python
|
|
300
|
+
# Enrich a memory
|
|
301
|
+
result = client.enrich("item_123", routines=["sentiment", "keywords"])
|
|
302
|
+
print(f"Enrichment result: {result}")
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Personalization
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
# Update user preferences
|
|
309
|
+
result = client.personalize(
|
|
310
|
+
traits={"learning_style": "visual"},
|
|
311
|
+
preferences={"language": "en", "complexity": "advanced"}
|
|
312
|
+
)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Feedback
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
# Provide feedback
|
|
319
|
+
result = client.provide_feedback(
|
|
320
|
+
feedback={
|
|
321
|
+
"item_id": "123",
|
|
322
|
+
"rating": 5,
|
|
323
|
+
"comment": "Very helpful"
|
|
324
|
+
},
|
|
325
|
+
memory_type="semantic"
|
|
326
|
+
)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Reasoning Traces (System 2 Memory)
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
# Extract reasoning from content
|
|
333
|
+
result = client.extract_reasoning('''
|
|
334
|
+
Thought: I need to analyze this bug.
|
|
335
|
+
Action: Let me search for the function.
|
|
336
|
+
Observation: Found the issue in line 42.
|
|
337
|
+
Conclusion: The fix is to add a null check.
|
|
338
|
+
''')
|
|
339
|
+
if result['has_reasoning']:
|
|
340
|
+
print(f"Found {result['step_count']} reasoning steps")
|
|
341
|
+
|
|
342
|
+
# Store a reasoning trace
|
|
343
|
+
result = client.store_reasoning_trace(
|
|
344
|
+
trace={
|
|
345
|
+
"trace_id": "trace_123",
|
|
346
|
+
"steps": [
|
|
347
|
+
{"type": "thought", "content": "Analyzing the problem"},
|
|
348
|
+
{"type": "conclusion", "content": "Found the solution"},
|
|
349
|
+
],
|
|
350
|
+
"task_context": {"goal": "Fix bug", "domain": "python"},
|
|
351
|
+
},
|
|
352
|
+
artifact_ids=["code_fix_456"] # Link to resulting artifacts
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
# Query reasoning traces ("why" queries)
|
|
356
|
+
result = client.query_reasoning("why did I use async/await?")
|
|
357
|
+
for trace in result['traces']:
|
|
358
|
+
print(f"Trace {trace['trace_id']}: {trace['content'][:100]}...")
|
|
359
|
+
|
|
360
|
+
# Get specific reasoning trace
|
|
361
|
+
trace = client.get_reasoning_trace("trace_123")
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Synthesis Evolution (Opinions & Observations)
|
|
365
|
+
|
|
366
|
+
```python
|
|
367
|
+
# Form opinions from episodic patterns
|
|
368
|
+
result = client.synthesize_opinions()
|
|
369
|
+
print(f"Status: {result['status']}")
|
|
370
|
+
|
|
371
|
+
# Create entity summaries from scattered facts
|
|
372
|
+
result = client.synthesize_observations()
|
|
373
|
+
print(f"Status: {result['status']}")
|
|
374
|
+
|
|
375
|
+
# Update opinion confidence based on new evidence
|
|
376
|
+
result = client.reinforce_opinions()
|
|
377
|
+
print(f"Status: {result['status']}")
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Health Check
|
|
381
|
+
|
|
382
|
+
```python
|
|
383
|
+
# Check service health
|
|
384
|
+
health = client.health_check()
|
|
385
|
+
print(health) # {'status': 'healthy'}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Summary Statistics
|
|
389
|
+
|
|
390
|
+
```python
|
|
391
|
+
# Get memory statistics
|
|
392
|
+
summary = client.get_summary()
|
|
393
|
+
print(f"Total memories: {summary.get('total_count', 0)}")
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Usage in Projects
|
|
397
|
+
|
|
398
|
+
### Maya Integration
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
# maya/requirements.txt
|
|
402
|
+
smartmemory-client>=1.0.0
|
|
403
|
+
|
|
404
|
+
# maya/maya/api/memory_manager.py
|
|
405
|
+
import os
|
|
406
|
+
from smartmemory_client import SmartMemoryClient
|
|
407
|
+
|
|
408
|
+
class MemoryManager:
|
|
409
|
+
def __init__(self, smartmemory_url):
|
|
410
|
+
self.client = SmartMemoryClient(
|
|
411
|
+
base_url=smartmemory_url,
|
|
412
|
+
api_key=os.getenv("SMARTMEMORY_API_KEY")
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
def ingest_conversation(self, content, metadata):
|
|
416
|
+
# user_id, tenant_id, workspace_id automatically from JWT token
|
|
417
|
+
return self.client.ingest(
|
|
418
|
+
content=content,
|
|
419
|
+
extractor_name="llm",
|
|
420
|
+
context=metadata
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
def retrieve_relevant_memories(self, query):
|
|
424
|
+
return self.client.search(query=query, top_k=5)
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Studio Integration
|
|
428
|
+
|
|
429
|
+
```python
|
|
430
|
+
import os
|
|
431
|
+
from smartmemory_client import SmartMemoryClient
|
|
432
|
+
|
|
433
|
+
client = SmartMemoryClient(
|
|
434
|
+
base_url=os.getenv("SMARTMEMORY_URL"),
|
|
435
|
+
api_key=os.getenv("SMARTMEMORY_API_KEY")
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
# Use client for memory operations
|
|
439
|
+
memories = client.search("project requirements", top_k=10)
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## Error Handling
|
|
443
|
+
|
|
444
|
+
```python
|
|
445
|
+
from smartmemory_client import SmartMemoryClient, SmartMemoryClientError
|
|
446
|
+
|
|
447
|
+
client = SmartMemoryClient("http://localhost:9001", api_key="token")
|
|
448
|
+
|
|
449
|
+
try:
|
|
450
|
+
item_id = client.add("Test memory")
|
|
451
|
+
except SmartMemoryClientError as e:
|
|
452
|
+
print(f"Error: {e}")
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
## Configuration
|
|
456
|
+
|
|
457
|
+
### Environment Variables
|
|
458
|
+
|
|
459
|
+
- `SMARTMEMORY_API_KEY` - JWT token for authentication
|
|
460
|
+
- `SMARTMEMORY_URL` - Base URL of the SmartMemory service (optional)
|
|
461
|
+
|
|
462
|
+
### Client Options
|
|
463
|
+
|
|
464
|
+
```python
|
|
465
|
+
client = SmartMemoryClient(
|
|
466
|
+
base_url="http://localhost:9001",
|
|
467
|
+
api_key="your_jwt_token",
|
|
468
|
+
timeout=30.0, # Request timeout in seconds
|
|
469
|
+
verify_ssl=True # Verify SSL certificates
|
|
470
|
+
)
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
## Development
|
|
474
|
+
|
|
475
|
+
### Setup
|
|
476
|
+
|
|
477
|
+
```bash
|
|
478
|
+
git clone https://github.com/smartmemory/smart-memory-client.git
|
|
479
|
+
cd smart-memory-client
|
|
480
|
+
pip install -e ".[dev]"
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Running Tests
|
|
484
|
+
|
|
485
|
+
```bash
|
|
486
|
+
# Run all tests
|
|
487
|
+
pytest
|
|
488
|
+
|
|
489
|
+
# Run with coverage
|
|
490
|
+
pytest --cov=smartmemory_client --cov-report=html
|
|
491
|
+
|
|
492
|
+
# Run specific test
|
|
493
|
+
pytest tests/test_client.py::test_add_memory
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Code Quality
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
# Format code
|
|
500
|
+
black smartmemory_client tests
|
|
501
|
+
|
|
502
|
+
# Sort imports
|
|
503
|
+
isort smartmemory_client tests
|
|
504
|
+
|
|
505
|
+
# Lint
|
|
506
|
+
ruff check smartmemory_client tests
|
|
507
|
+
|
|
508
|
+
# Type check
|
|
509
|
+
mypy smartmemory_client
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
## Versioning
|
|
513
|
+
|
|
514
|
+
This package follows [Semantic Versioning](https://semver.org/):
|
|
515
|
+
|
|
516
|
+
- **Major** (1.0.0 → 2.0.0): Breaking changes
|
|
517
|
+
- **Minor** (1.0.0 → 1.1.0): New features, backward compatible
|
|
518
|
+
- **Patch** (1.0.0 → 1.0.1): Bug fixes
|
|
519
|
+
|
|
520
|
+
The client version is synced with the SmartMemory Service version.
|
|
521
|
+
|
|
522
|
+
## Contributing
|
|
523
|
+
|
|
524
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
525
|
+
|
|
526
|
+
### Reporting Issues
|
|
527
|
+
|
|
528
|
+
Please report issues on [GitHub Issues](https://github.com/smartmemory/smart-memory-client/issues).
|
|
529
|
+
|
|
530
|
+
## License
|
|
531
|
+
|
|
532
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
533
|
+
|
|
534
|
+
## Links
|
|
535
|
+
|
|
536
|
+
- **Documentation**: https://docs.smartmemory.dev
|
|
537
|
+
- **SmartMemory Service**: https://github.com/smartmemory/smart-memory-service
|
|
538
|
+
- **PyPI Package**: https://pypi.org/project/smartmemory-client/
|
|
539
|
+
- **Issues**: https://github.com/smartmemory/smart-memory-client/issues
|
|
540
|
+
|
|
541
|
+
## Changelog
|
|
542
|
+
|
|
543
|
+
See [CHANGELOG.md](CHANGELOG.md) for version history.
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
**Made with ❤️ by the SmartMemory Team**
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
smartmemory_client/__init__.py,sha256=i7ZscDwqmnm79KuwYt_CMMbgZSMlZSlrgYJfCOVXorU,1520
|
|
2
|
+
smartmemory_client/client.py,sha256=JdthLZgmHepV3Sk6_vvlkPAeKhjSb19VKZByDGmQJQg,119603
|
|
3
|
+
smartmemory_client/models/__init__.py,sha256=P8umYIit1_7Q-B-Aj6L8a1RQNQ6ET6zMPLA43paRPUo,291
|
|
4
|
+
smartmemory_client/models/conversation.py,sha256=6FyF9iZxtgodZo4SSAOOwhtD1CM4aHrk8SmseuVXk4o,1109
|
|
5
|
+
smartmemory_client/models/memory_item.py,sha256=yuZIlj_yL5nXBD1h-6N-yuuE4L2Gl4iWTOT4ZHJrHwk,3346
|
|
6
|
+
smartmemory_client-0.5.0.dist-info/licenses/LICENSE,sha256=pFqlOGRWoN6up_Ieu7bKMYvBllZhcG_Z0QWwqn2e6SY,1073
|
|
7
|
+
smartmemory_client-0.5.0.dist-info/METADATA,sha256=WHO4oC2yMsqvhLagHnGryWaUvtZ0xJNky5VY90SlYo8,14127
|
|
8
|
+
smartmemory_client-0.5.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
9
|
+
smartmemory_client-0.5.0.dist-info/top_level.txt,sha256=a0l3QFj6t1qMlGMnFQNmcUPGxqUBFQiwIg0N9E-S1Fk,19
|
|
10
|
+
smartmemory_client-0.5.0.dist-info/RECORD,,
|