slide-narrator 5.5.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.
- narrator/__init__.py +18 -0
- narrator/database/__init__.py +8 -0
- narrator/database/cli.py +222 -0
- narrator/database/migrations/__init__.py +6 -0
- narrator/database/models.py +70 -0
- narrator/database/storage_backend.py +624 -0
- narrator/database/thread_store.py +282 -0
- narrator/models/__init__.py +9 -0
- narrator/models/attachment.py +386 -0
- narrator/models/message.py +512 -0
- narrator/models/thread.py +467 -0
- narrator/storage/__init__.py +7 -0
- narrator/storage/file_store.py +536 -0
- narrator/utils/__init__.py +9 -0
- narrator/utils/logging.py +52 -0
- slide_narrator-5.5.0.dist-info/METADATA +558 -0
- slide_narrator-5.5.0.dist-info/RECORD +20 -0
- slide_narrator-5.5.0.dist-info/WHEEL +4 -0
- slide_narrator-5.5.0.dist-info/entry_points.txt +2 -0
- slide_narrator-5.5.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: slide-narrator
|
|
3
|
+
Version: 5.5.0
|
|
4
|
+
Summary: Thread and file storage components for conversational AI - the companion to Tyler AI framework
|
|
5
|
+
Project-URL: Homepage, https://github.com/adamwdraper/slide
|
|
6
|
+
Project-URL: Repository, https://github.com/adamwdraper/slide
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/adamwdraper/slide/issues
|
|
8
|
+
Author: adamwdraper
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: aiosqlite>=0.21.0
|
|
21
|
+
Requires-Dist: alembic>=1.14.1
|
|
22
|
+
Requires-Dist: asyncpg>=0.30.0
|
|
23
|
+
Requires-Dist: click>=8.1.8
|
|
24
|
+
Requires-Dist: filetype>=1.2.0
|
|
25
|
+
Requires-Dist: greenlet>=3.2.3
|
|
26
|
+
Requires-Dist: pydantic>=2.10.4
|
|
27
|
+
Requires-Dist: pypdf>=5.3.0
|
|
28
|
+
Requires-Dist: sqlalchemy>=2.0.36
|
|
29
|
+
Requires-Dist: typing-extensions>=4.12.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: coverage>=7.6.10; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.25.2; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=8.3.4; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# The Narrator
|
|
38
|
+
|
|
39
|
+
Thread and file storage components for conversational AI - the storage foundation for the Slide ecosystem.
|
|
40
|
+
|
|
41
|
+
## Overview
|
|
42
|
+
|
|
43
|
+
The Narrator provides robust, production-ready storage solutions for conversational AI applications, serving as the storage layer for Tyler and other Slide components. It includes:
|
|
44
|
+
|
|
45
|
+
- **ThreadStore**: Persistent storage for conversation threads with support for both in-memory and SQL backends
|
|
46
|
+
- **FileStore**: Secure file storage with automatic processing for various file types
|
|
47
|
+
- **Models**: Pydantic models for threads, messages, and attachments
|
|
48
|
+
- **CLI Tools**: Command-line interface for database management and setup
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
### ThreadStore
|
|
53
|
+
- **Multiple Backends**: In-memory (development), SQLite (local), PostgreSQL (production)
|
|
54
|
+
- **Async/Await Support**: Built for modern Python async applications
|
|
55
|
+
- **Message Filtering**: Automatic handling of system vs. user messages
|
|
56
|
+
- **Platform Integration**: Support for external platform references (Slack, Discord, etc.)
|
|
57
|
+
- **Connection Pooling**: Production-ready database connection management
|
|
58
|
+
|
|
59
|
+
### FileStore
|
|
60
|
+
- **Secure Storage**: Automatic file validation and type checking
|
|
61
|
+
- **Multiple Formats**: Support for documents, images, audio, and more
|
|
62
|
+
- **Content Processing**: Automatic text extraction from PDFs, image analysis
|
|
63
|
+
- **Storage Limits**: Configurable file size and total storage limits
|
|
64
|
+
- **Sharded Storage**: Efficient file organization to prevent directory bloat
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Using uv (recommended)
|
|
70
|
+
uv add slide-narrator
|
|
71
|
+
|
|
72
|
+
# Using pip (fallback)
|
|
73
|
+
pip install slide-narrator
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Setup
|
|
77
|
+
|
|
78
|
+
### Docker Setup (Recommended for Development)
|
|
79
|
+
|
|
80
|
+
For local development with PostgreSQL, Narrator includes built-in Docker commands:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# One-command setup: starts PostgreSQL and initializes tables
|
|
84
|
+
uv run narrator docker-setup
|
|
85
|
+
|
|
86
|
+
# This will:
|
|
87
|
+
# 1. Start a PostgreSQL container
|
|
88
|
+
# 2. Wait for it to be ready
|
|
89
|
+
# 3. Initialize the database tables
|
|
90
|
+
# 4. Show you the connection string
|
|
91
|
+
|
|
92
|
+
# The database will be available at:
|
|
93
|
+
# postgresql+asyncpg://narrator:narrator_dev@localhost:5432/narrator
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
To manage the Docker container:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Stop container (preserves data)
|
|
100
|
+
uv run narrator docker-stop
|
|
101
|
+
|
|
102
|
+
# Stop and remove all data
|
|
103
|
+
uv run narrator docker-stop --remove-volumes
|
|
104
|
+
|
|
105
|
+
# Start container again
|
|
106
|
+
uv run narrator docker-start
|
|
107
|
+
|
|
108
|
+
# Check database status
|
|
109
|
+
uv run narrator status
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
For custom configurations, the Docker commands respect environment variables:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Use a different port
|
|
116
|
+
uv run narrator docker-setup --port 5433
|
|
117
|
+
|
|
118
|
+
# Or set environment variables (matching docker-compose.yml)
|
|
119
|
+
export NARRATOR_DB_NAME=mydb
|
|
120
|
+
export NARRATOR_DB_USER=myuser
|
|
121
|
+
export NARRATOR_DB_PASSWORD=mypassword
|
|
122
|
+
export NARRATOR_DB_PORT=5433
|
|
123
|
+
|
|
124
|
+
# Then run docker-setup
|
|
125
|
+
uv run narrator docker-setup
|
|
126
|
+
|
|
127
|
+
# This will create:
|
|
128
|
+
# postgresql+asyncpg://myuser:mypassword@localhost:5433/mydb
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Database Setup
|
|
132
|
+
|
|
133
|
+
For production use with PostgreSQL or SQLite persistence, you'll need to initialize the database tables:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Initialize database tables (PostgreSQL)
|
|
137
|
+
uv run narrator init --database-url "postgresql+asyncpg://user:password@localhost/dbname"
|
|
138
|
+
|
|
139
|
+
# Initialize database tables (SQLite)
|
|
140
|
+
uv run narrator init --database-url "sqlite+aiosqlite:///path/to/your/database.db"
|
|
141
|
+
|
|
142
|
+
# Check database status
|
|
143
|
+
uv run narrator status --database-url "postgresql+asyncpg://user:password@localhost/dbname"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
You can also use environment variables instead of passing the database URL:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Set environment variable
|
|
150
|
+
export NARRATOR_DATABASE_URL="postgresql+asyncpg://user:password@localhost/dbname"
|
|
151
|
+
|
|
152
|
+
# Then run without --database-url flag
|
|
153
|
+
uv run narrator init
|
|
154
|
+
uv run narrator status
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Environment Variables
|
|
158
|
+
|
|
159
|
+
Configure the narrator using environment variables:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# Database settings
|
|
163
|
+
NARRATOR_DATABASE_URL="postgresql+asyncpg://user:password@localhost/dbname"
|
|
164
|
+
NARRATOR_DB_POOL_SIZE=5 # Connection pool size
|
|
165
|
+
NARRATOR_DB_MAX_OVERFLOW=10 # Max additional connections
|
|
166
|
+
NARRATOR_DB_POOL_TIMEOUT=30 # Connection timeout (seconds)
|
|
167
|
+
NARRATOR_DB_POOL_RECYCLE=300 # Connection recycle time (seconds)
|
|
168
|
+
NARRATOR_DB_ECHO=false # Enable SQL logging
|
|
169
|
+
|
|
170
|
+
# File storage settings
|
|
171
|
+
NARRATOR_FILE_STORAGE_PATH=/path/to/files # Storage directory
|
|
172
|
+
NARRATOR_MAX_FILE_SIZE=52428800 # 50MB max file size
|
|
173
|
+
NARRATOR_MAX_STORAGE_SIZE=5368709120 # 5GB max total storage
|
|
174
|
+
NARRATOR_ALLOWED_MIME_TYPES=image/jpeg,application/pdf # Allowed file types
|
|
175
|
+
|
|
176
|
+
# Logging
|
|
177
|
+
NARRATOR_LOG_LEVEL=INFO # Log level
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Quick Start
|
|
181
|
+
|
|
182
|
+
### Basic Thread Storage
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
import asyncio
|
|
186
|
+
from narrator import ThreadStore, Thread, Message
|
|
187
|
+
|
|
188
|
+
async def main():
|
|
189
|
+
# Create an in-memory store for development
|
|
190
|
+
store = await ThreadStore.create()
|
|
191
|
+
|
|
192
|
+
# Create a thread
|
|
193
|
+
thread = Thread(title="My Conversation")
|
|
194
|
+
|
|
195
|
+
# Add messages
|
|
196
|
+
thread.add_message(Message(role="user", content="Hello!"))
|
|
197
|
+
thread.add_message(Message(role="assistant", content="Hi there!"))
|
|
198
|
+
|
|
199
|
+
# Save the thread
|
|
200
|
+
await store.save(thread)
|
|
201
|
+
|
|
202
|
+
# Retrieve the thread
|
|
203
|
+
retrieved = await store.get(thread.id)
|
|
204
|
+
print(f"Thread: {retrieved.title}")
|
|
205
|
+
print(f"Messages: {len(retrieved.messages)}")
|
|
206
|
+
|
|
207
|
+
asyncio.run(main())
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### File Storage
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
import asyncio
|
|
214
|
+
from narrator import FileStore
|
|
215
|
+
|
|
216
|
+
async def main():
|
|
217
|
+
# Create a file store
|
|
218
|
+
store = await FileStore.create()
|
|
219
|
+
|
|
220
|
+
# Save a file
|
|
221
|
+
content = b"Hello, world!"
|
|
222
|
+
metadata = await store.save(content, "hello.txt", "text/plain")
|
|
223
|
+
|
|
224
|
+
print(f"File ID: {metadata['id']}")
|
|
225
|
+
print(f"Storage path: {metadata['storage_path']}")
|
|
226
|
+
|
|
227
|
+
# Retrieve the file
|
|
228
|
+
retrieved_content = await store.get(metadata['id'])
|
|
229
|
+
print(f"Content: {retrieved_content.decode()}")
|
|
230
|
+
|
|
231
|
+
asyncio.run(main())
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Database Storage
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
import asyncio
|
|
238
|
+
from narrator import ThreadStore
|
|
239
|
+
|
|
240
|
+
async def main():
|
|
241
|
+
# Use SQLite for persistent storage
|
|
242
|
+
store = await ThreadStore.create("sqlite+aiosqlite:///conversations.db")
|
|
243
|
+
|
|
244
|
+
# Use PostgreSQL for production
|
|
245
|
+
# store = await ThreadStore.create("postgresql+asyncpg://user:pass@localhost/dbname")
|
|
246
|
+
|
|
247
|
+
# The API is the same regardless of backend
|
|
248
|
+
thread = Thread(title="Persistent Conversation")
|
|
249
|
+
await store.save(thread)
|
|
250
|
+
|
|
251
|
+
asyncio.run(main())
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Configuration
|
|
255
|
+
|
|
256
|
+
### Database Configuration
|
|
257
|
+
|
|
258
|
+
The Narrator supports multiple database backends:
|
|
259
|
+
|
|
260
|
+
#### Memory storage (Default)
|
|
261
|
+
```python
|
|
262
|
+
from narrator import ThreadStore
|
|
263
|
+
|
|
264
|
+
# Use factory pattern for immediate connection validation
|
|
265
|
+
store = await ThreadStore.create() # Uses memory backend
|
|
266
|
+
|
|
267
|
+
# Thread operations are immediate
|
|
268
|
+
thread = Thread()
|
|
269
|
+
await store.save(thread)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Key characteristics:
|
|
273
|
+
- Fastest possible performance (direct dictionary access)
|
|
274
|
+
- No persistence (data is lost when program exits)
|
|
275
|
+
- No setup required (works out of the box)
|
|
276
|
+
- Perfect for scripts and one-off conversations
|
|
277
|
+
- Great for testing and development
|
|
278
|
+
|
|
279
|
+
#### PostgreSQL storage
|
|
280
|
+
```python
|
|
281
|
+
from narrator import ThreadStore
|
|
282
|
+
|
|
283
|
+
# Use factory pattern for immediate connection validation
|
|
284
|
+
db_url = "postgresql+asyncpg://user:pass@localhost/dbname"
|
|
285
|
+
try:
|
|
286
|
+
store = await ThreadStore.create(db_url)
|
|
287
|
+
print("Connected to database successfully")
|
|
288
|
+
except Exception as e:
|
|
289
|
+
print(f"Database connection failed: {e}")
|
|
290
|
+
# Handle connection failure appropriately
|
|
291
|
+
|
|
292
|
+
# Must save threads and changes to persist
|
|
293
|
+
thread = Thread()
|
|
294
|
+
await store.save(thread) # Required
|
|
295
|
+
thread.add_message(message)
|
|
296
|
+
await store.save(thread) # Save changes
|
|
297
|
+
|
|
298
|
+
# Always use thread.id with database storage
|
|
299
|
+
result = await store.get(thread.id)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Key characteristics:
|
|
303
|
+
- Async operations for non-blocking I/O
|
|
304
|
+
- Persistent storage (data survives program restarts)
|
|
305
|
+
- Cross-session support (can access threads from different processes)
|
|
306
|
+
- Production-ready
|
|
307
|
+
- Automatic schema management through SQLAlchemy
|
|
308
|
+
- Connection validation at startup with factory pattern
|
|
309
|
+
|
|
310
|
+
#### SQLite storage
|
|
311
|
+
```python
|
|
312
|
+
from narrator import ThreadStore
|
|
313
|
+
|
|
314
|
+
# Use factory pattern for immediate connection validation
|
|
315
|
+
db_url = "sqlite+aiosqlite:///path/to/db.sqlite"
|
|
316
|
+
store = await ThreadStore.create(db_url)
|
|
317
|
+
|
|
318
|
+
# Or use in-memory SQLite database
|
|
319
|
+
store = await ThreadStore.create("sqlite+aiosqlite://") # In-memory SQLite
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### File Storage Configuration
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
from narrator import FileStore
|
|
326
|
+
|
|
327
|
+
# Create a FileStore instance with factory pattern
|
|
328
|
+
file_store = await FileStore.create(
|
|
329
|
+
base_path="/path/to/files", # Optional custom path
|
|
330
|
+
max_file_size=100 * 1024 * 1024, # 100MB (optional)
|
|
331
|
+
max_storage_size=10 * 1024 * 1024 * 1024 # 10GB (optional)
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
# Or use default settings from environment variables
|
|
335
|
+
file_store = await FileStore.create()
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Advanced Usage
|
|
339
|
+
|
|
340
|
+
### Using ThreadStore and FileStore Together
|
|
341
|
+
|
|
342
|
+
```python
|
|
343
|
+
import asyncio
|
|
344
|
+
from narrator import ThreadStore, FileStore, Thread, Message
|
|
345
|
+
|
|
346
|
+
async def main():
|
|
347
|
+
# Create stores
|
|
348
|
+
thread_store = await ThreadStore.create("sqlite+aiosqlite:///main.db")
|
|
349
|
+
file_store = await FileStore.create("/path/to/files")
|
|
350
|
+
|
|
351
|
+
# Create a thread with file attachment
|
|
352
|
+
thread = Thread(title="Document Discussion")
|
|
353
|
+
|
|
354
|
+
# Create a message with an attachment
|
|
355
|
+
message = Message(role="user", content="Here's a document")
|
|
356
|
+
|
|
357
|
+
# Add file content
|
|
358
|
+
pdf_content = b"..." # Your PDF content
|
|
359
|
+
message.add_attachment(pdf_content, filename="document.pdf")
|
|
360
|
+
|
|
361
|
+
thread.add_message(message)
|
|
362
|
+
|
|
363
|
+
# Save thread (attachments are processed automatically)
|
|
364
|
+
await thread_store.save(thread)
|
|
365
|
+
|
|
366
|
+
print(f"Thread saved with ID: {thread.id}")
|
|
367
|
+
|
|
368
|
+
asyncio.run(main())
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Message Attachments
|
|
372
|
+
|
|
373
|
+
Messages can include file attachments that are automatically processed:
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
import asyncio
|
|
377
|
+
from narrator import Thread, Message, Attachment, FileStore
|
|
378
|
+
|
|
379
|
+
async def main():
|
|
380
|
+
file_store = await FileStore.create()
|
|
381
|
+
|
|
382
|
+
# Create a message with an attachment
|
|
383
|
+
message = Message(role="user", content="Here's a document")
|
|
384
|
+
|
|
385
|
+
# Add file content
|
|
386
|
+
pdf_content = b"..." # Your PDF content
|
|
387
|
+
attachment = Attachment(filename="document.pdf", content=pdf_content)
|
|
388
|
+
message.add_attachment(attachment)
|
|
389
|
+
|
|
390
|
+
# Process and store the attachment
|
|
391
|
+
await attachment.process_and_store(file_store)
|
|
392
|
+
|
|
393
|
+
# The attachment now has extracted text and metadata
|
|
394
|
+
print(f"Status: {attachment.status}")
|
|
395
|
+
print(f"File ID: {attachment.file_id}")
|
|
396
|
+
if attachment.attributes:
|
|
397
|
+
print(f"Extracted text: {attachment.attributes.get('text', 'N/A')[:100]}...")
|
|
398
|
+
|
|
399
|
+
asyncio.run(main())
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Platform Integration
|
|
403
|
+
|
|
404
|
+
Threads can be linked to external platforms:
|
|
405
|
+
|
|
406
|
+
```python
|
|
407
|
+
import asyncio
|
|
408
|
+
from narrator import Thread, ThreadStore
|
|
409
|
+
|
|
410
|
+
async def main():
|
|
411
|
+
store = await ThreadStore.create()
|
|
412
|
+
|
|
413
|
+
# Create a thread linked to Slack
|
|
414
|
+
thread = Thread(
|
|
415
|
+
title="Support Ticket #123",
|
|
416
|
+
platforms={
|
|
417
|
+
"slack": {
|
|
418
|
+
"channel": "C1234567",
|
|
419
|
+
"thread_ts": "1234567890.123"
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
await store.save(thread)
|
|
425
|
+
|
|
426
|
+
# Find threads by platform
|
|
427
|
+
slack_threads = await store.find_by_platform("slack", {"channel": "C1234567"})
|
|
428
|
+
print(f"Found {len(slack_threads)} Slack threads in channel")
|
|
429
|
+
|
|
430
|
+
asyncio.run(main())
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## Database CLI
|
|
434
|
+
|
|
435
|
+
The Narrator includes a CLI tool for database management:
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
# Initialize database tables
|
|
439
|
+
uv run narrator init --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
|
|
440
|
+
|
|
441
|
+
# Initialize using environment variable
|
|
442
|
+
export NARRATOR_DATABASE_URL="postgresql+asyncpg://user:pass@localhost/dbname"
|
|
443
|
+
uv run narrator init
|
|
444
|
+
|
|
445
|
+
# Check database status
|
|
446
|
+
uv run narrator status --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
|
|
447
|
+
|
|
448
|
+
# Check status using environment variable
|
|
449
|
+
uv run narrator status
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Available commands:
|
|
453
|
+
- `uv run narrator init` - Initialize database tables
|
|
454
|
+
- `uv run narrator status` - Check database connection and basic statistics
|
|
455
|
+
|
|
456
|
+
## Key Design Principles
|
|
457
|
+
|
|
458
|
+
1. **Factory Pattern**: Use `await ThreadStore.create()` and `await FileStore.create()` for proper initialization and connection validation
|
|
459
|
+
2. **Backend Agnostic**: Same API whether using in-memory, SQLite, or PostgreSQL storage
|
|
460
|
+
3. **Production Ready**: Built-in connection pooling, error handling, and health checks
|
|
461
|
+
4. **Tyler Integration**: Seamlessly integrates with Tyler agents for conversation persistence
|
|
462
|
+
5. **Platform Support**: Native support for external platforms like Slack, Discord, and custom integrations
|
|
463
|
+
|
|
464
|
+
## API Reference
|
|
465
|
+
|
|
466
|
+
### ThreadStore
|
|
467
|
+
|
|
468
|
+
#### Methods
|
|
469
|
+
|
|
470
|
+
- `await ThreadStore.create(database_url=None)`: Factory method to create and initialize a store
|
|
471
|
+
- `await store.save(thread)`: Save a thread to storage
|
|
472
|
+
- `await store.get(thread_id)`: Retrieve a thread by ID
|
|
473
|
+
- `await store.delete(thread_id)`: Delete a thread
|
|
474
|
+
- `await store.list(limit=100, offset=0)`: List threads with pagination
|
|
475
|
+
- `await store.find_by_attributes(attributes)`: Find threads by custom attributes
|
|
476
|
+
- `await store.find_by_platform(platform_name, properties)`: Find threads by platform
|
|
477
|
+
- `await store.list_recent(limit=None)`: List recent threads
|
|
478
|
+
|
|
479
|
+
### FileStore
|
|
480
|
+
|
|
481
|
+
#### Methods
|
|
482
|
+
|
|
483
|
+
- `await FileStore.create(base_path=None, ...)`: Factory method to create and validate a store
|
|
484
|
+
- `await store.save(content, filename, mime_type=None)`: Save file content
|
|
485
|
+
- `await store.get(file_id, storage_path=None)`: Retrieve file content
|
|
486
|
+
- `await store.delete(file_id, storage_path=None)`: Delete a file
|
|
487
|
+
- `await store.get_storage_size()`: Get total storage size
|
|
488
|
+
- `await store.check_health()`: Check storage health
|
|
489
|
+
|
|
490
|
+
### Models
|
|
491
|
+
|
|
492
|
+
#### Thread
|
|
493
|
+
- `id`: Unique thread identifier
|
|
494
|
+
- `title`: Thread title
|
|
495
|
+
- `messages`: List of messages
|
|
496
|
+
- `created_at`: Creation timestamp
|
|
497
|
+
- `updated_at`: Last update timestamp
|
|
498
|
+
- `attributes`: Custom attributes dictionary
|
|
499
|
+
- `platforms`: Platform-specific metadata
|
|
500
|
+
|
|
501
|
+
#### Message
|
|
502
|
+
- `id`: Unique message identifier
|
|
503
|
+
- `role`: Message role (user, assistant, system, tool)
|
|
504
|
+
- `content`: Message content
|
|
505
|
+
- `attachments`: List of file attachments
|
|
506
|
+
- `timestamp`: Message timestamp
|
|
507
|
+
- `metrics`: Performance metrics
|
|
508
|
+
|
|
509
|
+
#### Attachment
|
|
510
|
+
- `filename`: Original filename
|
|
511
|
+
- `mime_type`: File MIME type
|
|
512
|
+
- `file_id`: Storage file ID
|
|
513
|
+
- `storage_path`: Path in storage
|
|
514
|
+
- `status`: Processing status (pending, stored, failed)
|
|
515
|
+
- `attributes`: Processed content and metadata
|
|
516
|
+
|
|
517
|
+
## Development
|
|
518
|
+
|
|
519
|
+
### Running Tests
|
|
520
|
+
|
|
521
|
+
To run the test suite locally:
|
|
522
|
+
|
|
523
|
+
```bash
|
|
524
|
+
# Install development dependencies
|
|
525
|
+
uv sync --extra dev
|
|
526
|
+
|
|
527
|
+
# Run tests with coverage
|
|
528
|
+
uv run pytest tests/ --cov=narrator --cov-report=term-missing --cov-branch --cov-report=term --no-cov-on-fail -v
|
|
529
|
+
|
|
530
|
+
# Run tests without coverage (faster)
|
|
531
|
+
uv run pytest tests/ -v
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Test Requirements
|
|
535
|
+
|
|
536
|
+
The test suite requires:
|
|
537
|
+
- Python 3.13+
|
|
538
|
+
- pytest with async support
|
|
539
|
+
- Test coverage reporting
|
|
540
|
+
|
|
541
|
+
## Contributing
|
|
542
|
+
|
|
543
|
+
1. Fork the repository
|
|
544
|
+
2. Create a feature branch
|
|
545
|
+
3. Make your changes
|
|
546
|
+
4. Add tests
|
|
547
|
+
5. Run the test suite to ensure everything works
|
|
548
|
+
6. Submit a pull request
|
|
549
|
+
|
|
550
|
+
## License
|
|
551
|
+
|
|
552
|
+
MIT License - see LICENSE file for details.
|
|
553
|
+
|
|
554
|
+
## Support
|
|
555
|
+
|
|
556
|
+
For issues and questions:
|
|
557
|
+
- GitHub Issues: [Repository Issues](https://github.com/adamwdraper/the-narrator/issues)
|
|
558
|
+
- Documentation: [API Reference](https://github.com/adamwdraper/the-narrator#api-reference)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
narrator/__init__.py,sha256=Yzh2UFHX_grpGr_RvFqWpUTevr06FyP_R_Z9IHpF9jI,403
|
|
2
|
+
narrator/database/__init__.py,sha256=UngOnFqImCeJiMZlMasm72mC4-UnJDDvfu1MNQLkRA8,189
|
|
3
|
+
narrator/database/cli.py,sha256=QvET17X5kLZ7GiOTw0b80-u4FuI-tOTu4SjAqCBkiSs,8355
|
|
4
|
+
narrator/database/models.py,sha256=OyiufON_ZLNM7PUxXrUoFu1uu62SolHABwpedVnSsfk,2659
|
|
5
|
+
narrator/database/storage_backend.py,sha256=NZ0SKvEL6qKgdJrxuBw5qs6k-9mdWXWE0Ad36epIn2U,27598
|
|
6
|
+
narrator/database/thread_store.py,sha256=vMIPDdwuSpTyPogEUmxGcILxM_r1wxoQBUOn8XJpdqM,11301
|
|
7
|
+
narrator/database/migrations/__init__.py,sha256=IqoSL8eCcbcOtn96u2_TTrNG0KN1Jn1yreDZEO4RsnM,173
|
|
8
|
+
narrator/models/__init__.py,sha256=J8Rsv2lmfGR5QmUjoAPEFTSQt5TGtyrBynnp17HdZnU,179
|
|
9
|
+
narrator/models/attachment.py,sha256=_DIe2F5Vd75UtKmWO7x9d69dzAxUWCOXotvzR-nGVuU,17940
|
|
10
|
+
narrator/models/message.py,sha256=QVItUV75subrmVoEWFy_25khHP7fwbVaKZI8Rh5IPTI,21629
|
|
11
|
+
narrator/models/thread.py,sha256=MMZfJPMMw1zdD_1Jp4NqEAvnTVpHjaFSY82eV4921PQ,19326
|
|
12
|
+
narrator/storage/__init__.py,sha256=K4cxGITSQoQiw32QOWZsCBm11fwDTbsyzHGeAqcL6yY,101
|
|
13
|
+
narrator/storage/file_store.py,sha256=H2F08e_DYjO-L782s1DgSctJ_2x-7EjrTFxLxV3oWuk,20062
|
|
14
|
+
narrator/utils/__init__.py,sha256=P4BhLvBJbBvb8qha2tTZPlYbjCRXth_K97f4vNc77UI,109
|
|
15
|
+
narrator/utils/logging.py,sha256=Hm6D4VX03e28UCkNS1pCOXnYQKHQ2nz_PvZX8h-wLgg,1807
|
|
16
|
+
slide_narrator-5.5.0.dist-info/METADATA,sha256=tcd_3zqNCGJ6EIgmcZVnvyRI3ROKzn7fcKFuOw-2N9E,16494
|
|
17
|
+
slide_narrator-5.5.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
18
|
+
slide_narrator-5.5.0.dist-info/entry_points.txt,sha256=5Oa53AERvPVdrEvsdWbY85xfzAGayOqq_P4KEmf1khA,56
|
|
19
|
+
slide_narrator-5.5.0.dist-info/licenses/LICENSE,sha256=g6cGasroU9sqSOjThWg14w0BMlwZhgmOQQVTiu036ks,1068
|
|
20
|
+
slide_narrator-5.5.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Adam Draper
|
|
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.
|