sovant 1.1.0__tar.gz

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