sovant 1.0.5__tar.gz → 1.0.6__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.
Potentially problematic release.
This version of sovant might be problematic. Click here for more details.
- sovant-1.0.6/PKG-INFO +475 -0
- sovant-1.0.6/README.md +462 -0
- {sovant-1.0.5 → sovant-1.0.6}/pyproject.toml +1 -1
- sovant-1.0.6/src/sovant.egg-info/PKG-INFO +475 -0
- sovant-1.0.5/PKG-INFO +0 -118
- sovant-1.0.5/README.md +0 -105
- sovant-1.0.5/src/sovant.egg-info/PKG-INFO +0 -118
- {sovant-1.0.5 → sovant-1.0.6}/LICENSE +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/MANIFEST.in +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/setup.cfg +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant/__init__.py +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant/base_client.py +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant/client.py +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant/exceptions.py +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant/models.py +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant/resources/__init__.py +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant/resources/memories.py +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant/resources/threads.py +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant/types.py +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant.egg-info/SOURCES.txt +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant.egg-info/dependency_links.txt +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant.egg-info/requires.txt +0 -0
- {sovant-1.0.5 → sovant-1.0.6}/src/sovant.egg-info/top_level.txt +0 -0
sovant-1.0.6/PKG-INFO
ADDED
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sovant
|
|
3
|
+
Version: 1.0.6
|
|
4
|
+
Summary: Sovant Memory-as-a-Service Python SDK
|
|
5
|
+
Author: Sovant
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: httpx>=0.27.0
|
|
11
|
+
Requires-Dist: pydantic>=2.8.2
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# Sovant Python SDK
|
|
15
|
+
|
|
16
|
+
Sovant is Memory-as-a-Service: a durable, queryable memory layer for AI apps with cross-model recall, hybrid (semantic + deterministic) retrieval, and simple SDKs.
|
|
17
|
+
|
|
18
|
+
[](https://pypi.org/project/sovant/)
|
|
19
|
+
[](https://opensource.org/licenses/MIT)
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install sovant
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from sovant import Sovant
|
|
31
|
+
|
|
32
|
+
# Initialize the client
|
|
33
|
+
client = Sovant(api_key="sk_live_your_api_key_here", base_url="https://sovant.ai")
|
|
34
|
+
|
|
35
|
+
# Create a memory
|
|
36
|
+
mem = client.memory.create({
|
|
37
|
+
"content": "User prefers dark mode",
|
|
38
|
+
"type": "preference",
|
|
39
|
+
"tags": ["ui", "settings"]
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
# Search memories
|
|
43
|
+
results = client.memory.search({
|
|
44
|
+
"query": "user preferences",
|
|
45
|
+
"limit": 10
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
# Update a memory
|
|
49
|
+
updated = client.memory.update(mem["id"], {
|
|
50
|
+
"tags": ["ui", "settings", "theme"]
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
# Delete a memory
|
|
54
|
+
client.memory.delete(mem["id"])
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Chat in 60 Seconds
|
|
58
|
+
|
|
59
|
+
Stream real-time chat responses with memory context:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from sovant import Sovant
|
|
63
|
+
import sys
|
|
64
|
+
|
|
65
|
+
client = Sovant(api_key="sk_live_your_api_key_here", base_url="https://sovant.ai")
|
|
66
|
+
|
|
67
|
+
# Create a chat session
|
|
68
|
+
session = client.chat.create_session({"title": "Demo"})
|
|
69
|
+
|
|
70
|
+
# Stream a response
|
|
71
|
+
stream = client.chat.send_message(
|
|
72
|
+
session["id"],
|
|
73
|
+
"hello",
|
|
74
|
+
{
|
|
75
|
+
"provider": "openai",
|
|
76
|
+
"model": "gpt-4o-mini",
|
|
77
|
+
"use_memory": True
|
|
78
|
+
},
|
|
79
|
+
stream=True
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
for ev in stream:
|
|
83
|
+
if ev["type"] == "delta":
|
|
84
|
+
sys.stdout.write(ev.get("data", ""))
|
|
85
|
+
elif ev["type"] == "done":
|
|
86
|
+
print("\n[done]")
|
|
87
|
+
|
|
88
|
+
# Get chat history
|
|
89
|
+
messages = client.chat.get_messages(session["id"])
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Profile Recall Helpers
|
|
93
|
+
|
|
94
|
+
Save and recall user profile facts with canonical patterns:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
# Extract profile entity from text
|
|
98
|
+
fact = client.recall.extract_profile("i'm from kuching")
|
|
99
|
+
# -> {"entity": "location", "value": "kuching"} | None
|
|
100
|
+
|
|
101
|
+
if fact:
|
|
102
|
+
client.recall.save_profile_fact(fact) # canonicalizes and persists
|
|
103
|
+
|
|
104
|
+
# Get all profile facts
|
|
105
|
+
profile = client.recall.get_profile_facts()
|
|
106
|
+
# -> {"name": "...", "age": "...", "location": "...", "preferences": [...]}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Configuration
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from sovant import Sovant
|
|
113
|
+
|
|
114
|
+
client = Sovant(
|
|
115
|
+
api_key="sk_live_your_api_key_here", # Required
|
|
116
|
+
base_url="https://sovant.ai", # Optional, API endpoint
|
|
117
|
+
timeout=30.0, # Optional, request timeout in seconds (default: 30.0)
|
|
118
|
+
max_retries=3, # Optional, max retry attempts (default: 3)
|
|
119
|
+
debug=False, # Optional, enable debug logging (default: False)
|
|
120
|
+
)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The SDK handles dual authentication automatically, preferring the `x-sovant-api-key` header over `Authorization: Bearer`.
|
|
124
|
+
|
|
125
|
+
## API Reference
|
|
126
|
+
|
|
127
|
+
### Memory Operations
|
|
128
|
+
|
|
129
|
+
#### Create Memory
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
memory = client.memory.create({
|
|
133
|
+
"content": "Customer contacted support about billing",
|
|
134
|
+
"type": "observation", # 'journal' | 'insight' | 'observation' | 'task' | 'preference'
|
|
135
|
+
"tags": ["support", "billing"],
|
|
136
|
+
"metadata": {"ticket_id": "12345"},
|
|
137
|
+
"thread_id": "thread_abc123", # Optional thread association
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### List Memories
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
memories = client.memory.list({
|
|
145
|
+
"limit": 20, # Max items per page (default: 20)
|
|
146
|
+
"offset": 0, # Pagination offset
|
|
147
|
+
"tags": ["billing"], # Filter by tags
|
|
148
|
+
"type": "observation", # Filter by type
|
|
149
|
+
"is_archived": False, # Filter archived status
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
print(memories["memories"]) # Array of memories
|
|
153
|
+
print(memories["total"]) # Total count
|
|
154
|
+
print(memories["has_more"]) # More pages available
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Get Memory by ID
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
memory = client.memory.get("mem_123abc")
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Update Memory (Partial)
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
updated = client.memory.update("mem_123abc", {
|
|
167
|
+
"tags": ["support", "billing", "resolved"],
|
|
168
|
+
"metadata": {
|
|
169
|
+
**memory.get("metadata", {}),
|
|
170
|
+
"resolved": True,
|
|
171
|
+
},
|
|
172
|
+
"is_archived": True,
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Replace Memory (Full)
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
replaced = client.memory.put("mem_123abc", {
|
|
180
|
+
"content": "Updated content here", # Required for PUT
|
|
181
|
+
"type": "observation",
|
|
182
|
+
"tags": ["updated"],
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### Delete Memory
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
client.memory.delete("mem_123abc")
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### Search Memories
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
# Semantic search
|
|
196
|
+
semantic_results = client.memory.search({
|
|
197
|
+
"query": "customer preferences about notifications",
|
|
198
|
+
"limit": 10,
|
|
199
|
+
"type": "preference",
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
# Filter-based search
|
|
203
|
+
filter_results = client.memory.search({
|
|
204
|
+
"tags": ["settings", "notifications"],
|
|
205
|
+
"from_date": "2024-01-01",
|
|
206
|
+
"to_date": "2024-12-31",
|
|
207
|
+
"limit": 20,
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### Batch Operations
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
batch = client.memory.batch({
|
|
215
|
+
"operations": [
|
|
216
|
+
{
|
|
217
|
+
"op": "create",
|
|
218
|
+
"data": {
|
|
219
|
+
"content": "First memory",
|
|
220
|
+
"type": "journal",
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"op": "update",
|
|
225
|
+
"id": "mem_123abc",
|
|
226
|
+
"data": {
|
|
227
|
+
"tags": ["updated"],
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"op": "delete",
|
|
232
|
+
"id": "mem_456def",
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
print(batch["results"]) # Individual operation results
|
|
238
|
+
print(batch["summary"]) # Summary statistics
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Thread Management
|
|
242
|
+
|
|
243
|
+
Associate memories with conversation threads:
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
# Create a thread
|
|
247
|
+
thread = client.threads.create({
|
|
248
|
+
"title": "Customer Support Session",
|
|
249
|
+
"metadata": {"user_id": "user_123"}
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
# List threads
|
|
253
|
+
threads = client.threads.list({
|
|
254
|
+
"limit": 10,
|
|
255
|
+
"offset": 0
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
# Get thread by ID
|
|
259
|
+
thread = client.threads.get("thread_abc123")
|
|
260
|
+
|
|
261
|
+
# Update thread
|
|
262
|
+
updated_thread = client.threads.update("thread_abc123", {
|
|
263
|
+
"title": "Resolved: Billing Issue",
|
|
264
|
+
"metadata": {"status": "resolved"}
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
# Delete thread
|
|
268
|
+
client.threads.delete("thread_abc123")
|
|
269
|
+
|
|
270
|
+
# Link memory to thread
|
|
271
|
+
client.threads.link_memory("thread_abc123", "mem_123abc")
|
|
272
|
+
|
|
273
|
+
# Create memories within a thread
|
|
274
|
+
memory1 = client.memory.create({
|
|
275
|
+
"content": "User asked about pricing",
|
|
276
|
+
"type": "observation",
|
|
277
|
+
"thread_id": "thread_abc123",
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
memory2 = client.memory.create({
|
|
281
|
+
"content": "User selected enterprise plan",
|
|
282
|
+
"type": "observation",
|
|
283
|
+
"thread_id": "thread_abc123",
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
# List memories in a thread
|
|
287
|
+
thread_memories = client.memory.list({
|
|
288
|
+
"thread_id": "thread_abc123",
|
|
289
|
+
})
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### API Key Management
|
|
293
|
+
|
|
294
|
+
Manage API keys programmatically:
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
# List all API keys
|
|
298
|
+
keys = client.keys.list()
|
|
299
|
+
print(keys) # Array of key objects
|
|
300
|
+
|
|
301
|
+
# Create a new API key
|
|
302
|
+
new_key = client.keys.create({"name": "CI key"})
|
|
303
|
+
print(new_key["key"]) # The actual secret key (only shown once!)
|
|
304
|
+
|
|
305
|
+
# Update key metadata
|
|
306
|
+
client.keys.update(new_key["id"], {"name": "Production key"})
|
|
307
|
+
|
|
308
|
+
# Revoke a key
|
|
309
|
+
client.keys.revoke(new_key["id"])
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Memory Types
|
|
313
|
+
|
|
314
|
+
- **journal** - Chronological entries and logs
|
|
315
|
+
- **insight** - Derived patterns and conclusions
|
|
316
|
+
- **observation** - Factual, observed information
|
|
317
|
+
- **task** - Action items and todos
|
|
318
|
+
- **preference** - User preferences and settings
|
|
319
|
+
|
|
320
|
+
## Error Handling
|
|
321
|
+
|
|
322
|
+
The SDK provides typed errors for better error handling:
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
from sovant import Sovant, SovantError, AuthError, RateLimitError, NetworkError, TimeoutError
|
|
326
|
+
|
|
327
|
+
try:
|
|
328
|
+
memory = client.memory.get("invalid_id")
|
|
329
|
+
except AuthError as e:
|
|
330
|
+
print(f"Authentication failed: {e}")
|
|
331
|
+
# Handle authentication error
|
|
332
|
+
except RateLimitError as e:
|
|
333
|
+
print(f"Rate limit exceeded: {e}")
|
|
334
|
+
print(f"Retry after: {e.retry_after}")
|
|
335
|
+
# Handle rate limiting
|
|
336
|
+
except NetworkError as e:
|
|
337
|
+
print(f"Network error: {e}")
|
|
338
|
+
# Handle network issues
|
|
339
|
+
except TimeoutError as e:
|
|
340
|
+
print(f"Request timed out: {e}")
|
|
341
|
+
# Handle timeout
|
|
342
|
+
except SovantError as e:
|
|
343
|
+
print(f"API Error: {e}")
|
|
344
|
+
print(f"Status: {e.status}")
|
|
345
|
+
print(f"Request ID: {e.request_id}")
|
|
346
|
+
|
|
347
|
+
if e.status == 404:
|
|
348
|
+
# Handle not found
|
|
349
|
+
pass
|
|
350
|
+
elif e.status == 400:
|
|
351
|
+
# Handle bad request
|
|
352
|
+
pass
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Advanced Features
|
|
356
|
+
|
|
357
|
+
### Retry Configuration
|
|
358
|
+
|
|
359
|
+
The SDK automatically retries failed requests with exponential backoff:
|
|
360
|
+
|
|
361
|
+
```python
|
|
362
|
+
client = Sovant(
|
|
363
|
+
api_key="sk_live_...",
|
|
364
|
+
max_retries=5, # Increase retry attempts
|
|
365
|
+
timeout=60.0, # Increase timeout for slow connections
|
|
366
|
+
)
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Debug Mode
|
|
370
|
+
|
|
371
|
+
Enable debug logging to see detailed request/response information:
|
|
372
|
+
|
|
373
|
+
```python
|
|
374
|
+
client = Sovant(
|
|
375
|
+
api_key="sk_live_...",
|
|
376
|
+
debug=True, # Enable debug output
|
|
377
|
+
)
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Custom Base URL
|
|
381
|
+
|
|
382
|
+
Connect to different environments:
|
|
383
|
+
|
|
384
|
+
```python
|
|
385
|
+
client = Sovant(
|
|
386
|
+
api_key="sk_live_...",
|
|
387
|
+
base_url="https://staging.sovant.ai",
|
|
388
|
+
)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Best Practices
|
|
392
|
+
|
|
393
|
+
1. **Use appropriate memory types** - Choose the correct type for your use case
|
|
394
|
+
2. **Add meaningful tags** - Tags improve searchability and organization
|
|
395
|
+
3. **Use threads** - Group related memories together
|
|
396
|
+
4. **Handle errors gracefully** - Implement proper error handling
|
|
397
|
+
5. **Batch operations** - Use batch API for multiple operations
|
|
398
|
+
6. **Archive don't delete** - Consider archiving instead of deleting
|
|
399
|
+
|
|
400
|
+
## Examples
|
|
401
|
+
|
|
402
|
+
### Customer Support Integration
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
# Track customer interaction
|
|
406
|
+
interaction = client.memory.create({
|
|
407
|
+
"content": "Customer reported slow dashboard loading",
|
|
408
|
+
"type": "observation",
|
|
409
|
+
"thread_id": f"ticket_{ticket_id}",
|
|
410
|
+
"tags": ["support", "performance", "dashboard"],
|
|
411
|
+
"metadata": {
|
|
412
|
+
"ticket_id": ticket_id,
|
|
413
|
+
"customer_id": customer_id,
|
|
414
|
+
"priority": "high",
|
|
415
|
+
},
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
# Record resolution
|
|
419
|
+
resolution = client.memory.create({
|
|
420
|
+
"content": "Resolved by clearing cache and upgrading plan",
|
|
421
|
+
"type": "insight",
|
|
422
|
+
"thread_id": f"ticket_{ticket_id}",
|
|
423
|
+
"tags": ["support", "resolved"],
|
|
424
|
+
"metadata": {
|
|
425
|
+
"ticket_id": ticket_id,
|
|
426
|
+
"resolution_time": "2h",
|
|
427
|
+
},
|
|
428
|
+
})
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### User Preference Tracking
|
|
432
|
+
|
|
433
|
+
```python
|
|
434
|
+
# Store preference
|
|
435
|
+
preference = client.memory.create({
|
|
436
|
+
"content": "User prefers email notifications over SMS",
|
|
437
|
+
"type": "preference",
|
|
438
|
+
"tags": ["notifications", "email", "settings"],
|
|
439
|
+
"metadata": {
|
|
440
|
+
"user_id": user_id,
|
|
441
|
+
"setting": "notification_channel",
|
|
442
|
+
"value": "email",
|
|
443
|
+
},
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
# Query preferences
|
|
447
|
+
preferences = client.memory.search({
|
|
448
|
+
"query": "notification preferences",
|
|
449
|
+
"type": "preference",
|
|
450
|
+
"tags": ["notifications"],
|
|
451
|
+
})
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Rate Limiting
|
|
455
|
+
|
|
456
|
+
The API implements rate limiting. The SDK automatically handles rate limit responses with retries. Rate limit headers are included in responses:
|
|
457
|
+
|
|
458
|
+
- `X-RateLimit-Limit` - Request limit per window
|
|
459
|
+
- `X-RateLimit-Remaining` - Remaining requests
|
|
460
|
+
- `X-RateLimit-Reset` - Reset timestamp
|
|
461
|
+
|
|
462
|
+
## Support
|
|
463
|
+
|
|
464
|
+
- Documentation: [https://sovant.ai/docs](https://sovant.ai/docs)
|
|
465
|
+
- API/Auth docs: [https://sovant.ai/docs/security/auth](https://sovant.ai/docs/security/auth)
|
|
466
|
+
- Issues: [GitHub Issues](https://github.com/sovant-ai/python-sdk/issues)
|
|
467
|
+
- Support: support@sovant.ai
|
|
468
|
+
|
|
469
|
+
## Changelog
|
|
470
|
+
|
|
471
|
+
See [CHANGELOG.md](./CHANGELOG.md) for a detailed history of changes.
|
|
472
|
+
|
|
473
|
+
## License
|
|
474
|
+
|
|
475
|
+
MIT - See [LICENSE](LICENSE) file for details.
|