slide-narrator 0.4.0__tar.gz → 1.0.1__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.

Potentially problematic release.


This version of slide-narrator might be problematic. Click here for more details.

Files changed (21) hide show
  1. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/PKG-INFO +65 -12
  2. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/README.md +64 -11
  3. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/__init__.py +1 -1
  4. slide_narrator-1.0.1/narrator/database/cli.py +222 -0
  5. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/pyproject.toml +2 -2
  6. slide_narrator-0.4.0/narrator/database/cli.py +0 -66
  7. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/.gitignore +0 -0
  8. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/LICENSE +0 -0
  9. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/database/__init__.py +0 -0
  10. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/database/migrations/__init__.py +0 -0
  11. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/database/models.py +0 -0
  12. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/database/storage_backend.py +0 -0
  13. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/database/thread_store.py +0 -0
  14. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/models/__init__.py +0 -0
  15. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/models/attachment.py +0 -0
  16. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/models/message.py +0 -0
  17. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/models/thread.py +0 -0
  18. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/storage/__init__.py +0 -0
  19. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/storage/file_store.py +0 -0
  20. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/utils/__init__.py +0 -0
  21. {slide_narrator-0.4.0 → slide_narrator-1.0.1}/narrator/utils/logging.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slide-narrator
3
- Version: 0.4.0
3
+ Version: 1.0.1
4
4
  Summary: Thread and file storage components for conversational AI - the companion to Tyler AI framework
5
5
  Project-URL: Homepage, https://github.com/adamwdraper/slide
6
6
  Project-URL: Repository, https://github.com/adamwdraper/slide
@@ -74,19 +74,72 @@ pip install slide-narrator
74
74
 
75
75
  ## Setup
76
76
 
77
+ ### Docker Setup (Recommended for Development)
78
+
79
+ For local development with PostgreSQL, Narrator includes built-in Docker commands:
80
+
81
+ ```bash
82
+ # One-command setup: starts PostgreSQL and initializes tables
83
+ uv run narrator docker-setup
84
+
85
+ # This will:
86
+ # 1. Start a PostgreSQL container
87
+ # 2. Wait for it to be ready
88
+ # 3. Initialize the database tables
89
+ # 4. Show you the connection string
90
+
91
+ # The database will be available at:
92
+ # postgresql+asyncpg://narrator:narrator_dev@localhost:5432/narrator
93
+ ```
94
+
95
+ To manage the Docker container:
96
+
97
+ ```bash
98
+ # Stop container (preserves data)
99
+ uv run narrator docker-stop
100
+
101
+ # Stop and remove all data
102
+ uv run narrator docker-stop --remove-volumes
103
+
104
+ # Start container again
105
+ uv run narrator docker-start
106
+
107
+ # Check database status
108
+ uv run narrator status
109
+ ```
110
+
111
+ For custom configurations, the Docker commands respect environment variables:
112
+
113
+ ```bash
114
+ # Use a different port
115
+ uv run narrator docker-setup --port 5433
116
+
117
+ # Or set environment variables (matching docker-compose.yml)
118
+ export NARRATOR_DB_NAME=mydb
119
+ export NARRATOR_DB_USER=myuser
120
+ export NARRATOR_DB_PASSWORD=mypassword
121
+ export NARRATOR_DB_PORT=5433
122
+
123
+ # Then run docker-setup
124
+ uv run narrator docker-setup
125
+
126
+ # This will create:
127
+ # postgresql+asyncpg://myuser:mypassword@localhost:5433/mydb
128
+ ```
129
+
77
130
  ### Database Setup
78
131
 
79
132
  For production use with PostgreSQL or SQLite persistence, you'll need to initialize the database tables:
80
133
 
