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.
- narrator/__init__.py +18 -0
- narrator/database/__init__.py +8 -0
- narrator/database/cli.py +66 -0
- narrator/database/migrations/__init__.py +6 -0
- narrator/database/models.py +69 -0
- narrator/database/storage_backend.py +580 -0
- narrator/database/thread_store.py +280 -0
- narrator/models/__init__.py +9 -0
- narrator/models/attachment.py +363 -0
- narrator/models/message.py +507 -0
- narrator/models/thread.py +469 -0
- narrator/storage/__init__.py +7 -0
- narrator/storage/file_store.py +535 -0
- narrator/utils/__init__.py +9 -0
- narrator/utils/logging.py +58 -0
- slide_narrator-0.2.1.dist-info/METADATA +531 -0
- slide_narrator-0.2.1.dist-info/RECORD +20 -0
- slide_narrator-0.2.1.dist-info/WHEEL +4 -0
- slide_narrator-0.2.1.dist-info/entry_points.txt +2 -0
- slide_narrator-0.2.1.dist-info/licenses/LICENSE +21 -0
narrator/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The Narrator - Thread and file storage components for conversational AI
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .database.thread_store import ThreadStore
|
|
6
|
+
from .storage.file_store import FileStore
|
|
7
|
+
from .models.thread import Thread
|
|
8
|
+
from .models.message import Message
|
|
9
|
+
from .models.attachment import Attachment
|
|
10
|
+
|
|
11
|
+
__version__ = "0.2.1"
|
|
12
|
+
__all__ = [
|
|
13
|
+
"ThreadStore",
|
|
14
|
+
"FileStore",
|
|
15
|
+
"Thread",
|
|
16
|
+
"Message",
|
|
17
|
+
"Attachment",
|
|
18
|
+
]
|
narrator/database/cli.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
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()
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from sqlalchemy.types import TypeDecorator, TEXT, JSON
|
|
3
|
+
|
|
4
|
+
"""Database models for SQLAlchemy"""
|
|
5
|
+
from sqlalchemy import Column, String, DateTime, Text, ForeignKey, Integer
|
|
6
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
|
7
|
+
from sqlalchemy.orm import declarative_base
|
|
8
|
+
from sqlalchemy.orm import relationship
|
|
9
|
+
from datetime import datetime, UTC
|
|
10
|
+
|
|
11
|
+
class JSONBCompat(TypeDecorator):
|
|
12
|
+
impl = TEXT
|
|
13
|
+
cache_ok = True
|
|
14
|
+
|
|
15
|
+
def load_dialect_impl(self, dialect):
|
|
16
|
+
if dialect.name == 'postgresql':
|
|
17
|
+
return dialect.type_descriptor(JSONB())
|
|
18
|
+
else:
|
|
19
|
+
return dialect.type_descriptor(JSON())
|
|
20
|
+
|
|
21
|
+
def process_bind_param(self, value, dialect):
|
|
22
|
+
if dialect.name == 'postgresql':
|
|
23
|
+
return value
|
|
24
|
+
if value is not None:
|
|
25
|
+
return value
|
|
26
|
+
return value
|
|
27
|
+
|
|
28
|
+
def process_result_value(self, value, dialect):
|
|
29
|
+
if dialect.name == 'postgresql':
|
|
30
|
+
return value
|
|
31
|
+
if value is not None:
|
|
32
|
+
return value
|
|
33
|
+
return value
|
|
34
|
+
|
|
35
|
+
Base = declarative_base()
|
|
36
|
+
|
|
37
|
+
class ThreadRecord(Base):
|
|
38
|
+
__tablename__ = 'threads'
|
|
39
|
+
|
|
40
|
+
id = Column(String, primary_key=True)
|
|
41
|
+
title = Column(String, nullable=True)
|
|
42
|
+
attributes = Column(JSONBCompat, nullable=False, default={})
|
|
43
|
+
platforms = Column(JSONBCompat, nullable=True)
|
|
44
|
+
created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(UTC))
|
|
45
|
+
updated_at = Column(DateTime(timezone=True), default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC))
|
|
46
|
+
|
|
47
|
+
messages = relationship("MessageRecord", back_populates="thread", cascade="all, delete-orphan")
|
|
48
|
+
|
|
49
|
+
class MessageRecord(Base):
|
|
50
|
+
__tablename__ = 'messages'
|
|
51
|
+
|
|
52
|
+
id = Column(String, primary_key=True)
|
|
53
|
+
thread_id = Column(String, ForeignKey('threads.id', ondelete='CASCADE'), nullable=False)
|
|
54
|
+
sequence = Column(Integer, nullable=False)
|
|
55
|
+
turn = Column(Integer, nullable=True)
|
|
56
|
+
role = Column(String, nullable=False)
|
|
57
|
+
content = Column(Text, nullable=True)
|
|
58
|
+
name = Column(String, nullable=True)
|
|
59
|
+
tool_call_id = Column(String, nullable=True)
|
|
60
|
+
tool_calls = Column(JSONBCompat, nullable=True)
|
|
61
|
+
attributes = Column(JSONBCompat, nullable=False, default={})
|
|
62
|
+
timestamp = Column(DateTime(timezone=True), default=lambda: datetime.now(UTC))
|
|
63
|
+
source = Column(JSONBCompat, nullable=True)
|
|
64
|
+
platforms = Column(JSONBCompat, nullable=True)
|
|
65
|
+
attachments = Column(JSONBCompat, nullable=True)
|
|
66
|
+
metrics = Column(JSONBCompat, nullable=False, default={})
|
|
67
|
+
reactions = Column(JSONBCompat, nullable=True)
|
|
68
|
+
|
|
69
|
+
thread = relationship("ThreadRecord", back_populates="messages")
|