slide-narrator 0.2.1__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 slide-narrator might be problematic. Click here for more details.

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