81
134
  ```bash
82
135
  # Initialize database tables (PostgreSQL)
83
- narrator-db init --database-url "postgresql+asyncpg://user:password@localhost/dbname"
136
+ uv run narrator init --database-url "postgresql+asyncpg://user:password@localhost/dbname"
84
137
 
85
138
  # Initialize database tables (SQLite)
86
- narrator-db init --database-url "sqlite+aiosqlite:///path/to/your/database.db"
139
+ uv run narrator init --database-url "sqlite+aiosqlite:///path/to/your/database.db"
87
140
 
88
141
  # Check database status
89
- narrator-db status --database-url "postgresql+asyncpg://user:password@localhost/dbname"
142
+ uv run narrator status --database-url "postgresql+asyncpg://user:password@localhost/dbname"
90
143
  ```
91
144
 
92
145
  You can also use environment variables instead of passing the database URL:
@@ -96,8 +149,8 @@ You can also use environment variables instead of passing the database URL:
96
149
  export NARRATOR_DATABASE_URL="postgresql+asyncpg://user:password@localhost/dbname"
97
150
 
98
151
  # Then run without --database-url flag
99
- narrator-db init
100
- narrator-db status
152
+ uv run narrator init
153
+ uv run narrator status
101
154
  ```
102
155
 
103
156
  ### Environment Variables
@@ -382,22 +435,22 @@ The Narrator includes a CLI tool for database management:
382
435
 
383
436
  ```bash
384
437
  # Initialize database tables
385
- narrator-db init --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
438
+ uv run narrator init --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
386
439
 
387
440
  # Initialize using environment variable
388
441
  export NARRATOR_DATABASE_URL="postgresql+asyncpg://user:pass@localhost/dbname"
389
- narrator-db init
442
+ uv run narrator init
390
443
 
391
444
  # Check database status
392
- narrator-db status --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
445
+ uv run narrator status --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
393
446
 
394
447
  # Check status using environment variable
395
- narrator-db status
448
+ uv run narrator status
396
449
  ```
397
450
 
398
451
  Available commands:
399
- - `narrator-db init` - Initialize database tables
400
- - `narrator-db status` - Check database connection and basic statistics
452
+ - `uv run narrator init` - Initialize database tables
453
+ - `uv run narrator status` - Check database connection and basic statistics
401
454
 
402
455
  ## Key Design Principles
403
456
 
@@ -39,19 +39,72 @@ pip install slide-narrator
39
39
 
40
40
  ## Setup
41
41
 
42
+ ### Docker Setup (Recommended for Development)
43
+
44
+ For local development with PostgreSQL, Narrator includes built-in Docker commands:
45
+
46
+ ```bash
47
+ # One-command setup: starts PostgreSQL and initializes tables
48
+ uv run narrator docker-setup
49
+
50
+ # This will:
51
+ # 1. Start a PostgreSQL container
52
+ # 2. Wait for it to be ready
53
+ # 3. Initialize the database tables
54
+ # 4. Show you the connection string
55
+
56
+ # The database will be available at:
57
+ # postgresql+asyncpg://narrator:narrator_dev@localhost:5432/narrator
58
+ ```
59
+
60
+ To manage the Docker container:
61
+
62
+ ```bash
63
+ # Stop container (preserves data)
64
+ uv run narrator docker-stop
65
+
66
+ # Stop and remove all data
67
+ uv run narrator docker-stop --remove-volumes
68
+
69
+ # Start container again
70
+ uv run narrator docker-start
71
+
72
+ # Check database status
73
+ uv run narrator status
74
+ ```
75
+
76
+ For custom configurations, the Docker commands respect environment variables:
77
+
78
+ ```bash
79
+ # Use a different port
80
+ uv run narrator docker-setup --port 5433
81
+
82
+ # Or set environment variables (matching docker-compose.yml)
83
+ export NARRATOR_DB_NAME=mydb
84
+ export NARRATOR_DB_USER=myuser
85
+ export NARRATOR_DB_PASSWORD=mypassword
86
+ export NARRATOR_DB_PORT=5433
87
+
88
+ # Then run docker-setup
89
+ uv run narrator docker-setup
90
+
91
+ # This will create:
92
+ # postgresql+asyncpg://myuser:mypassword@localhost:5433/mydb
93
+ ```
94
+
42
95
  ### Database Setup
43
96
 
44
97
  For production use with PostgreSQL or SQLite persistence, you'll need to initialize the database tables:
45
98
 
46
99
  ```bash
