trainly 0.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.
- trainly-0.1.0/LICENSE +22 -0
- trainly-0.1.0/MANIFEST.in +8 -0
- trainly-0.1.0/PKG-INFO +649 -0
- trainly-0.1.0/README.md +615 -0
- trainly-0.1.0/examples/basic_usage.py +59 -0
- trainly-0.1.0/examples/context_manager.py +93 -0
- trainly-0.1.0/examples/file_management.py +124 -0
- trainly-0.1.0/examples/streaming_example.py +69 -0
- trainly-0.1.0/examples/v1_oauth_example.py +136 -0
- trainly-0.1.0/pyproject.toml +72 -0
- trainly-0.1.0/requirements.txt +2 -0
- trainly-0.1.0/setup.cfg +4 -0
- trainly-0.1.0/setup.py +11 -0
- trainly-0.1.0/tests/test_client.py +112 -0
- trainly-0.1.0/tests/test_models.py +186 -0
- trainly-0.1.0/trainly/__init__.py +39 -0
- trainly-0.1.0/trainly/client.py +413 -0
- trainly-0.1.0/trainly/models.py +161 -0
- trainly-0.1.0/trainly/v1_client.py +524 -0
- trainly-0.1.0/trainly.egg-info/PKG-INFO +649 -0
- trainly-0.1.0/trainly.egg-info/SOURCES.txt +22 -0
- trainly-0.1.0/trainly.egg-info/dependency_links.txt +1 -0
- trainly-0.1.0/trainly.egg-info/requires.txt +9 -0
- trainly-0.1.0/trainly.egg-info/top_level.txt +1 -0
trainly-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Trainly
|
|
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.
|
|
22
|
+
|
trainly-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: trainly
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Dead simple RAG integration for Python applications
|
|
5
|
+
Author-email: Trainly Team <support@trainly.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://trainly.com
|
|
8
|
+
Project-URL: Documentation, https://trainly.com/docs/python-sdk
|
|
9
|
+
Project-URL: Repository, https://github.com/trainly/python-sdk
|
|
10
|
+
Project-URL: Bug Reports, https://github.com/trainly/python-sdk/issues
|
|
11
|
+
Keywords: trainly,rag,ai,llm,knowledge-base,embeddings,vector-search
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: requests>=2.25.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: flake8>=5.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: mypy>=0.990; extra == "dev"
|
|
32
|
+
Requires-Dist: types-requests>=2.25.0; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# Trainly Python SDK
|
|
36
|
+
|
|
37
|
+
**Dead simple RAG integration for Python applications with V1 OAuth Authentication**
|
|
38
|
+
|
|
39
|
+
Go from `pip install` to working AI in under 5 minutes. Now supports direct OAuth integration with **permanent user subchats** and complete privacy protection.
|
|
40
|
+
|
|
41
|
+
[](https://badge.fury.io/py/trainly)
|
|
42
|
+
[](https://pypi.org/project/trainly/)
|
|
43
|
+
[](https://opensource.org/licenses/MIT)
|
|
44
|
+
|
|
45
|
+
## 🚀 Quick Start
|
|
46
|
+
|
|
47
|
+
### Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install trainly
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Basic Usage
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from trainly import TrainlyClient
|
|
57
|
+
|
|
58
|
+
# Initialize the client
|
|
59
|
+
trainly = TrainlyClient(
|
|
60
|
+
api_key="tk_your_api_key_here",
|
|
61
|
+
chat_id="chat_abc123"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Ask a question
|
|
65
|
+
response = trainly.query(
|
|
66
|
+
question="What are the main findings?"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
print("Answer:", response.answer)
|
|
70
|
+
print("Citations:", len(response.context))
|
|
71
|
+
|
|
72
|
+
# Access context details
|
|
73
|
+
for i, chunk in enumerate(response.context):
|
|
74
|
+
print(f"Citation [{i}]: {chunk.chunk_text[:100]}... (score: {chunk.score})")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 📖 Table of Contents
|
|
78
|
+
|
|
79
|
+
- [Installation](#installation)
|
|
80
|
+
- [Basic Usage](#basic-usage)
|
|
81
|
+
- [Type Hints](#type-hints)
|
|
82
|
+
- [Environment Variables](#environment-variables)
|
|
83
|
+
- [V1 OAuth Authentication](#v1-oauth-authentication)
|
|
84
|
+
- [Core Features](#core-features)
|
|
85
|
+
- [Query](#query)
|
|
86
|
+
- [Streaming Responses](#streaming-responses)
|
|
87
|
+
- [File Upload](#file-upload)
|
|
88
|
+
- [List Files](#list-files)
|
|
89
|
+
- [Delete Files](#delete-files)
|
|
90
|
+
- [Custom Scopes](#custom-scopes)
|
|
91
|
+
- [Error Handling](#error-handling)
|
|
92
|
+
- [Configuration Options](#configuration-options)
|
|
93
|
+
- [Examples](#examples)
|
|
94
|
+
- [API Reference](#api-reference)
|
|
95
|
+
|
|
96
|
+
## 🎯 Type Hints
|
|
97
|
+
|
|
98
|
+
The Python SDK includes full type hints for better IDE support:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from trainly import TrainlyClient, QueryResponse, ChunkScore
|
|
102
|
+
from typing import List
|
|
103
|
+
|
|
104
|
+
trainly = TrainlyClient(
|
|
105
|
+
api_key="tk_your_api_key",
|
|
106
|
+
chat_id="chat_abc123"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Fully typed response
|
|
110
|
+
response: QueryResponse = trainly.query(
|
|
111
|
+
question="What is the conclusion?",
|
|
112
|
+
model="gpt-4o",
|
|
113
|
+
temperature=0.5,
|
|
114
|
+
max_tokens=2000
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Access typed fields
|
|
118
|
+
answer: str = response.answer
|
|
119
|
+
context: List[ChunkScore] = response.context
|
|
120
|
+
if response.usage:
|
|
121
|
+
tokens: int = response.usage.total_tokens
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 🔐 Environment Variables
|
|
125
|
+
|
|
126
|
+
For better security, use environment variables for your credentials:
|
|
127
|
+
|
|
128
|
+
**.env**
|
|
129
|
+
```bash
|
|
130
|
+
TRAINLY_API_KEY=tk_your_api_key_here
|
|
131
|
+
TRAINLY_CHAT_ID=chat_abc123
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Python**
|
|
135
|
+
```python
|
|
136
|
+
from trainly import TrainlyClient
|
|
137
|
+
|
|
138
|
+
# Automatically loads from environment variables
|
|
139
|
+
trainly = TrainlyClient()
|
|
140
|
+
|
|
141
|
+
response = trainly.query("What are the key findings?")
|
|
142
|
+
print(response.answer)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## 🆕 V1 OAuth Authentication
|
|
146
|
+
|
|
147
|
+
For user-facing applications with OAuth:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from trainly import TrainlyV1Client
|
|
151
|
+
|
|
152
|
+
# User authenticates with their OAuth provider
|
|
153
|
+
user_token = get_user_oauth_token() # Your OAuth implementation
|
|
154
|
+
|
|
155
|
+
# Initialize V1 client with user's token
|
|
156
|
+
trainly = TrainlyV1Client(
|
|
157
|
+
user_token=user_token,
|
|
158
|
+
app_id="app_your_app_id"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
# Query user's private data
|
|
162
|
+
response = trainly.query(
|
|
163
|
+
messages=[
|
|
164
|
+
{"role": "user", "content": "What is in my documents?"}
|
|
165
|
+
]
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
print(response.answer)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### V1 Benefits
|
|
172
|
+
|
|
173
|
+
- ✅ **Permanent User Data**: Same user = same private subchat forever
|
|
174
|
+
- ✅ **Complete Privacy**: Developer never sees user files or queries
|
|
175
|
+
- ✅ **Any OAuth Provider**: Clerk, Auth0, Cognito, Firebase, custom OIDC
|
|
176
|
+
- ✅ **Zero Migration**: Works with your existing OAuth setup
|
|
177
|
+
- ✅ **Simple Integration**: Just provide `app_id` and user's OAuth token
|
|
178
|
+
|
|
179
|
+
## 🎨 Core Features
|
|
180
|
+
|
|
181
|
+
### Query
|
|
182
|
+
|
|
183
|
+
Ask questions about your knowledge base:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from trainly import TrainlyClient
|
|
187
|
+
|
|
188
|
+
trainly = TrainlyClient(
|
|
189
|
+
api_key="tk_your_api_key",
|
|
190
|
+
chat_id="chat_abc123"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Simple query
|
|
194
|
+
response = trainly.query("What are the main conclusions?")
|
|
195
|
+
print(response.answer)
|
|
196
|
+
|
|
197
|
+
# Query with custom parameters
|
|
198
|
+
response = trainly.query(
|
|
199
|
+
question="Explain the methodology in detail",
|
|
200
|
+
model="gpt-4o",
|
|
201
|
+
temperature=0.3,
|
|
202
|
+
max_tokens=2000,
|
|
203
|
+
include_context=True
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Access context chunks
|
|
207
|
+
for chunk in response.context:
|
|
208
|
+
print(f"Source: {chunk.source}")
|
|
209
|
+
print(f"Score: {chunk.score}")
|
|
210
|
+
print(f"Text: {chunk.chunk_text[:200]}...")
|
|
211
|
+
print("---")
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Streaming Responses
|
|
215
|
+
|
|
216
|
+
Stream responses in real-time:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from trainly import TrainlyClient
|
|
220
|
+
|
|
221
|
+
trainly = TrainlyClient(
|
|
222
|
+
api_key="tk_your_api_key",
|
|
223
|
+
chat_id="chat_abc123"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Stream response chunks
|
|
227
|
+
for chunk in trainly.query_stream("Explain the methodology in detail"):
|
|
228
|
+
if chunk.is_content:
|
|
229
|
+
print(chunk.data, end="", flush=True)
|
|
230
|
+
elif chunk.is_context:
|
|
231
|
+
print("\n\nContext chunks received:", len(chunk.data))
|
|
232
|
+
elif chunk.is_end:
|
|
233
|
+
print("\n\nStream complete!")
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### File Upload
|
|
237
|
+
|
|
238
|
+
Upload files to your knowledge base:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from trainly import TrainlyClient
|
|
242
|
+
|
|
243
|
+
trainly = TrainlyClient(
|
|
244
|
+
api_key="tk_your_api_key",
|
|
245
|
+
chat_id="chat_abc123"
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# Upload a file
|
|
249
|
+
result = trainly.upload_file("./research_paper.pdf")
|
|
250
|
+
print(f"Uploaded: {result.filename}")
|
|
251
|
+
print(f"File ID: {result.file_id}")
|
|
252
|
+
print(f"Size: {result.size_bytes} bytes")
|
|
253
|
+
|
|
254
|
+
# Upload with custom scopes
|
|
255
|
+
result = trainly.upload_file(
|
|
256
|
+
"./document.pdf",
|
|
257
|
+
scope_values={
|
|
258
|
+
"project_id": "proj_123",
|
|
259
|
+
"category": "research"
|
|
260
|
+
}
|
|
261
|
+
)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### List Files
|
|
265
|
+
|
|
266
|
+
Get all files in your knowledge base:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
from trainly import TrainlyClient
|
|
270
|
+
|
|
271
|
+
trainly = TrainlyClient(
|
|
272
|
+
api_key="tk_your_api_key",
|
|
273
|
+
chat_id="chat_abc123"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# List all files
|
|
277
|
+
files = trainly.list_files()
|
|
278
|
+
print(f"Total files: {files.total_files}")
|
|
279
|
+
print(f"Total size: {files.total_size_bytes} bytes")
|
|
280
|
+
|
|
281
|
+
for file in files.files:
|
|
282
|
+
print(f"- {file.filename}")
|
|
283
|
+
print(f" ID: {file.file_id}")
|
|
284
|
+
print(f" Size: {file.size_bytes} bytes")
|
|
285
|
+
print(f" Chunks: {file.chunk_count}")
|
|
286
|
+
print(f" Uploaded: {file.upload_datetime}")
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Delete Files
|
|
290
|
+
|
|
291
|
+
Remove files from your knowledge base:
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
from trainly import TrainlyClient
|
|
295
|
+
|
|
296
|
+
trainly = TrainlyClient(
|
|
297
|
+
api_key="tk_your_api_key",
|
|
298
|
+
chat_id="chat_abc123"
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# Delete a specific file
|
|
302
|
+
result = trainly.delete_file("v1_user_xyz_document.pdf_1609459200")
|
|
303
|
+
print(f"Deleted: {result.filename}")
|
|
304
|
+
print(f"Chunks deleted: {result.chunks_deleted}")
|
|
305
|
+
print(f"Space freed: {result.size_bytes_freed} bytes")
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## 🏷️ Custom Scopes
|
|
309
|
+
|
|
310
|
+
Tag your documents with custom attributes for powerful filtering:
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
from trainly import TrainlyClient
|
|
314
|
+
|
|
315
|
+
trainly = TrainlyClient(
|
|
316
|
+
api_key="tk_your_api_key",
|
|
317
|
+
chat_id="chat_abc123"
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
# Upload with scopes
|
|
321
|
+
trainly.upload_file(
|
|
322
|
+
"./project_report.pdf",
|
|
323
|
+
scope_values={
|
|
324
|
+
"playlist_id": "xyz123",
|
|
325
|
+
"workspace_id": "acme_corp",
|
|
326
|
+
"project": "alpha"
|
|
327
|
+
}
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
# Query with scope filters - only searches matching documents
|
|
331
|
+
response = trainly.query(
|
|
332
|
+
question="What are the key features?",
|
|
333
|
+
scope_filters={"playlist_id": "xyz123"}
|
|
334
|
+
)
|
|
335
|
+
# ☝️ Only searches documents with playlist_id="xyz123"
|
|
336
|
+
|
|
337
|
+
# Query with multiple filters
|
|
338
|
+
response = trainly.query(
|
|
339
|
+
question="Show me updates",
|
|
340
|
+
scope_filters={
|
|
341
|
+
"workspace_id": "acme_corp",
|
|
342
|
+
"project": "alpha"
|
|
343
|
+
}
|
|
344
|
+
)
|
|
345
|
+
# ☝️ Only searches documents matching ALL specified scopes
|
|
346
|
+
|
|
347
|
+
# Query everything (no filters)
|
|
348
|
+
response = trainly.query("What do I have?")
|
|
349
|
+
# ☝️ Searches ALL documents
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Use Cases:**
|
|
353
|
+
|
|
354
|
+
- 🎵 **Playlist Apps**: Filter by `playlist_id` to query specific playlists
|
|
355
|
+
- 🏢 **Multi-Tenant SaaS**: Filter by `tenant_id` or `workspace_id`
|
|
356
|
+
- 📁 **Project Management**: Filter by `project_id` or `team_id`
|
|
357
|
+
- 👥 **User Segmentation**: Filter by `user_tier`, `department`, etc.
|
|
358
|
+
|
|
359
|
+
## ⚠️ Error Handling
|
|
360
|
+
|
|
361
|
+
Always wrap API calls in try-except blocks:
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
from trainly import TrainlyClient, TrainlyError
|
|
365
|
+
|
|
366
|
+
trainly = TrainlyClient(
|
|
367
|
+
api_key="tk_your_api_key",
|
|
368
|
+
chat_id="chat_abc123"
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
response = trainly.query("What is the conclusion?")
|
|
373
|
+
print(response.answer)
|
|
374
|
+
except TrainlyError as e:
|
|
375
|
+
if e.status_code == 429:
|
|
376
|
+
print("Rate limit exceeded. Please wait and retry.")
|
|
377
|
+
elif e.status_code == 401:
|
|
378
|
+
print("Invalid API key")
|
|
379
|
+
elif e.status_code == 400:
|
|
380
|
+
print(f"Bad request: {e}")
|
|
381
|
+
else:
|
|
382
|
+
print(f"Error: {e}")
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## ⚙️ Configuration Options
|
|
386
|
+
|
|
387
|
+
### TrainlyClient
|
|
388
|
+
|
|
389
|
+
```python
|
|
390
|
+
from trainly import TrainlyClient
|
|
391
|
+
|
|
392
|
+
trainly = TrainlyClient(
|
|
393
|
+
api_key="tk_your_api_key", # Required (or set TRAINLY_API_KEY)
|
|
394
|
+
chat_id="chat_abc123", # Required (or set TRAINLY_CHAT_ID)
|
|
395
|
+
base_url="https://api.trainly.com", # Optional: Custom API URL
|
|
396
|
+
timeout=30, # Optional: Request timeout (seconds)
|
|
397
|
+
max_retries=3, # Optional: Max retry attempts
|
|
398
|
+
)
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### TrainlyV1Client (OAuth)
|
|
402
|
+
|
|
403
|
+
```python
|
|
404
|
+
from trainly import TrainlyV1Client
|
|
405
|
+
|
|
406
|
+
trainly = TrainlyV1Client(
|
|
407
|
+
user_token="user_oauth_token", # Required: User's OAuth token
|
|
408
|
+
app_id="app_your_app_id", # Required: Your app ID
|
|
409
|
+
base_url="https://api.trainly.com", # Optional: Custom API URL
|
|
410
|
+
timeout=30, # Optional: Request timeout (seconds)
|
|
411
|
+
)
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## 🔍 Examples
|
|
415
|
+
|
|
416
|
+
### Complete File Management
|
|
417
|
+
|
|
418
|
+
```python
|
|
419
|
+
from trainly import TrainlyClient, TrainlyError
|
|
420
|
+
|
|
421
|
+
def manage_files():
|
|
422
|
+
trainly = TrainlyClient(
|
|
423
|
+
api_key="tk_your_api_key",
|
|
424
|
+
chat_id="chat_abc123"
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
# Upload multiple files
|
|
428
|
+
files_to_upload = [
|
|
429
|
+
"./doc1.pdf",
|
|
430
|
+
"./doc2.txt",
|
|
431
|
+
"./doc3.docx"
|
|
432
|
+
]
|
|
433
|
+
|
|
434
|
+
for file_path in files_to_upload:
|
|
435
|
+
try:
|
|
436
|
+
result = trainly.upload_file(file_path)
|
|
437
|
+
print(f"✅ Uploaded: {result.filename}")
|
|
438
|
+
except TrainlyError as e:
|
|
439
|
+
print(f"❌ Failed to upload {file_path}: {e}")
|
|
440
|
+
|
|
441
|
+
# List all files
|
|
442
|
+
files = trainly.list_files()
|
|
443
|
+
print(f"\n📁 Total files: {files.total_files}")
|
|
444
|
+
print(f"💾 Total storage: {files.total_size_bytes / 1024 / 1024:.2f} MB")
|
|
445
|
+
|
|
446
|
+
for file in files.files:
|
|
447
|
+
print(f"\n- {file.filename}")
|
|
448
|
+
print(f" Size: {file.size_bytes / 1024:.2f} KB")
|
|
449
|
+
print(f" Chunks: {file.chunk_count}")
|
|
450
|
+
|
|
451
|
+
# Query the knowledge base
|
|
452
|
+
response = trainly.query("What are the key findings across all documents?")
|
|
453
|
+
print(f"\n🤖 AI Response:\n{response.answer}")
|
|
454
|
+
|
|
455
|
+
# Delete old files
|
|
456
|
+
if files.files:
|
|
457
|
+
oldest_file = files.files[0]
|
|
458
|
+
confirm = input(f"\nDelete {oldest_file.filename}? (y/n): ")
|
|
459
|
+
if confirm.lower() == 'y':
|
|
460
|
+
result = trainly.delete_file(oldest_file.file_id)
|
|
461
|
+
print(f"🗑️ Deleted: {result.filename}")
|
|
462
|
+
print(f"💾 Freed: {result.size_bytes_freed / 1024:.2f} KB")
|
|
463
|
+
|
|
464
|
+
if __name__ == "__main__":
|
|
465
|
+
manage_files()
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Context Manager Usage
|
|
469
|
+
|
|
470
|
+
```python
|
|
471
|
+
from trainly import TrainlyClient
|
|
472
|
+
|
|
473
|
+
# Use as context manager for automatic cleanup
|
|
474
|
+
with TrainlyClient(api_key="tk_your_api_key", chat_id="chat_abc123") as trainly:
|
|
475
|
+
response = trainly.query("What are the main points?")
|
|
476
|
+
print(response.answer)
|
|
477
|
+
|
|
478
|
+
files = trainly.list_files()
|
|
479
|
+
print(f"Total files: {files.total_files}")
|
|
480
|
+
# Session automatically closed
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### V1 OAuth Integration (Flask Example)
|
|
484
|
+
|
|
485
|
+
```python
|
|
486
|
+
from flask import Flask, request, jsonify
|
|
487
|
+
from trainly import TrainlyV1Client, TrainlyError
|
|
488
|
+
|
|
489
|
+
app = Flask(__name__)
|
|
490
|
+
|
|
491
|
+
@app.route("/api/query", methods=["POST"])
|
|
492
|
+
def query_user_data():
|
|
493
|
+
# Get user's OAuth token from request
|
|
494
|
+
auth_header = request.headers.get("Authorization")
|
|
495
|
+
if not auth_header or not auth_header.startswith("Bearer "):
|
|
496
|
+
return jsonify({"error": "Missing or invalid authorization"}), 401
|
|
497
|
+
|
|
498
|
+
user_token = auth_header.split(" ")[1]
|
|
499
|
+
|
|
500
|
+
try:
|
|
501
|
+
# Initialize V1 client with user's token
|
|
502
|
+
trainly = TrainlyV1Client(
|
|
503
|
+
user_token=user_token,
|
|
504
|
+
app_id="app_your_app_id"
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
# Get question from request
|
|
508
|
+
data = request.get_json()
|
|
509
|
+
question = data.get("question")
|
|
510
|
+
|
|
511
|
+
if not question:
|
|
512
|
+
return jsonify({"error": "Missing question"}), 400
|
|
513
|
+
|
|
514
|
+
# Query user's private knowledge base
|
|
515
|
+
response = trainly.query(
|
|
516
|
+
messages=[{"role": "user", "content": question}]
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
return jsonify({
|
|
520
|
+
"answer": response.answer,
|
|
521
|
+
"context_count": len(response.context),
|
|
522
|
+
"model": response.model
|
|
523
|
+
})
|
|
524
|
+
|
|
525
|
+
except TrainlyError as e:
|
|
526
|
+
return jsonify({"error": str(e)}), e.status_code or 500
|
|
527
|
+
finally:
|
|
528
|
+
if 'trainly' in locals():
|
|
529
|
+
trainly.close()
|
|
530
|
+
|
|
531
|
+
if __name__ == "__main__":
|
|
532
|
+
app.run(debug=True)
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
## 📚 API Reference
|
|
536
|
+
|
|
537
|
+
### TrainlyClient
|
|
538
|
+
|
|
539
|
+
#### `__init__(api_key, chat_id, base_url, timeout, max_retries)`
|
|
540
|
+
Initialize the client with API credentials.
|
|
541
|
+
|
|
542
|
+
#### `query(question, model, temperature, max_tokens, include_context, scope_filters) -> QueryResponse`
|
|
543
|
+
Query the knowledge base with a question.
|
|
544
|
+
|
|
545
|
+
#### `query_stream(question, model, temperature, max_tokens, scope_filters) -> Iterator[StreamChunk]`
|
|
546
|
+
Stream responses in real-time.
|
|
547
|
+
|
|
548
|
+
#### `upload_file(file_path, scope_values) -> UploadResult`
|
|
549
|
+
Upload a file to the knowledge base.
|
|
550
|
+
|
|
551
|
+
#### `list_files() -> FileListResult`
|
|
552
|
+
List all files in the knowledge base.
|
|
553
|
+
|
|
554
|
+
#### `delete_file(file_id) -> FileDeleteResult`
|
|
555
|
+
Delete a file from the knowledge base.
|
|
556
|
+
|
|
557
|
+
### TrainlyV1Client
|
|
558
|
+
|
|
559
|
+
#### `__init__(user_token, app_id, base_url, timeout)`
|
|
560
|
+
Initialize the V1 client with OAuth token.
|
|
561
|
+
|
|
562
|
+
#### `query(messages, model, temperature, max_tokens, scope_filters) -> QueryResponse`
|
|
563
|
+
Query the user's private knowledge base.
|
|
564
|
+
|
|
565
|
+
#### `upload_file(file_path, scope_values) -> UploadResult`
|
|
566
|
+
Upload a file to the user's knowledge base.
|
|
567
|
+
|
|
568
|
+
#### `upload_text(text, content_name, scope_values) -> UploadResult`
|
|
569
|
+
Upload text content to the user's knowledge base.
|
|
570
|
+
|
|
571
|
+
#### `list_files() -> FileListResult`
|
|
572
|
+
List all files in the user's knowledge base.
|
|
573
|
+
|
|
574
|
+
#### `delete_file(file_id) -> FileDeleteResult`
|
|
575
|
+
Delete a file from the user's knowledge base.
|
|
576
|
+
|
|
577
|
+
#### `bulk_upload_files(file_paths, scope_values) -> BulkUploadResult`
|
|
578
|
+
Upload multiple files at once (up to 10 files).
|
|
579
|
+
|
|
580
|
+
### Response Models
|
|
581
|
+
|
|
582
|
+
All response models are dataclasses with full type hints:
|
|
583
|
+
|
|
584
|
+
- `QueryResponse`: Answer and context from a query
|
|
585
|
+
- `ChunkScore`: A chunk of text with relevance score
|
|
586
|
+
- `Usage`: Token usage information
|
|
587
|
+
- `UploadResult`: Result of a file upload
|
|
588
|
+
- `FileInfo`: Information about a file
|
|
589
|
+
- `FileListResult`: List of files with metadata
|
|
590
|
+
- `FileDeleteResult`: Result of file deletion
|
|
591
|
+
- `BulkUploadResult`: Result of bulk upload operation
|
|
592
|
+
- `StreamChunk`: A chunk from a streaming response
|
|
593
|
+
|
|
594
|
+
### Exceptions
|
|
595
|
+
|
|
596
|
+
- `TrainlyError`: Base exception for all Trainly SDK errors
|
|
597
|
+
- `status_code`: HTTP status code (if applicable)
|
|
598
|
+
- `details`: Additional error details
|
|
599
|
+
|
|
600
|
+
## 🛠️ Development
|
|
601
|
+
|
|
602
|
+
### Install in Development Mode
|
|
603
|
+
|
|
604
|
+
```bash
|
|
605
|
+
git clone https://github.com/trainly/python-sdk.git
|
|
606
|
+
cd python-sdk
|
|
607
|
+
pip install -e ".[dev]"
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Run Tests
|
|
611
|
+
|
|
612
|
+
```bash
|
|
613
|
+
pytest
|
|
614
|
+
pytest --cov=trainly # With coverage
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
### Format Code
|
|
618
|
+
|
|
619
|
+
```bash
|
|
620
|
+
black trainly examples tests
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### Type Checking
|
|
624
|
+
|
|
625
|
+
```bash
|
|
626
|
+
mypy trainly
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
## 📝 License
|
|
630
|
+
|
|
631
|
+
MIT - see [LICENSE](LICENSE) file for details.
|
|
632
|
+
|
|
633
|
+
## 🤝 Contributing
|
|
634
|
+
|
|
635
|
+
Contributions welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
636
|
+
|
|
637
|
+
## 🆘 Support
|
|
638
|
+
|
|
639
|
+
- 📖 [Documentation](https://trainly.com/docs/python-sdk)
|
|
640
|
+
- 💬 [Discord Community](https://discord.gg/trainly)
|
|
641
|
+
- 📧 [Email Support](mailto:support@trainly.com)
|
|
642
|
+
- 🐛 [Report Issues](https://github.com/trainly/python-sdk/issues)
|
|
643
|
+
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
**Made with ❤️ by the Trainly team**
|
|
647
|
+
|
|
648
|
+
*The simplest way to add AI to your Python app*
|
|
649
|
+
|