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/__init__.py +63 -0
- sovant/base_client.py +256 -0
- sovant/client.py +87 -0
- sovant/exceptions.py +58 -0
- sovant/resources/__init__.py +6 -0
- sovant/resources/memories.py +317 -0
- sovant/resources/threads.py +362 -0
- sovant/types.py +224 -0
- sovant-1.0.0.dist-info/METADATA +492 -0
- sovant-1.0.0.dist-info/RECORD +13 -0
- sovant-1.0.0.dist-info/WHEEL +5 -0
- sovant-1.0.0.dist-info/licenses/LICENSE +21 -0
- sovant-1.0.0.dist-info/top_level.txt +1 -0
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
|