47
100
  # Initialize database tables (PostgreSQL)
48
- narrator-db init --database-url "postgresql+asyncpg://user:password@localhost/dbname"
101
+ uv run narrator init --database-url "postgresql+asyncpg://user:password@localhost/dbname"
49
102
 
50
103
  # Initialize database tables (SQLite)
51
- narrator-db init --database-url "sqlite+aiosqlite:///path/to/your/database.db"
104
+ uv run narrator init --database-url "sqlite+aiosqlite:///path/to/your/database.db"
52
105
 
53
106
  # Check database status
54
- narrator-db status --database-url "postgresql+asyncpg://user:password@localhost/dbname"
107
+ uv run narrator status --database-url "postgresql+asyncpg://user:password@localhost/dbname"
55
108
  ```
56
109
 
57
110
  You can also use environment variables instead of passing the database URL:
@@ -61,8 +114,8 @@ You can also use environment variables instead of passing the database URL:
61
114
  export NARRATOR_DATABASE_URL="postgresql+asyncpg://user:password@localhost/dbname"
62
115
 
63
116
  # Then run without --database-url flag
64
- narrator-db init
65
- narrator-db status
117
+ uv run narrator init
118
+ uv run narrator status
66
119
  ```
67
120
 
68
121
  ### Environment Variables
@@ -347,22 +400,22 @@ The Narrator includes a CLI tool for database management:
347
400
 
348
401
  ```bash
349
402
  # Initialize database tables
350
- narrator-db init --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
403
+ uv run narrator init --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
351
404
 
352
405
  # Initialize using environment variable
353
406
  export NARRATOR_DATABASE_URL="postgresql+asyncpg://user:pass@localhost/dbname"
354
- narrator-db init
407
+ uv run narrator init
355
408
 
356
409
  # Check database status
357
- narrator-db status --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
410
+ uv run narrator status --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
358
411
 
359
412
  # Check status using environment variable
360
- narrator-db status
413
+ uv run narrator status
361
414
  ```
362
415
 
363
416
  Available commands:
364
- - `narrator-db init` - Initialize database tables
365
- - `narrator-db status` - Check database connection and basic statistics
417
+ - `uv run narrator init` - Initialize database tables
418
+ - `uv run narrator status` - Check database connection and basic statistics
366
419
 
367
420
  ## Key Design Principles
368
421
 
@@ -8,7 +8,7 @@ from .models.thread import Thread
8
8
  from .models.message import Message
9
9
  from .models.attachment import Attachment
10
10
 
