sovant 1.0.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.

Potentially problematic release.


This version of sovant might be problematic. Click here for more details.

sovant/types.py ADDED
@@ -0,0 +1,224 @@
1
+ """Type definitions for the Sovant SDK."""
2
+
3
+ from datetime import datetime
4
+ from enum import Enum
5
+ from typing import Any, Dict, List, Literal, Optional, Union
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class MemoryType(str, Enum):
11
+ """Types of memories."""
12
+
13
+ OBSERVATION = "observation"
14
+ REFLECTION = "reflection"
15
+ DECISION = "decision"
16
+ EMOTION = "emotion"
17
+ LEARNING = "learning"
18
+ PREFERENCE = "preference"
19
+ EVENT = "event"
20
+ CONVERSATION = "conversation"
21
+ TASK = "task"
22
+ REMINDER = "reminder"
23
+ QUESTION = "question"
24
+ INSIGHT = "insight"
25
+ JOURNAL = "journal"
26
+ ROUTINE = "routine"
27
+ META = "meta"
28
+
29
+
30
+ class EmotionType(str, Enum):
31
+ """Types of emotions."""
32
+
33
+ NEUTRAL = "neutral"
34
+ HAPPY = "happy"
35
+ EXCITED = "excited"
36
+ ANXIOUS = "anxious"
37
+ SAD = "sad"
38
+ STRESSED = "stressed"
39
+ CALM = "calm"
40
+ REFLECTIVE = "reflective"
41
+ POSITIVE = "positive"
42
+ NEGATIVE = "negative"
43
+
44
+
45
+ class ThreadStatus(str, Enum):
46
+ """Status of a thread."""
47
+
48
+ ACTIVE = "active"
49
+ ARCHIVED = "archived"
50
+ COMPLETED = "completed"
51
+
52
+
53
+ class EmotionalContext(BaseModel):
54
+ """Emotional context of a memory."""
55
+
56
+ type: EmotionType
57
+ intensity: Optional[float] = Field(None, ge=0, le=1)
58
+ tone_tags: Optional[List[str]] = Field(None, alias="toneTags")
59
+
60
+
61
+ class Memory(BaseModel):
62
+ """A memory object."""
63
+
64
+ id: str
65
+ content: str
66
+ type: MemoryType
67
+ importance: float = Field(ge=0, le=1)
68
+ created_at: datetime
69
+ updated_at: Optional[datetime] = None
70
+ user_id: Optional[str] = None
71
+ metadata: Optional[Dict[str, Any]] = None
72
+ tags: Optional[List[str]] = None
73
+ emotion: Optional[EmotionalContext] = None
74
+ decisions: Optional[List[str]] = None
75
+ questions: Optional[List[str]] = None
76
+ action_items: Optional[List[str]] = None
77
+ follow_up_required: Optional[bool] = None
78
+ follow_up_due: Optional[datetime] = None
79
+ is_pinned: Optional[bool] = None
80
+ is_archived: Optional[bool] = None
81
+ title: Optional[str] = None
82
+ thread_ids: Optional[List[str]] = None
83
+
84
+
85
+ class CreateMemoryInput(BaseModel):
86
+ """Input for creating a memory."""
87
+
88
+ content: str
89
+ type: Optional[MemoryType] = MemoryType.OBSERVATION
90
+ importance: Optional[float] = Field(0.5, ge=0, le=1)
91
+ metadata: Optional[Dict[str, Any]] = None
92
+ tags: Optional[List[str]] = None
93
+ emotion: Optional[EmotionalContext] = None
94
+ decisions: Optional[List[str]] = None
95
+ questions: Optional[List[str]] = None
96
+ action_items: Optional[List[str]] = None
97
+ follow_up_required: Optional[bool] = None
98
+ follow_up_due: Optional[datetime] = None
99
+ title: Optional[str] = None
100
+ thread_ids: Optional[List[str]] = None
101
+
102
+
103
+ class UpdateMemoryInput(BaseModel):
104
+ """Input for updating a memory."""
105
+
106
+ content: Optional[str] = None
107
+ type: Optional[MemoryType] = None
108
+ importance: Optional[float] = Field(None, ge=0, le=1)
109
+ metadata: Optional[Dict[str, Any]] = None
110
+ tags: Optional[List[str]] = None
111
+ emotion: Optional[EmotionalContext] = None
112
+ decisions: Optional[List[str]] = None
113
+ questions: Optional[List[str]] = None
114
+ action_items: Optional[List[str]] = None
115
+ follow_up_required: Optional[bool] = None
116
+ follow_up_due: Optional[datetime] = None
117
+ title: Optional[str] = None
118
+ is_pinned: Optional[bool] = None
119
+ is_archived: Optional[bool] = None
120
+
121
+
122
+ class SearchOptions(BaseModel):
123
+ """Options for searching memories."""
124
+
125
+ query: str
126
+ limit: Optional[int] = 10
127
+ offset: Optional[int] = 0
128
+ type: Optional[Union[MemoryType, List[MemoryType]]] = None
129
+ tags: Optional[List[str]] = None
130
+ created_after: Optional[datetime] = None
131
+ created_before: Optional[datetime] = None
132
+ search_type: Optional[Literal["semantic", "keyword", "hybrid"]] = "hybrid"
133
+ include_archived: Optional[bool] = False
134
+ metadata_filters: Optional[Dict[str, Any]] = None
135
+ sort_by: Optional[Literal["relevance", "created_at", "importance"]] = "relevance"
136
+ sort_order: Optional[Literal["asc", "desc"]] = "desc"
137
+
138
+
139
+ class SearchResult(Memory):
140
+ """A search result with relevance score."""
141
+
142
+ relevance_score: float = Field(ge=0, le=1)
143
+ highlights: Optional[List[str]] = None
144
+
145
+
146
+ class Thread(BaseModel):
147
+ """A thread object."""
148
+
149
+ id: str
150
+ name: str
151
+ description: Optional[str] = None
152
+ memory_ids: List[str]
153
+ status: ThreadStatus
154
+ metadata: Optional[Dict[str, Any]] = None
155
+ tags: Optional[List[str]] = None
156
+ created_at: datetime
157
+ updated_at: datetime
158
+ last_activity_at: datetime
159
+ user_id: Optional[str] = None
160
+
161
+
162
+ class CreateThreadInput(BaseModel):
163
+ """Input for creating a thread."""
164
+
165
+ name: str
166
+ description: Optional[str] = None
167
+ memory_ids: Optional[List[str]] = None
168
+ metadata: Optional[Dict[str, Any]] = None
169
+ tags: Optional[List[str]] = None
170
+ status: Optional[ThreadStatus] = ThreadStatus.ACTIVE
171
+
172
+
173
+ class UpdateThreadInput(BaseModel):
174
+ """Input for updating a thread."""
175
+
176
+ name: Optional[str] = None
177
+ description: Optional[str] = None
178
+ metadata: Optional[Dict[str, Any]] = None
179
+ tags: Optional[List[str]] = None
180
+ status: Optional[ThreadStatus] = None
181
+
182
+
183
+ class ThreadStats(BaseModel):
184
+ """Statistics for a thread."""
185
+
186
+ memory_count: int
187
+ memory_types: Dict[str, int]
188
+ emotion_types: Dict[str, int]
189
+ avg_importance: float
190
+ follow_up_count: int
191
+ earliest_memory: datetime
192
+ latest_memory: datetime
193
+ total_decisions: int
194
+ total_questions: int
195
+ total_action_items: int
196
+
197
+
198
+ class BatchCreateResult(BaseModel):
199
+ """Result of batch create operation."""
200
+
201
+ success: List[Memory]
202
+ failed: List[Dict[str, Any]]
203
+ success_count: int
204
+ failed_count: int
205
+
206
+
207
+ class PaginatedResponse(BaseModel):
208
+ """Paginated response."""
209
+
210
+ data: List[Any]
211
+ total: int
212
+ limit: int
213
+ offset: int
214
+ has_more: bool
215
+
216
+
217
+ class Config(BaseModel):
218
+ """Configuration for the Sovant client."""
219
+
220
+ api_key: str
221
+ base_url: str = "https://api.sovant.ai/v1"
222
+ timeout: int = 30
223
+ max_retries: int = 3
224
+ retry_delay: float = 1.0
@@ -0,0 +1,492 @@
1
+ Metadata-Version: 2.4
2
+ Name: sovant
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for Sovant Memory API
5
+ Author-email: Sovant AI <support@sovant.ai>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 Sovant AI
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://sovant.ai
28
+ Project-URL: Documentation, https://docs.sovant.ai
29
+ Project-URL: Repository, https://github.com/sovant-ai/python-sdk
30
+ Project-URL: Issues, https://github.com/sovant-ai/python-sdk/issues
31
+ Keywords: sovant,memory,api,ai,sdk
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: Programming Language :: Python :: 3.8
37
+ Classifier: Programming Language :: Python :: 3.9
38
+ Classifier: Programming Language :: Python :: 3.10
39
+ Classifier: Programming Language :: Python :: 3.11
40
+ Classifier: Programming Language :: Python :: 3.12
41
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
42
+ Requires-Python: >=3.8
43
+ Description-Content-Type: text/markdown
44
+ License-File: LICENSE
45
+ Requires-Dist: httpx>=0.25.0
46
+ Requires-Dist: pydantic>=2.0.0
47
+ Requires-Dist: python-dateutil>=2.8.0
48
+ Provides-Extra: async
49
+ Requires-Dist: httpx[http2]; extra == "async"
50
+ Provides-Extra: dev
51
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
52
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
53
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
54
+ Requires-Dist: black>=23.0.0; extra == "dev"
55
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
56
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
57
+ Dynamic: license-file
58
+
59
+ # Sovant Python SDK
60
+
61
+ Official Python SDK for the Sovant Memory API. Build AI applications with persistent memory, semantic search, and intelligent context management.
62
+
63
+ > **Note: Coming Soon!** This SDK is currently in development and will be available on PyPI soon. In the meantime, you can use our REST API directly.
64
+
65
+ ## Installation
66
+
67
+ ```bash
68
+ # Coming soon
69
+ pip install sovant
70
+ ```
71
+
72
+ For async support:
73
+
74
+ ```bash
75
+ # Coming soon
76
+ pip install sovant[async]
77
+ ```
78
+
79
+ ## Quick Start
80
+
81
+ ```python
82
+ from sovant import SovantClient
83
+
84
+ # Initialize the client
85
+ client = SovantClient(api_key="YOUR_API_KEY")
86
+
87
+ # Create a memory
88
+ memory = client.memories.create({
89
+ "content": "User prefers Python for data science projects",
90
+ "type": "preference",
91
+ "metadata": {
92
+ "confidence": 0.95,
93
+ "context": "programming_languages"
94
+ }
95
+ })
96
+
97
+ print(f"Created memory: {memory.id}")
98
+
99
+ # Search memories
100
+ results = client.memories.search({
101
+ "query": "What programming languages does the user prefer?",
102
+ "limit": 5
103
+ })
104
+
105
+ for result in results:
106
+ print(f"- {result.content} (relevance: {result.relevance_score:.2f})")
107
+ ```
108
+
109
+ ## Features
110
+
111
+ - 🐍 **Full Type Hints** - Complete type annotations for better IDE support
112
+ - 🔄 **Async/Await Support** - Built for modern Python applications
113
+ - 🔁 **Automatic Retries** - Configurable retry logic with exponential backoff
114
+ - 📦 **Batch Operations** - Efficient bulk create/delete operations
115
+ - 🎯 **Smart Search** - Semantic, keyword, and hybrid search modes
116
+ - 🧵 **Thread Management** - Organize memories into contextual threads
117
+ - 📊 **Analytics** - Built-in insights and statistics
118
+ - 🛡️ **Comprehensive Error Handling** - Typed exceptions for all error cases
119
+
120
+ ## Configuration
121
+
122
+ ```python
123
+ from sovant import SovantClient, Config
124
+
125
+ # Using configuration object
126
+ config = Config(
127
+ api_key="YOUR_API_KEY",
128
+ base_url="https://api.sovant.ai/v1", # Optional
129
+ timeout=30, # Request timeout in seconds
130
+ max_retries=3, # Number of retries for failed requests
131
+ retry_delay=1.0 # Initial delay between retries
132
+ )
133
+
134
+ client = SovantClient(config)
135
+ ```
136
+
137
+ You can also use environment variables:
138
+
139
+ ```bash
140
+ export SOVANT_API_KEY="YOUR_API_KEY"
141
+ ```
142
+
143
+ ```python
144
+ from sovant import SovantClient
145
+
146
+ # Automatically reads from SOVANT_API_KEY env var
147
+ client = SovantClient()
148
+ ```
149
+
150
+ ## Memory Operations
151
+
152
+ ### Create a Memory
153
+
154
+ ```python
155
+ from sovant import MemoryType, EmotionType
156
+
157
+ memory = client.memories.create({
158
+ "content": "User completed the onboarding tutorial",
159
+ "type": MemoryType.EVENT,
160
+ "importance": 0.8,
161
+ "metadata": {
162
+ "step_completed": "tutorial",
163
+ "duration_seconds": 180
164
+ },
165
+ "tags": ["onboarding", "milestone"],
166
+ "emotion": {
167
+ "type": EmotionType.POSITIVE,
168
+ "intensity": 0.7
169
+ },
170
+ "action_items": ["Send welcome email", "Unlock advanced features"]
171
+ })
172
+ ```
173
+
174
+ ### Update a Memory
175
+
176
+ ```python
177
+ updated = client.memories.update(memory.id, {
178
+ "importance": 0.9,
179
+ "follow_up_required": True,
180
+ "follow_up_due": "2024-12-31T23:59:59Z"
181
+ })
182
+ ```
183
+
184
+ ### Search Memories
185
+
186
+ ```python
187
+ # Semantic search
188
+ semantic_results = client.memories.search({
189
+ "query": "user achievements and milestones",
190
+ "search_type": "semantic",
191
+ "limit": 10
192
+ })
193
+
194
+ # Filtered search
195
+ from sovant import MemoryType
196
+
197
+ filtered_results = client.memories.search({
198
+ "query": "preferences",
199
+ "type": [MemoryType.PREFERENCE, MemoryType.DECISION],
200
+ "tags": ["important"],
201
+ "created_after": "2024-01-01",
202
+ "sort_by": "importance",
203
+ "sort_order": "desc"
204
+ })
205
+ ```
206
+
207
+ ### Batch Operations
208
+
209
+ ```python
210
+ # Batch create
211
+ memories_data = [
212
+ {"content": "User is a data scientist", "type": "observation"},
213
+ {"content": "User works with ML models", "type": "learning"},
214
+ {"content": "User prefers Jupyter notebooks", "type": "preference"}
215
+ ]
216
+
217
+ batch_result = client.memories.create_batch(memories_data)
218
+ print(f"Created {batch_result.success_count} memories")
219
+ print(f"Failed {batch_result.failed_count} memories")
220
+
221
+ # Batch delete
222
+ result = client.memories.delete_batch(["mem_123", "mem_456", "mem_789"])
223
+ print(f"Deleted {result['deleted']} memories")
224
+ ```
225
+
226
+ ## Thread Management
227
+
228
+ ### Create a Thread
229
+
230
+ ```python
231
+ thread = client.threads.create({
232
+ "name": "Customer Support Chat",
233
+ "description": "Tracking customer issues and resolutions",
234
+ "tags": ["support", "customer", "priority"]
235
+ })
236
+ ```
237
+
238
+ ### Add Memories to Thread
239
+
240
+ ```python
241
+ # Add existing memories
242
+ client.threads.add_memories(thread.id, [memory1.id, memory2.id])
243
+
244
+ # Create and add in one operation
245
+ new_memory = client.memories.create({
246
+ "content": "Customer reported login issue",
247
+ "type": "conversation",
248
+ "thread_ids": [thread.id]
249
+ })
250
+ ```
251
+
252
+ ### Get Thread with Memories
253
+
254
+ ```python
255
+ # Get thread with all memories included
256
+ thread_with_memories = client.threads.get(thread.id, include_memories=True)
257
+
258
+ print(f"Thread: {thread_with_memories.name}")
259
+ print(f"Total memories: {len(thread_with_memories.memories)}")
260
+
261
+ for memory in thread_with_memories.memories:
262
+ print(f"- {memory.content}")
263
+ ```
264
+
265
+ ### Thread Analytics
266
+
267
+ ```python
268
+ stats = client.threads.get_stats(thread.id)
269
+
270
+ print(f"Total memories: {stats.memory_count}")
271
+ print(f"Average importance: {stats.avg_importance:.2f}")
272
+ print(f"Decisions made: {stats.total_decisions}")
273
+ print(f"Open questions: {stats.total_questions}")
274
+ print(f"Action items: {stats.total_action_items}")
275
+ ```
276
+
277
+ ## Async Support
278
+
279
+ ```python
280
+ import asyncio
281
+ from sovant import AsyncSovantClient
282
+
283
+ async def main():
284
+ # Use async client for better performance
285
+ async with AsyncSovantClient(api_key="YOUR_API_KEY") as client:
286
+ # Create multiple memories concurrently
287
+ memories = await asyncio.gather(
288
+ client.memories.create({"content": "Memory 1"}),
289
+ client.memories.create({"content": "Memory 2"}),
290
+ client.memories.create({"content": "Memory 3"})
291
+ )
292
+
293
+ print(f"Created {len(memories)} memories")
294
+
295
+ # Async search
296
+ results = await client.memories.search({
297
+ "query": "user preferences",
298
+ "search_type": "semantic"
299
+ })
300
+
301
+ for result in results:
302
+ print(f"- {result.content}")
303
+
304
+ asyncio.run(main())
305
+ ```
306
+
307
+ ## Error Handling
308
+
309
+ The SDK provides typed exceptions for different error scenarios:
310
+
311
+ ```python
312
+ from sovant import (
313
+ AuthenticationError,
314
+ RateLimitError,
315
+ ValidationError,
316
+ NotFoundError,
317
+ NetworkError
318
+ )
319
+
320
+ try:
321
+ memory = client.memories.get("invalid_id")
322
+ except NotFoundError:
323
+ print("Memory not found")
324
+ except RateLimitError as e:
325
+ print(f"Rate limited. Retry after {e.retry_after} seconds")
326
+ except ValidationError as e:
327
+ print(f"Validation errors: {e.errors}")
328
+ except AuthenticationError:
329
+ print("Invalid API key")
330
+ except NetworkError:
331
+ print("Network error occurred")
332
+ ```
333
+
334
+ ## Advanced Usage
335
+
336
+ ### Memory Insights
337
+
338
+ ```python
339
+ insights = client.memories.get_insights(
340
+ time_range="last_30_days",
341
+ group_by="type"
342
+ )
343
+
344
+ print(f"Total memories: {insights['total_count']}")
345
+ print(f"Type distribution: {insights['type_distribution']}")
346
+ print(f"Emotion distribution: {insights['emotion_distribution']}")
347
+ print(f"Average importance: {insights['importance_stats']['avg']}")
348
+ print(f"Growth rate: {insights['growth_rate']}%")
349
+ ```
350
+
351
+ ### Related Memories
352
+
353
+ ```python
354
+ # Find memories related to a specific memory
355
+ related = client.memories.get_related(
356
+ memory_id=memory.id,
357
+ limit=5,
358
+ threshold=0.7 # Minimum similarity score
359
+ )
360
+
361
+ for r in related:
362
+ print(f"- {r.content} (similarity: {r.relevance_score:.2f})")
363
+ ```
364
+
365
+ ### Thread Operations
366
+
367
+ ```python
368
+ # Archive a thread
369
+ archived = client.threads.archive(thread.id)
370
+
371
+ # Search threads
372
+ threads = client.threads.search(
373
+ query="customer support",
374
+ status="active",
375
+ tags=["priority"]
376
+ )
377
+
378
+ # Merge threads
379
+ merged = client.threads.merge(
380
+ target_id=main_thread.id,
381
+ source_ids=[thread2.id, thread3.id]
382
+ )
383
+
384
+ # Clone a thread
385
+ cloned = client.threads.clone(
386
+ thread_id=thread.id,
387
+ name="Cloned Thread",
388
+ include_memories=True
389
+ )
390
+ ```
391
+
392
+ ### Pagination
393
+
394
+ ```python
395
+ # List memories with pagination
396
+ page1 = client.memories.list(limit=20, offset=0)
397
+ print(f"Total memories: {page1.total}")
398
+ print(f"Has more: {page1.has_more}")
399
+
400
+ # Get next page
401
+ if page1.has_more:
402
+ page2 = client.memories.list(limit=20, offset=20)
403
+ ```
404
+
405
+ ## Type Safety
406
+
407
+ The SDK uses Pydantic for data validation and provides enums for better type safety:
408
+
409
+ ```python
410
+ from sovant import MemoryType, EmotionType, ThreadStatus
411
+
412
+ # Using enums ensures valid values
413
+ memory = client.memories.create({
414
+ "content": "Important decision made",
415
+ "type": MemoryType.DECISION, # Type-safe enum
416
+ "emotion": {
417
+ "type": EmotionType.NEUTRAL, # Type-safe enum
418
+ "intensity": 0.5
419
+ }
420
+ })
421
+
422
+ # IDE will provide autocompletion for all fields
423
+ thread = client.threads.create({
424
+ "name": "Project Planning",
425
+ "status": ThreadStatus.ACTIVE # Type-safe enum
426
+ })
427
+ ```
428
+
429
+ ## Best Practices
430
+
431
+ 1. **Use batch operations for bulk actions**
432
+
433
+ ```python
434
+ # Good - single API call
435
+ batch_result = client.memories.create_batch(memories_list)
436
+
437
+ # Avoid - multiple API calls
438
+ for memory_data in memories_list:
439
+ client.memories.create(memory_data)
440
+ ```
441
+
442
+ 2. **Use async client for concurrent operations**
443
+
444
+ ```python
445
+ async with AsyncSovantClient() as client:
446
+ results = await asyncio.gather(*tasks)
447
+ ```
448
+
449
+ 3. **Handle errors gracefully**
450
+
451
+ ```python
452
+ try:
453
+ result = client.memories.search({"query": "test"})
454
+ except RateLimitError as e:
455
+ await asyncio.sleep(e.retry_after)
456
+ # Retry the request
457
+ ```
458
+
459
+ 4. **Use type hints for better IDE support**
460
+
461
+ ```python
462
+ from sovant import Memory, SearchResult
463
+
464
+ def process_memory(memory: Memory) -> None:
465
+ print(f"Processing {memory.id}")
466
+
467
+ def handle_results(results: list[SearchResult]) -> None:
468
+ for result in results:
469
+ print(f"Score: {result.relevance_score}")
470
+ ```
471
+
472
+ ## Examples
473
+
474
+ Check out the [examples directory](https://github.com/sovant-ai/python-sdk/tree/main/examples) for complete working examples:
475
+
476
+ - Basic CRUD operations
477
+ - Advanced search techniques
478
+ - Thread management workflows
479
+ - Async patterns
480
+ - Error handling
481
+ - Data analysis with pandas integration
482
+
483
+ ## Support
484
+
485
+ - 📚 [Documentation](https://docs.sovant.ai)
486
+ - 💬 [Discord Community](https://discord.gg/sovant)
487
+ - 🐛 [Issue Tracker](https://github.com/sovant-ai/python-sdk/issues)
488
+ - 📧 [Email Support](mailto:support@sovant.ai)
489
+
490
+ ## License
491
+
492
+ MIT © Sovant AI