slide-narrator 1.0.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.
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/PKG-INFO +65 -12
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/README.md +64 -11
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/__init__.py +1 -1
- slide_narrator-1.0.1/narrator/database/cli.py +222 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/pyproject.toml +2 -2
- slide_narrator-1.0.0/narrator/database/cli.py +0 -66
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/.gitignore +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/LICENSE +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/database/__init__.py +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/database/migrations/__init__.py +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/database/models.py +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/database/storage_backend.py +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/database/thread_store.py +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/models/__init__.py +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/models/attachment.py +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/models/message.py +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/models/thread.py +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/storage/__init__.py +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/storage/file_store.py +0 -0
- {slide_narrator-1.0.0 → slide_narrator-1.0.1}/narrator/utils/__init__.py +0 -0
- {slide_narrator-1.0.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: 1.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
|
|
136
|
+
uv run narrator init --database-url "postgresql+asyncpg://user:password@localhost/dbname"
|
|
84
137
|
|
|
85
138
|
# Initialize database tables (SQLite)
|
|
86
|
-
narrator
|
|
139
|
+
uv run narrator init --database-url "sqlite+aiosqlite:///path/to/your/database.db"
|
|
87
140
|
|
|
88
141
|
# Check database status
|
|
89
|
-
narrator
|
|
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
|
|
100
|
-
narrator
|
|
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
|
|
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
|
|
442
|
+
uv run narrator init
|
|
390
443
|
|
|
391
444
|
# Check database status
|
|
392
|
-
narrator
|
|
445
|
+
uv run narrator status --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
|
|
393
446
|
|
|
394
447
|
# Check status using environment variable
|
|
395
|
-
narrator
|
|
448
|
+
uv run narrator status
|
|
396
449
|
```
|
|
397
450
|
|
|
398
451
|
Available commands:
|
|
399
|
-
- `narrator
|
|
400
|
-
- `narrator
|
|
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
|
|
101
|
+
uv run narrator init --database-url "postgresql+asyncpg://user:password@localhost/dbname"
|
|
49
102
|
|
|
50
103
|
# Initialize database tables (SQLite)
|
|
51
|
-
narrator
|
|
104
|
+
uv run narrator init --database-url "sqlite+aiosqlite:///path/to/your/database.db"
|
|
52
105
|
|
|
53
106
|
# Check database status
|
|
54
|
-
narrator
|
|
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
|
|
65
|
-
narrator
|
|
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
|
|
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
|
|
407
|
+
uv run narrator init
|
|
355
408
|
|
|
356
409
|
# Check database status
|
|
357
|
-
narrator
|
|
410
|
+
uv run narrator status --database-url "postgresql+asyncpg://user:pass@localhost/dbname"
|
|
358
411
|
|
|
359
412
|
# Check status using environment variable
|
|
360
|
-
narrator
|
|
413
|
+
uv run narrator status
|
|
361
414
|
```
|
|
362
415
|
|
|
363
416
|
Available commands:
|
|
364
|
-
- `narrator
|
|
365
|
-
- `narrator
|
|
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
|
|
|
@@ -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 = "1.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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|