11
- __version__ = "0.4.0"
11
+ __version__ = "1.0.1"
12
12
  __all__ = [
13
13
  "ThreadStore",
14
14
  "FileStore",
@@ -0,0 +1,222 @@
1
+ """Database CLI for Tyler Stores"""
2
+ import asyncio
3
+ import os
4
+ import click
5
+ import functools
6
+ import subprocess
7
+ import tempfile
8
+ import time
9
+ from pathlib import Path
10
+ from .thread_store import ThreadStore
11
+ from ..utils.logging import get_logger
12
+
13
+ logger = get_logger(__name__)
14
+
15
+ @click.group()
16
+ def main():
17
+ """Narrator CLI - Database management commands"""
18
+ pass
19
+
20
+ @main.command()
21
+ @click.option('--database-url', help='Database URL for initialization')
22
+ def init(database_url):
23
+ """Initialize database tables"""
24
+ async def _init():
25
+ try:
26
+ # Use provided URL or check environment variable
27
+ url = database_url or os.environ.get('NARRATOR_DATABASE_URL')
28
+
29
+ if url:
30
+ store = await ThreadStore.create(url)
31
+ else:
32
+ # Use in-memory storage
33
+ store = await ThreadStore.create()
34
+
35
+ logger.info("Database initialized successfully")
36
+ click.echo("Database initialized successfully")
37
+ except Exception as e:
38
+ logger.error(f"Failed to initialize database: {e}")
39
+ click.echo(f"Error: Failed to initialize database: {e}")
40
+ raise click.Abort()
41
+
42
+ asyncio.run(_init())
43
+
44
+ @main.command()
45
+ @click.option('--database-url', help='Database URL')
46
+ def status(database_url):
47
+ """Check database status"""
48
+ async def _status():
49
+ try:
50
+ # Use provided URL or check environment variable
51
+ url = database_url or os.environ.get('NARRATOR_DATABASE_URL')
52
+
53
+ if url:
54
+ store = await ThreadStore.create(url)
55
+ else:
56
+ store = await ThreadStore.create()
57
+
58
+ # Get some basic stats
59
+ threads = await store.list_recent(limit=5)
60
+ click.echo(f"Database connection: OK")
61
+ click.echo(f"Recent threads count: {len(threads)}")
62
+
63
+ except Exception as e:
64
+ logger.error(f"Database status check failed: {e}")
65
+ click.echo(f"Error: Database status check failed: {e}")
66
+ raise click.Abort()
67
+
68
+ asyncio.run(_status())
69
+
70
+ @main.command()
71
+ @click.option('--port', help='Port to expose PostgreSQL on (default: 5432 or NARRATOR_DB_PORT)')
72
+ @click.option('--detach/--no-detach', default=True, help='Run container in background (default: True)')
73
+ def docker_start(port, detach):
74
+ """Start a PostgreSQL container for Narrator"""
75
+ # Use environment variables with defaults matching docker-compose.yml
76
+ db_name = os.environ.get('NARRATOR_DB_NAME', 'narrator')
77
+ db_user = os.environ.get('NARRATOR_DB_USER', 'narrator')
78
+ db_password = os.environ.get('NARRATOR_DB_PASSWORD', 'narrator_dev')
79
+ db_port = port or os.environ.get('NARRATOR_DB_PORT', '5432')
80
+
81
+ docker_compose_content = f"""services:
82
+ postgres:
83
+ image: postgres:16
84
+ container_name: narrator-postgres
85
+ environment:
86
+ POSTGRES_DB: {db_name}
87
+ POSTGRES_USER: {db_user}
88
+ POSTGRES_PASSWORD: {db_password}
89
+ ports:
90
+ - "{db_port}:5432"
91
+ volumes:
92
+ - narrator_postgres_data:/var/lib/postgresql/data
93
+ healthcheck:
94
+ test: ["CMD-SHELL", "pg_isready -U {db_user}"]
95
+ interval: 5s
96
+ timeout: 5s
97
+ retries: 5
98
+
99
+ volumes:
100
+ narrator_postgres_data:
101
+ """
102
+
103
+ # Create a temporary directory for docker-compose.yml
104
+ with tempfile.TemporaryDirectory() as tmpdir:
105
+ compose_file = Path(tmpdir) / "docker-compose.yml"
106
+ compose_file.write_text(docker_compose_content)
107
+
108
+ # Check if docker is available
109
+ try:
110
+ subprocess.run(["docker", "--version"], capture_output=True, check=True)
111
+ except (subprocess.CalledProcessError, FileNotFoundError):
112
+ click.echo("❌ Docker is not installed or not available in PATH")
113
+ raise click.Abort()
114
+
115
+ # Check if docker-compose or docker compose is available
116
+ compose_cmd = None
117
+ try:
118
+ subprocess.run(["docker", "compose", "version"], capture_output=True, check=True)
119
+ compose_cmd = ["docker", "compose"]
120
+ except (subprocess.CalledProcessError, FileNotFoundError):
121
+ try:
122
+ subprocess.run(["docker-compose", "version"], capture_output=True, check=True)
123
+ compose_cmd = ["docker-compose"]
124
+ except (subprocess.CalledProcessError, FileNotFoundError):
125
+ click.echo("❌ Docker Compose is not installed")
126
+ raise click.Abort()
127
+
128
+ # Start the container
129
+ click.echo("📦 Starting PostgreSQL container...")
130
+ cmd = compose_cmd + ["up"]
131
+ if detach:
132
+ cmd.append("-d")
133
+
134
+ result = subprocess.run(cmd, cwd=tmpdir)
135
+
136
+ if result.returncode != 0:
137
+ click.echo("❌ Failed to start PostgreSQL container")
138
+ raise click.Abort()
139
+
140
+ if detach:
141
+ # Wait for PostgreSQL to be ready
142
+ click.echo("⏳ Waiting for PostgreSQL to be ready...")
143
+ for i in range(30):
144
+ result = subprocess.run(
145
+ ["docker", "exec", "narrator-postgres", "pg_isready", "-U", db_user],
146
+ capture_output=True
147
+ )
148
+ if result.returncode == 0:
149
+ click.echo("✅ PostgreSQL is ready!")
150
+ click.echo(f"\n🎉 Database available at:")
151
+ click.echo(f" postgresql+asyncpg://{db_user}:{db_password}@localhost:{db_port}/{db_name}")
152
+ return
153
+ time.sleep(1)
154
+
155
+ click.echo("❌ PostgreSQL failed to start after 30 seconds")
156
+ raise click.Abort()
157
+
158
+ @main.command()
159
+ @click.option('--remove-volumes', is_flag=True, help='Remove data volumes (destroys all data)')
160
+ def docker_stop(remove_volumes):
161
+ """Stop the PostgreSQL container"""
162
+ # Check if docker is available
163
+ try:
164
+ subprocess.run(["docker", "--version"], capture_output=True, check=True)
165
+ except (subprocess.CalledProcessError, FileNotFoundError):
166
+ click.echo("❌ Docker is not installed or not available in PATH")
167
+ raise click.Abort()
168
+
169
+ # Check if container exists
170
+ result = subprocess.run(
171
+ ["docker", "ps", "-a", "--format", "{{.Names}}"],
172
+ capture_output=True,
173
+ text=True
174
+ )
175
+
176
+ if "narrator-postgres" not in result.stdout:
177
+ click.echo("ℹ️ No Narrator PostgreSQL container found")
178
+ return
179
+
180
+ click.echo("🛑 Stopping PostgreSQL container...")
181
+
182
+ # Stop the container
183
+ subprocess.run(["docker", "stop", "narrator-postgres"], check=False)
184
+ subprocess.run(["docker", "rm", "narrator-postgres"], check=False)
185
+
186
+ if remove_volumes:
187
+ click.echo("🗑️ Removing data volume...")
188
+ subprocess.run(["docker", "volume", "rm", "narrator_postgres_data"], check=False)
189
+ click.echo("✅ Container and data removed")
190
+ else:
191
+ click.echo("✅ Container stopped (data preserved)")
192
+
193
+ @main.command()
194
+ @click.option('--port', help='Port to expose PostgreSQL on (default: 5432 or NARRATOR_DB_PORT)')
195
+ def docker_setup(port):
196
+ """One-command Docker setup: start PostgreSQL and initialize tables"""
197
+ # Start PostgreSQL
198
+ ctx = click.get_current_context()
199
+ ctx.invoke(docker_start, port=port, detach=True)
200
+
201
+ # Get database configuration from environment or defaults
202
+ db_name = os.environ.get('NARRATOR_DB_NAME', 'narrator')
203
+ db_user = os.environ.get('NARRATOR_DB_USER', 'narrator')
204
+ db_password = os.environ.get('NARRATOR_DB_PASSWORD', 'narrator_dev')
205
+ db_port = port or os.environ.get('NARRATOR_DB_PORT', '5432')
206
+
207
+ # Set up database URL
208
+ database_url = f"postgresql+asyncpg://{db_user}:{db_password}@localhost:{db_port}/{db_name}"
209
+ os.environ['NARRATOR_DATABASE_URL'] = database_url
210
+
211
+ # Initialize tables
212
+ click.echo("\n🔧 Initializing database tables...")
213
+ ctx.invoke(init, database_url=database_url)
214
+
215
+ click.echo("\n🎉 Setup complete! Your database is ready.")
216
+ click.echo("\nTo use in your code:")
217
+ click.echo(f'export NARRATOR_DATABASE_URL="{database_url}"')
218
+ click.echo("\nTo stop the container: narrator docker-stop")
219
+ click.echo("To remove all data: narrator docker-stop --remove-volumes")
220
+
221
+ if __name__ == '__main__':
222
+ main()
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "slide-narrator"
7
- version = "0.4.0"
7
+ version = "1.0.1"
8
8
  description = "Thread and file storage components for conversational AI - the companion to Tyler AI framework"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -48,7 +48,7 @@ Repository = "https://github.com/adamwdraper/slide"
48
48
  "Bug Tracker" = "https://github.com/adamwdraper/slide/issues"
49
49
 
50
50
  [project.scripts]
51
- narrator-db = "narrator.database.cli:main"
51
+ narrator = "narrator.database.cli:main"
52
52
 
53
53
  [tool.uv.sources]
54
54
  # No workspace dependencies for narrator currently
@@ -1,66 +0,0 @@
1
- """Database CLI for Tyler Stores"""
2
- import asyncio
3
- import click
4
- from .thread_store import ThreadStore
5
- from ..utils.logging import get_logger
6
-
7
- logger = get_logger(__name__)
8
-
9
- @click.group()
10
- def main():
11
- """Tyler Stores Database CLI"""
12
- pass
13
-
14
- @click.command()
15
- @click.option('--database-url', help='Database URL for initialization')
16
- async def init(database_url):
17
- """Initialize database tables"""
18
- try:
19
- if database_url:
20
- store = await ThreadStore.create(database_url)
21
- else:
22
- # Use environment variables or default
23
- store = await ThreadStore.create()
24
-
25
- logger.info("Database initialized successfully")
26
- click.echo("Database initialized successfully")
27
- except Exception as e:
28
- logger.error(f"Failed to initialize database: {e}")
29
- click.echo(f"Error: Failed to initialize database: {e}")
30
- raise click.Abort()
31
-
32
- @click.command()
33
- @click.option('--database-url', help='Database URL')
34
- async def status(database_url):
35
- """Check database status"""
36
- try:
37
- if database_url:
38
- store = await ThreadStore.create(database_url)
39
- else:
40
- store = await ThreadStore.create()
41
-
42
- # Get some basic stats
43
- threads = await store.list_recent(limit=5)
44
- click.echo(f"Database connection: OK")
45
- click.echo(f"Recent threads count: {len(threads)}")
46
-
47
- except Exception as e:
48
- logger.error(f"Database status check failed: {e}")
49
- click.echo(f"Error: Database status check failed: {e}")
50
- raise click.Abort()
51
-
52
- # Add async wrapper for commands
53
- def async_command(f):
54
- def wrapper(*args, **kwargs):
55
- return asyncio.run(f(*args, **kwargs))
56
- return wrapper
57
-
58
- # Apply async wrapper to commands
59
- init = click.command()(async_command(init))
60
- status = click.command()(async_command(status))
61
-
62
- main.add_command(init)
63
- main.add_command(status)
64
-
65
- if __name__ == '__main__':
66
- main()
File without changes