diff --git a/migrations/utils.py b/migrations/utils.py new file mode 100644 index 00000000..cc18fc20 --- /dev/null +++ b/migrations/utils.py @@ -0,0 +1,32 @@ +from os import getenv +from typing import Optional + +from alembic import op +import sqlalchemy as sa + +def get_schema() -> str: + return getenv("DATABASE_SCHEMA", "public") + +def column_exists(table_name: str, column_name: str, inspector: Optional[sa.Inspector] = None) -> bool: + """Check if a column exists in a table.""" + if inspector is None: + inspector = sa.inspect(op.get_bind()) + schema = get_schema() + existing_columns = [col["name"] for col in inspector.get_columns(table_name, schema=schema)] + return column_name in existing_columns + +def fk_exists(table_name: str, fk_name: str, inspector: Optional[sa.Inspector] = None) -> bool: + """Check if a foreign key exists in a table.""" + if inspector is None: + inspector = sa.inspect(op.get_bind()) + schema = get_schema() + foreign_keys = inspector.get_foreign_keys(table_name, schema=schema) + return any(fk.get("name") == fk_name for fk in foreign_keys) + +def index_exists(table_name: str, index_name: str, inspector: Optional[sa.Inspector] = None) -> bool: + """Check if an index exists in a table.""" + if inspector is None: + inspector = sa.inspect(op.get_bind()) + schema = get_schema() + indexes = inspector.get_indexes(table_name, schema=schema) + return any(idx["name"] == index_name for idx in indexes) \ No newline at end of file diff --git a/migrations/versions/556a16564f50_add_user_id_and_app_id_to_tables.py b/migrations/versions/556a16564f50_add_user_id_and_app_id_to_tables.py new file mode 100644 index 00000000..ba59b34e --- /dev/null +++ b/migrations/versions/556a16564f50_add_user_id_and_app_id_to_tables.py @@ -0,0 +1,493 @@ +"""add user id and app id to tables + +Revision ID: 556a16564f50 +Revises: b765d82110bd +Create Date: 2025-05-13 17:10:33.805495 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.exc import IntegrityError, ProgrammingError + +from migrations.utils import ( + get_schema, + column_exists, + fk_exists, + index_exists, +) + +# revision identifiers, used by Alembic. +revision: str = '556a16564f50' +down_revision: Union[str, None] = 'b765d82110bd' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + schema = get_schema() + conn = op.get_bind() + inspector = sa.inspect(conn) + + # 1. SESSIONS TABLE + if not column_exists("sessions", "app_id", inspector): + op.add_column("sessions", sa.Column("app_id", sa.Text(), nullable=True), schema=schema) + print("Added app_id column to sessions table") + else: + print("app_id column already exists in sessions table") + + # Create foreign key constraint for app_id + if not fk_exists("sessions", "sessions_app_id_fkey", inspector): + try: + op.create_foreign_key( + "sessions_app_id_fkey", + "sessions", + "apps", + ["app_id"], + ["public_id"], + source_schema=schema, + referent_schema=schema + ) + print("Created app_id foreign key for sessions table") + except IntegrityError: + print("Cannot create app_id foreign key for sessions table - integrity error") + else: + print("app_id foreign key already exists for sessions table") + + # Data migration: Fill app_id from users table + try: + op.execute(""" + UPDATE sessions s + SET app_id = u.app_id + FROM users u + WHERE s.user_id = u.public_id + AND (s.app_id IS NULL OR s.app_id <> u.app_id) + """) + print("Updated app_id values from users for sessions table") + except Exception as e: + print(f"Error updating app_id values for sessions table: {e}") + + # Make app_id non-nullable + try: + op.alter_column("sessions", "app_id", nullable=False, schema=schema) + print("Made app_id non-nullable for sessions table") + except (ProgrammingError, IntegrityError) as e: + print(f"Error making app_id non-nullable for sessions table: {e}") + + # Create index for sessions.app_id + if not index_exists("sessions", "ix_sessions_app_id"): + op.create_index(op.f("ix_sessions_app_id"), "sessions", ["app_id"], unique=False, schema=schema) + print("Created index ix_sessions_app_id") + else: + print("Index ix_sessions_app_id already exists") + + # 2. MESSAGES TABLE + if not column_exists("messages", "app_id", inspector): + op.add_column("messages", sa.Column("app_id", sa.Text()), schema=schema) + print("Added app_id column to messages table") + else: + print("app_id column already exists in messages table") + + if not column_exists("messages", "user_id", inspector): + op.add_column("messages", sa.Column("user_id", sa.Text()), schema=schema) + print("Added user_id column to messages table") + else: + print("user_id column already exists in messages table") + + # Create foreign key constraints for app_id and user_id + if not fk_exists("messages", "messages_app_id_fkey", inspector): + try: + op.create_foreign_key( + "messages_app_id_fkey", + "messages", + "apps", + ["app_id"], + ["public_id"], + source_schema=schema, + referent_schema=schema + ) + print("Created app_id foreign key for messages table") + except IntegrityError: + print("Cannot create app_id foreign key for messages table - integrity error") + else: + print("app_id foreign key already exists for messages table") + + if not fk_exists("messages", "messages_user_id_fkey", inspector): + try: + op.create_foreign_key( + "messages_user_id_fkey", + "messages", + "users", + ["user_id"], + ["public_id"], + source_schema=schema, + referent_schema=schema + ) + print("Created user_id foreign key for messages table") + except IntegrityError: + print("Cannot create user_id foreign key for messages table - integrity error") + else: + print("user_id foreign key already exists for messages table") + + # Data migration: Fill app_id and user_id from sessions table + try: + op.execute(""" + UPDATE messages m + SET app_id = s.app_id, user_id = s.user_id + FROM sessions s + WHERE m.session_id = s.public_id + AND ( + m.app_id IS NULL OR m.app_id <> s.app_id OR + m.user_id IS NULL OR m.user_id <> s.user_id + ) + """) + print("Updated app_id and user_id values from sessions for messages table") + except Exception as e: + print(f"Error updating app_id and user_id values for messages table: {e}") + + # Make app_id and user_id non-nullable + try: + op.alter_column("messages", "app_id", nullable=False, schema=schema) + op.alter_column("messages", "user_id", nullable=False, schema=schema) + print("Made app_id and user_id non-nullable for messages table") + except (ProgrammingError, IntegrityError) as e: + print(f"Error making app_id and user_id non-nullable for messages table: {e}") + + # Create indices for messages foreign keys + if not index_exists("messages", "ix_messages_app_id"): + op.create_index(op.f("ix_messages_app_id"), "messages", ["app_id"], unique=False, schema=schema) + print("Created index ix_messages_app_id") + else: + print("Index ix_messages_app_id already exists") + + if not index_exists("messages", "ix_messages_user_id"): + op.create_index(op.f("ix_messages_user_id"), "messages", ["user_id"], unique=False, schema=schema) + print("Created index ix_messages_user_id") + else: + print("Index ix_messages_user_id already exists") + + # 3. METAMESSAGES TABLE + if not column_exists("metamessages", "app_id", inspector): + op.add_column("metamessages", sa.Column("app_id", sa.Text()), schema=schema) + print("Added app_id column to metamessages table") + else: + print("app_id column already exists in metamessages table") + + # Create foreign key constraint for app_id + if not fk_exists("metamessages", "metamessages_app_id_fkey", inspector): + try: + op.create_foreign_key( + "metamessages_app_id_fkey", + "metamessages", + "apps", + ["app_id"], + ["public_id"], + source_schema=schema, + referent_schema=schema + ) + print("Created app_id foreign key for metamessages table") + except IntegrityError: + print("Cannot create app_id foreign key for metamessages table - integrity error") + else: + print("app_id foreign key already exists for metamessages table") + + # Data migration: Fill app_id from users table + try: + op.execute(""" + UPDATE metamessages m + SET app_id = u.app_id + FROM users u + WHERE m.user_id = u.public_id + AND (m.app_id IS NULL OR m.app_id <> u.app_id) + """) + print("Updated app_id values from users") + except Exception as e: + print(f"Error updating app_id values: {e}") + + # Make app_id non-nullable + try: + op.alter_column("metamessages", "app_id", nullable=False, schema=schema) + print("Made app_id non-nullable for metamessages table") + except (ProgrammingError, IntegrityError) as e: + print(f"Error making app_id non-nullable for metamessages table: {e}") + + # Create index for metamessages.app_id + if not index_exists("metamessages", "ix_metamessages_app_id"): + op.create_index(op.f("ix_metamessages_app_id"), "metamessages", ["app_id"], unique=False, schema=schema) + print("Created index ix_metamessages_app_id") + else: + print("Index ix_metamessages_app_id already exists") + + # 4. COLLECTIONS TABLE + if not column_exists("collections", "app_id", inspector): + op.add_column("collections", sa.Column("app_id", sa.Text()), schema=schema) + print("Added app_id column to collections table") + else: + print("app_id column already exists in collections table") + + # Create foreign key constraint for app_id + if not fk_exists("collections", "collections_app_id_fkey", inspector): + try: + op.create_foreign_key( + "collections_app_id_fkey", + "collections", + "apps", + ["app_id"], + ["public_id"], + source_schema=schema, + referent_schema=schema + ) + print("Created app_id foreign key for collections table") + except IntegrityError: + print("Cannot create app_id foreign key for collections table - integrity error") + else: + print("app_id foreign key already exists for collections table") + + # Data migration: Fill app_id from users table + try: + op.execute(""" + UPDATE collections c + SET app_id = u.app_id + FROM users u + WHERE c.user_id = u.public_id + AND (c.app_id IS NULL OR c.app_id <> u.app_id) + """) + print("Updated app_id values from users for collections table") + except Exception as e: + print(f"Error updating app_id values for collections table: {e}") + + # Make app_id non-nullable + try: + op.alter_column("collections", "app_id", nullable=False, schema=schema) + print("Made app_id non-nullable for collections table") + except (ProgrammingError, IntegrityError) as e: + print(f"Error making app_id non-nullable for collections table: {e}") + + # Create index for collections.app_id + if not index_exists("collections", "ix_collections_app_id"): + op.create_index(op.f("ix_collections_app_id"), "collections", ["app_id"], unique=False, schema=schema) + print("Created index ix_collections_app_id") + else: + print("Index ix_collections_app_id already exists") + + # 5. DOCUMENTS TABLE + if not column_exists("documents", "app_id", inspector): + op.add_column("documents", sa.Column("app_id", sa.Text()), schema=schema) + print("Added app_id column to documents table") + else: + print("app_id column already exists in documents table") + + if not column_exists("documents", "user_id", inspector): + op.add_column("documents", sa.Column("user_id", sa.Text()), schema=schema) + print("Added user_id column to documents table") + else: + print("user_id column already exists in documents table") + + # Create foreign key constraints for app_id and user_id + if not fk_exists("documents", "documents_app_id_fkey", inspector): + try: + op.create_foreign_key( + "documents_app_id_fkey", + "documents", + "apps", + ["app_id"], + ["public_id"], + source_schema=schema, + referent_schema=schema + ) + print("Created app_id foreign key for documents table") + except IntegrityError: + print("Cannot create app_id foreign key for documents table - integrity error") + else: + print("app_id foreign key already exists for documents table") + + if not fk_exists("documents", "documents_user_id_fkey", inspector): + try: + op.create_foreign_key( + "documents_user_id_fkey", + "documents", + "users", + ["user_id"], + ["public_id"], + source_schema=schema, + referent_schema=schema + ) + print("Created user_id foreign key for documents table") + except IntegrityError: + print("Cannot create user_id foreign key for documents table - integrity error") + else: + print("user_id foreign key already exists for documents table") + + # Data migration: Fill app_id and user_id from collections table + try: + op.execute(""" + UPDATE documents d + SET app_id = c.app_id, user_id = c.user_id + FROM collections c + WHERE d.collection_id = c.public_id + AND ( + d.app_id IS NULL OR d.app_id <> c.app_id OR + d.user_id IS NULL OR d.user_id <> c.user_id + ) + """) + print("Updated app_id and user_id values from collections for documents table") + except Exception as e: + print(f"Error updating app_id and user_id values for documents table: {e}") + + # Make app_id and user_id non-nullable + try: + op.alter_column("documents", "app_id", nullable=False, schema=schema) + op.alter_column("documents", "user_id", nullable=False, schema=schema) + print("Made app_id and user_id non-nullable for documents table") + except (ProgrammingError, IntegrityError) as e: + print(f"Error making app_id and user_id non-nullable for documents table: {e}") + + # Create indices for documents foreign keys + if not index_exists("documents", "ix_documents_app_id"): + op.create_index(op.f("ix_documents_app_id"), "documents", ["app_id"], unique=False, schema=schema) + print("Created index ix_documents_app_id") + else: + print("Index ix_documents_app_id already exists") + + if not index_exists("documents", "ix_documents_user_id"): + op.create_index(op.f("ix_documents_user_id"), "documents", ["user_id"], unique=False, schema=schema) + print("Created index ix_documents_user_id") + else: + print("Index ix_documents_user_id already exists") + +def downgrade(): + schema = get_schema() + inspector = sa.inspect(op.get_bind()) + + # 5. Documents table + + # Drop indices + if index_exists("documents", "ix_documents_app_id", inspector): + op.drop_index("ix_documents_app_id", table_name="documents", schema=schema) + print("Dropped index ix_documents_app_id") + if index_exists("documents", "ix_documents_user_id", inspector): + op.drop_index("ix_documents_user_id", table_name="documents", schema=schema) + print("Dropped index ix_documents_user_id") + + # Make app_id and user_id nullable again + op.alter_column("documents", "app_id", nullable=True, schema=schema) + op.alter_column("documents", "user_id", nullable=True, schema=schema) + print("Made app_id and user_id nullable again for documents table") + + # Drop foreign key constraints + try: + op.drop_constraint("documents_user_id_fkey", "documents", schema=schema) + print("Dropped user_id foreign key for documents table") + except Exception as e: + print(f"Error dropping user_id foreign key for documents table: {e}") + + try: + op.drop_constraint("documents_app_id_fkey", "documents", schema=schema) + print("Dropped app_id foreign key for documents table") + except Exception as e: + print(f"Error dropping app_id foreign key for documents table: {e}") + + # Drop the columns + op.drop_column("documents", "user_id", schema=schema) + op.drop_column("documents", "app_id", schema=schema) + print("Dropped app_id and user_id columns from documents table") + + # 4. Collections table + + # Drop indices + if index_exists("collections", "ix_collections_app_id", inspector): + op.drop_index("ix_collections_app_id", table_name="collections", schema=schema) + print("Dropped index ix_collections_app_id") + + # Make app_id nullable again + op.alter_column("collections", "app_id", nullable=True, schema=schema) + print("Made app_id nullable again for collections table") + + # Drop foreign key constraint + try: + op.drop_constraint("collections_app_id_fkey", "collections", schema=schema) + print("Dropped app_id foreign key for collections table") + except Exception as e: + print(f"Error dropping app_id foreign key for collections table: {e}") + + # Drop the column + op.drop_column("collections", "app_id", schema=schema) + print("Dropped app_id column from collections table") + + # 3. Metamessages table + + # Drop indices + if index_exists("metamessages", "ix_metamessages_app_id", inspector): + op.drop_index("ix_metamessages_app_id", table_name="metamessages", schema=schema) + print("Dropped index ix_metamessages_app_id") + + # Make app_id nullable again + op.alter_column("metamessages", "app_id", nullable=True, schema=schema) + print("Made app_id nullable again for metamessages table") + + # Drop foreign key constraint + try: + op.drop_constraint("metamessages_app_id_fkey", "metamessages", schema=schema) + print("Dropped app_id foreign key for metamessages table") + except Exception as e: + print(f"Error dropping app_id foreign key for metamessages table: {e}") + + # Drop the column + op.drop_column("metamessages", "app_id", schema=schema) + print("Dropped app_id column from metamessages table") + + # 2. Messages table + + # Drop indices + if index_exists("messages", "ix_messages_app_id", inspector): + op.drop_index("ix_messages_app_id", table_name="messages", schema=schema) + print("Dropped index ix_messages_app_id") + if index_exists("messages", "ix_messages_user_id", inspector): + op.drop_index("ix_messages_user_id", table_name="messages", schema=schema) + print("Dropped index ix_messages_user_id") + + # Make app_id and user_id nullable again + op.alter_column("messages", "app_id", nullable=True, schema=schema) + op.alter_column("messages", "user_id", nullable=True, schema=schema) + print("Made app_id and user_id nullable again for messages table") + + # Drop foreign key constraints + try: + op.drop_constraint("messages_user_id_fkey", "messages", schema=schema) + print("Dropped user_id foreign key for messages table") + except Exception as e: + print(f"Error dropping user_id foreign key for messages table: {e}") + + try: + op.drop_constraint("messages_app_id_fkey", "messages", schema=schema) + print("Dropped app_id foreign key for messages table") + except Exception as e: + print(f"Error dropping app_id foreign key for messages table: {e}") + + # Drop the columns + op.drop_column("messages", "user_id", schema=schema) + op.drop_column("messages", "app_id", schema=schema) + print("Dropped app_id and user_id columns from messages table") + + # 1. Sessions table + + # Drop indices + if index_exists("sessions", "ix_sessions_app_id", inspector): + op.drop_index("ix_sessions_app_id", table_name="sessions", schema=schema) + print("Dropped index ix_sessions_app_id") + + # Make app_id nullable again + op.alter_column("sessions", "app_id", nullable=True, schema=schema) + print("Made app_id nullable again for sessions table") + + # Drop foreign key constraint + try: + op.drop_constraint("sessions_app_id_fkey", "sessions", schema=schema) + print("Dropped app_id foreign key for sessions table") + except Exception as e: + print(f"Error dropping app_id foreign key for sessions table: {e}") + + # Drop the column + op.drop_column("sessions", "app_id", schema=schema) + print("Dropped app_id column from sessions table") \ No newline at end of file diff --git a/src/agent.py b/src/agent.py index 3786c937..6e40d405 100644 --- a/src/agent.py +++ b/src/agent.py @@ -191,19 +191,16 @@ async def chat( f"Created embedding store with collection_id: {collection.public_id if collection else None}" ) - # 2. Get the latest user message to attach the user representation to stmt = ( - select(models.Message) - .join(models.Session, models.Session.public_id == models.Message.session_id) - .join(models.User, models.User.public_id == models.Session.user_id) - .join(models.App, models.App.public_id == models.User.app_id) - .where(models.App.public_id == app_id) - .where(models.User.public_id == user_id) - .where(models.Message.session_id == session_id) - .where(models.Message.is_user) - .order_by(models.Message.id.desc()) - .limit(1) - ) + select(models.Message) + .where(models.Message.app_id == app_id) + .where(models.Message.user_id == user_id) + .where(models.Message.session_id == session_id) + .where(models.Message.is_user) + .order_by(models.Message.id.desc()) + .limit(1) + ) + latest_messages = await db.execute(stmt) latest_message = latest_messages.scalar_one_or_none() latest_message_id = latest_message.public_id if latest_message else None @@ -454,12 +451,7 @@ async def generate_user_representation( logger.debug(f"Fetching latest representation for session {session_id}") latest_representation_stmt = ( select(models.Metamessage) - .join( - models.Message, - models.Message.public_id == models.Metamessage.message_id, - ) - .join(models.Session, models.Message.session_id == models.Session.public_id) - .where(models.Session.public_id == session_id) # Only from the same session + .where(models.Metamessage.session_id == session_id) # only from the same session .where( models.Metamessage.metamessage_type == USER_REPRESENTATION_METAMESSAGE_TYPE diff --git a/src/crud.py b/src/crud.py index 84100a6a..61be8b57 100644 --- a/src/crud.py +++ b/src/crud.py @@ -346,8 +346,7 @@ async def get_session( """ stmt = ( select(models.Session) - .join(models.User, models.User.public_id == models.Session.user_id) - .where(models.User.app_id == app_id) + .where(models.Session.app_id == app_id) .where(models.Session.public_id == session_id) ) if user_id is not None: @@ -361,7 +360,6 @@ async def get_session( async def get_sessions( - db: AsyncSession, app_id: str, user_id: str, reverse: Optional[bool] = False, @@ -370,8 +368,7 @@ async def get_sessions( ) -> Select: stmt = ( select(models.Session) - .join(models.User, models.User.public_id == models.Session.user_id) - .where(models.User.app_id == app_id) + .where(models.Session.app_id == app_id) .where(models.Session.user_id == user_id) ) @@ -416,6 +413,7 @@ async def create_session( honcho_session = models.Session( user_id=user_id, + app_id=app_id, h_metadata=session.metadata, ) db.add(honcho_session) @@ -488,10 +486,9 @@ async def delete_session( """ stmt = ( select(models.Session) - .join(models.User, models.User.public_id == models.Session.user_id) .where(models.Session.public_id == session_id) - .where(models.User.app_id == app_id) .where(models.Session.user_id == user_id) + .where(models.Session.app_id == app_id) ) result = await db.execute(stmt) honcho_session = result.scalar_one_or_none() @@ -531,10 +528,9 @@ async def clone_session( # Get the original session stmt = ( select(models.Session) - .join(models.User, models.User.public_id == models.Session.user_id) - .where(models.Session.public_id == original_session_id) + .where(models.Session.app_id == app_id) .where(models.Session.user_id == user_id) - .where(models.User.app_id == app_id) + .where(models.Session.public_id == original_session_id) ) original_session = await db.scalar(stmt) if not original_session: @@ -556,6 +552,7 @@ async def clone_session( # Create new session new_session = models.Session( user_id=original_session.user_id, + app_id=original_session.app_id, h_metadata=original_session.h_metadata, ) db.add(new_session) @@ -583,6 +580,8 @@ async def clone_session( "content": message.content, "is_user": message.is_user, "h_metadata": message.h_metadata, + "app_id": original_session.app_id, + "user_id": original_session.user_id, } for message in messages_to_clone ] @@ -599,8 +598,10 @@ async def clone_session( # Handle metamessages if deep copy is requested if deep_copy: # Fetch all metamessages tied to the session in a single query - stmt = select(models.Metamessage).where( - models.Metamessage.session_id == original_session_id + stmt = ( + select(models.Metamessage) + .where(models.Metamessage.session_id == original_session_id) + .order_by(models.Metamessage.id) # Explicit ordering by id ) if cutoff_message_id is not None and cutoff_message is not None: # Only get metamessages related to messages we're cloning @@ -625,6 +626,7 @@ async def clone_session( "metamessage_type": meta.metamessage_type, "content": meta.content, "h_metadata": meta.h_metadata, + "app_id": original_session.app_id, } # If the metamessage was tied to a message, tie it to the corresponding new message @@ -666,6 +668,8 @@ async def create_message( is_user=message.is_user, content=message.content, h_metadata=message.metadata, + user_id=user_id, + app_id=app_id, ) db.add(honcho_message) await db.commit() @@ -696,6 +700,8 @@ async def create_messages( "is_user": message.is_user, "content": message.content, "h_metadata": message.metadata, + "user_id": user_id, + "app_id": app_id, } for message in messages ] @@ -718,11 +724,8 @@ async def get_messages( ) -> Select: stmt = ( select(models.Message) - .join(models.Session, models.Session.public_id == models.Message.session_id) - .join(models.User, models.User.public_id == models.Session.user_id) - .join(models.App, models.App.public_id == models.User.app_id) - .where(models.App.public_id == app_id) - .where(models.User.public_id == user_id) + .where(models.Message.app_id == app_id) + .where(models.Message.user_id == user_id) .where(models.Message.session_id == session_id) ) @@ -746,11 +749,9 @@ async def get_message( ) -> Optional[models.Message]: stmt = ( select(models.Message) - .join(models.Session, models.Session.public_id == models.Message.session_id) - .join(models.User, models.User.public_id == models.Session.user_id) - .where(models.User.app_id == app_id) - .where(models.User.public_id == user_id) - .where(models.Session.public_id == session_id) + .where(models.Message.app_id == app_id) + .where(models.Message.user_id == user_id) + .where(models.Message.session_id == session_id) .where(models.Message.public_id == message_id) ) result = await db.execute(stmt) @@ -798,6 +799,7 @@ async def create_metamessage( # Initialize metamessage data metamessage_data = { "user_id": user_id, + "app_id": app_id, "metamessage_type": metamessage.metamessage_type, "content": metamessage.content, "h_metadata": metamessage.metadata, @@ -855,10 +857,8 @@ async def get_metamessages( # Base query starts with metamessage and user relationship stmt = ( select(models.Metamessage) - .join(models.User, models.User.public_id == models.Metamessage.user_id) - .join(models.App, models.App.public_id == models.User.app_id) - .where(models.App.public_id == app_id) - .where(models.User.public_id == user_id) + .where(models.Metamessage.app_id == app_id) + .where(models.Metamessage.user_id == user_id) ) # If session_id is provided, filter by it @@ -897,9 +897,8 @@ async def get_metamessage( # Base query for metamessage by ID stmt = ( select(models.Metamessage) - .join(models.User, models.User.public_id == models.Metamessage.user_id) - .where(models.User.app_id == app_id) - .where(models.User.public_id == user_id) + .where(models.Metamessage.app_id == app_id) + .where(models.Metamessage.user_id == user_id) .where(models.Metamessage.public_id == metamessage_id) ) @@ -985,9 +984,8 @@ async def get_collections( """Get a distinct list of the names of collections associated with a user""" stmt = ( select(models.Collection) - .join(models.User, models.User.public_id == models.Collection.user_id) - .where(models.User.app_id == app_id) - .where(models.User.public_id == user_id) + .where(models.Collection.app_id == app_id) + .where(models.Collection.user_id == user_id) ) if filter is not None: @@ -1021,9 +1019,8 @@ async def get_collection_by_id( """ stmt = ( select(models.Collection) - .join(models.User, models.User.public_id == models.Collection.user_id) - .where(models.User.app_id == app_id) - .where(models.User.public_id == user_id) + .where(models.Collection.app_id == app_id) + .where(models.Collection.user_id == user_id) .where(models.Collection.public_id == collection_id) ) result = await db.execute(stmt) @@ -1058,9 +1055,8 @@ async def get_collection_by_name( """ stmt = ( select(models.Collection) - .join(models.User, models.User.public_id == models.Collection.user_id) - .where(models.User.app_id == app_id) - .where(models.User.public_id == user_id) + .where(models.Collection.app_id == app_id) + .where(models.Collection.user_id == user_id) .where(models.Collection.name == name) ) result = await db.execute(stmt) @@ -1109,6 +1105,7 @@ async def create_collection( honcho_collection = models.Collection( user_id=user_id, + app_id=app_id, name=collection.name, h_metadata=collection.metadata, ) @@ -1133,6 +1130,7 @@ async def create_user_protected_collection( ) -> models.Collection: honcho_collection = models.Collection( user_id=user_id, + app_id=app_id, name=DEF_PROTECTED_COLLECTION_NAME, ) try: @@ -1283,13 +1281,8 @@ async def get_documents( ) -> Select: stmt = ( select(models.Document) - .join( - models.Collection, - models.Collection.public_id == models.Document.collection_id, - ) - .join(models.User, models.User.public_id == models.Collection.user_id) - .where(models.User.app_id == app_id) - .where(models.User.public_id == user_id) + .where(models.Document.app_id == app_id) + .where(models.Document.user_id == user_id) .where(models.Document.collection_id == collection_id) ) @@ -1329,13 +1322,8 @@ async def get_document( """ stmt = ( select(models.Document) - .join( - models.Collection, - models.Collection.public_id == models.Document.collection_id, - ) - .join(models.User, models.User.public_id == models.Collection.user_id) - .where(models.User.app_id == app_id) - .where(models.User.public_id == user_id) + .where(models.Document.app_id == app_id) + .where(models.Document.user_id == user_id) .where(models.Document.collection_id == collection_id) .where(models.Document.public_id == document_id) ) @@ -1367,13 +1355,8 @@ async def query_documents( embedding_query = response.data[0].embedding stmt = ( select(models.Document) - .join( - models.Collection, - models.Collection.public_id == models.Document.collection_id, - ) - .join(models.User, models.User.public_id == models.Collection.user_id) - .where(models.User.app_id == app_id) - .where(models.User.public_id == user_id) + .where(models.Document.app_id == app_id) + .where(models.Document.user_id == user_id) .where(models.Document.collection_id == collection_id) # .limit(top_k) ) @@ -1447,6 +1430,8 @@ async def create_document( return duplicate honcho_document = models.Document( + app_id=app_id, + user_id=user_id, collection_id=collection_id, content=document.content, h_metadata=document.metadata, @@ -1454,6 +1439,7 @@ async def create_document( ) db.add(honcho_document) await db.commit() + await db.refresh(honcho_document) return honcho_document @@ -1511,13 +1497,8 @@ async def delete_document( ) stmt = ( select(models.Document) - .join( - models.Collection, - models.Collection.public_id == models.Document.collection_id, - ) - .join(models.User, models.User.public_id == models.Collection.user_id) - .where(models.User.app_id == app_id) - .where(models.User.public_id == user_id) + .where(models.Document.app_id == app_id) + .where(models.Document.user_id == user_id) .where(models.Document.collection_id == collection_id) .where(models.Document.public_id == document_id) ) diff --git a/src/models.py b/src/models.py index 2f62b37f..a7ca233d 100644 --- a/src/models.py +++ b/src/models.py @@ -92,6 +92,7 @@ class Session(Base): messages = relationship("Message", back_populates="session") metamessages = relationship("Metamessage", back_populates="session") user_id: Mapped[str] = mapped_column(ForeignKey("users.public_id"), index=True) + app_id: Mapped[str] = mapped_column(ForeignKey("apps.public_id"), index=True) user = relationship("User", back_populates="sessions") __table_args__ = ( @@ -124,6 +125,8 @@ class Message(Base): ) session = relationship("Session", back_populates="messages") metamessages = relationship("Metamessage", back_populates="message") + user_id: Mapped[str] = mapped_column(ForeignKey("users.public_id"), index=True) + app_id: Mapped[str] = mapped_column(ForeignKey("apps.public_id"), index=True) __table_args__ = ( CheckConstraint("length(public_id) = 21", name="public_id_length"), @@ -154,6 +157,7 @@ class Metamessage(Base): # Foreign keys - message_id is now optional user_id: Mapped[str] = mapped_column(ForeignKey("users.public_id"), index=True) + app_id: Mapped[str] = mapped_column(ForeignKey("apps.public_id"), index=True) session_id: Mapped[str | None] = mapped_column( ForeignKey("sessions.public_id"), index=True, nullable=True ) @@ -236,6 +240,7 @@ class Collection(Base): user_id: Mapped[str] = mapped_column( TEXT, ForeignKey("users.public_id"), index=True ) + app_id: Mapped[str] = mapped_column(ForeignKey("apps.public_id"), index=True) __table_args__ = ( UniqueConstraint("name", "user_id", name="unique_name_collection_user"), @@ -263,6 +268,8 @@ class Document(Base): collection_id: Mapped[str] = mapped_column( TEXT, ForeignKey("collections.public_id"), index=True ) + user_id: Mapped[str] = mapped_column(ForeignKey("users.public_id"), index=True) + app_id: Mapped[str] = mapped_column(ForeignKey("apps.public_id"), index=True) collection = relationship("Collection", back_populates="documents") __table_args__ = ( diff --git a/src/routers/sessions.py b/src/routers/sessions.py index 53e64f1f..b52be7c6 100644 --- a/src/routers/sessions.py +++ b/src/routers/sessions.py @@ -93,7 +93,6 @@ async def get_sessions( return await paginate( db, await crud.get_sessions( - db, app_id=app_id, user_id=user_id, reverse=reverse, diff --git a/src/schemas.py b/src/schemas.py index aa602284..98beb44b 100644 --- a/src/schemas.py +++ b/src/schemas.py @@ -112,6 +112,8 @@ class Message(MessageBase): h_metadata: dict = Field(exclude=True) metadata: dict created_at: datetime.datetime + app_id: str + user_id: str @field_validator("metadata", mode="before") def fetch_h_metadata(cls, value, info): @@ -150,6 +152,7 @@ class Session(SessionBase): # messages: list[Message] is_active: bool user_id: str + app_id: str h_metadata: dict = Field(exclude=True) metadata: dict @@ -201,6 +204,7 @@ class Metamessage(MetamessageBase): metamessage_type: str content: str user_id: str + app_id: str session_id: str | None message_id: str | None h_metadata: dict = Field(exclude=True) @@ -256,6 +260,7 @@ class Collection(CollectionBase): id: str name: str user_id: str + app_id: str h_metadata: dict = Field(exclude=True) metadata: dict created_at: datetime.datetime @@ -306,6 +311,8 @@ class Document(DocumentBase): metadata: dict created_at: datetime.datetime collection_id: str + app_id: str + user_id: str @field_validator("metadata", mode="before") def fetch_h_metadata(cls, value, info): diff --git a/src/utils/cache.py b/src/utils/cache.py new file mode 100644 index 00000000..04744139 --- /dev/null +++ b/src/utils/cache.py @@ -0,0 +1,98 @@ +from collections import OrderedDict +import asyncio + +class LRUCache: + """ + A simple asyncio-safe LRU cache implementation. + + This cache automatically evicts the least recently used items when it reaches + its capacity limit. All operations are protected by an asyncio lock for + concurrent access safety. + """ + + def __init__(self, capacity=100): + """ + Initialize the LRU cache. + + Args: + capacity (int): Maximum number of items to store in the cache + """ + self._cache = OrderedDict() + self._capacity = max(1, capacity) + self._lock = asyncio.Lock() + + + async def get(self, key): + """ + Get a value from the cache. + + Args: + key: The key to look up + + Returns: + The cached value or None if not found + """ + async with self._lock: + if key not in self._cache: + return None + + # Move accessed item to the end to mark as most recently used + value = self._cache.pop(key) + self._cache[key] = value + return value + + async def put(self, key, value): + """ + Add or update an entry in the cache. + + Args: + key: The key to store + value: The value to store + + Returns: + The value that was stored + """ + async with self._lock: + # If key already exists, remove it first to update its position + if key in self._cache: + self._cache.pop(key) + + # If at capacity, remove the least recently used item + elif len(self._cache) >= self._capacity: + self._cache.popitem(last=False) + + # Add the new item + self._cache[key] = value + return value + + async def delete(self, key): + """ + Remove an item from the cache. + + Args: + key: The key to remove + + Returns: + bool: True if the key was removed, False if it didn't exist + """ + async with self._lock: + if key in self._cache: + self._cache.pop(key) + return True + return False + + +def generate_cache_key(model_type: str, app_id: str, user_id: str, model_id: str): + """ + Generate a cache key for a given model type, app ID, user ID, and model ID. + + Args: + model_type (str): The type of model to generate a cache key for (e.g. "collection", "document", "message", "session", "user") + app_id (str): The ID of the app to generate a cache key for + user_id (str): The ID of the user to generate a cache key for + model_id (str): The ID of the model to generate a cache key for + + Returns: + str: A cache key for the given model type, app ID, user ID, and model ID + """ + return f"{model_type}:{app_id}:{user_id}:{model_id}" \ No newline at end of file diff --git a/tests/routes/test_messages.py b/tests/routes/test_messages.py index 0c7d99df..3328269e 100644 --- a/tests/routes/test_messages.py +++ b/tests/routes/test_messages.py @@ -7,7 +7,7 @@ from src import models # Import your SQLAlchemy models async def test_create_message(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session - test_session = models.Session(user_id=test_user.public_id) + test_session = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() @@ -31,11 +31,11 @@ async def test_create_message(client, db_session, sample_data): async def test_get_messages(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session and message - test_session = models.Session(user_id=test_user.public_id) + test_session = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() test_message = models.Message( - session_id=test_session.public_id, content="Test message", is_user=True + session_id=test_session.public_id, content="Test message", is_user=True, app_id=test_app.public_id, user_id=test_user.public_id ) db_session.add(test_message) await db_session.commit() @@ -57,19 +57,23 @@ async def test_get_messages(client, db_session, sample_data): async def test_get_filtered_messages(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session and message - test_session = models.Session(user_id=test_user.public_id) + test_session = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() test_message = models.Message( session_id=test_session.public_id, content="Test message", is_user=True, + app_id=test_app.public_id, + user_id=test_user.public_id, h_metadata={"key": "value"}, ) test_message2 = models.Message( session_id=test_session.public_id, content="Test message", is_user=True, + app_id=test_app.public_id, + user_id=test_user.public_id, h_metadata={"key": "value2"}, ) db_session.add(test_message) @@ -94,11 +98,11 @@ async def test_get_filtered_messages(client, db_session, sample_data): async def test_update_message(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session and message - test_session = models.Session(user_id=test_user.public_id) + test_session = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() test_message = models.Message( - session_id=test_session.public_id, content="Test message", is_user=True + session_id=test_session.public_id, content="Test message", is_user=True, app_id=test_app.public_id, user_id=test_user.public_id ) db_session.add(test_message) await db_session.commit() @@ -116,11 +120,11 @@ async def test_update_message(client, db_session, sample_data): async def test_update_message_empty_metadata(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session and message - test_session = models.Session(user_id=test_user.public_id) + test_session = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() test_message = models.Message( - session_id=test_session.public_id, content="Test message", is_user=True + session_id=test_session.public_id, content="Test message", is_user=True, app_id=test_app.public_id, user_id=test_user.public_id ) db_session.add(test_message) await db_session.commit() @@ -139,7 +143,7 @@ async def test_update_message_empty_metadata(client, db_session, sample_data): async def test_create_batch_messages(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session - test_session = models.Session(user_id=test_user.public_id) + test_session = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() @@ -187,7 +191,7 @@ async def test_create_batch_messages(client, db_session, sample_data): @pytest.mark.asyncio async def test_create_batch_messages_limit(client, db_session, sample_data): test_app, test_user = sample_data - test_session = models.Session(user_id=test_user.public_id) + test_session = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() diff --git a/tests/routes/test_metamessages.py b/tests/routes/test_metamessages.py index ca7f643c..07a40633 100644 --- a/tests/routes/test_metamessages.py +++ b/tests/routes/test_metamessages.py @@ -7,11 +7,11 @@ from src import models # Import your SQLAlchemy models async def test_create_metamessage(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session - test_session = models.Session(user_id=test_user.public_id) + test_session = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() test_message = models.Message( - session_id=test_session.public_id, content="Test message", is_user=True + session_id=test_session.public_id, content="Test message", is_user=True, app_id=test_app.public_id, user_id=test_user.public_id ) db_session.add(test_message) await db_session.commit() @@ -40,16 +40,17 @@ async def test_create_metamessage(client, db_session, sample_data): async def test_get_metamessage(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session - test_session = models.Session(user_id=test_user.public_id) + test_session = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() test_message = models.Message( - session_id=test_session.public_id, content="Test message", is_user=True + session_id=test_session.public_id, content="Test message", is_user=True, app_id=test_app.public_id, user_id=test_user.public_id ) db_session.add(test_message) await db_session.commit() test_metamessage = models.Metamessage( user_id=test_user.public_id, + app_id=test_app.public_id, session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", @@ -65,6 +66,7 @@ async def test_get_metamessage(client, db_session, sample_data): assert response.status_code == 200 data = response.json() assert data["user_id"] == str(test_user.public_id) + assert data["app_id"] == str(test_app.public_id) assert data["session_id"] == str(test_session.public_id) assert data["message_id"] == str(test_message.public_id) assert data["content"] == "Test Metamessage" @@ -76,11 +78,11 @@ async def test_get_metamessage(client, db_session, sample_data): async def test_get_metamessages_by_session(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session - test_session = models.Session(user_id=test_user.public_id) + test_session = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() test_message = models.Message( - session_id=test_session.public_id, content="Test message", is_user=True + session_id=test_session.public_id, content="Test message", is_user=True, app_id=test_app.public_id, user_id=test_user.public_id ) db_session.add(test_message) await db_session.commit() @@ -88,6 +90,7 @@ async def test_get_metamessages_by_session(client, db_session, sample_data): # Create metamessages for the same session test_metamessage_1 = models.Metamessage( user_id=test_user.public_id, + app_id=test_app.public_id, session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", @@ -96,6 +99,7 @@ async def test_get_metamessages_by_session(client, db_session, sample_data): ) test_metamessage_2 = models.Metamessage( user_id=test_user.public_id, + app_id=test_app.public_id, session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", @@ -104,6 +108,7 @@ async def test_get_metamessages_by_session(client, db_session, sample_data): ) test_metamessage_3 = models.Metamessage( user_id=test_user.public_id, + app_id=test_app.public_id, session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", @@ -112,6 +117,7 @@ async def test_get_metamessages_by_session(client, db_session, sample_data): ) test_metamessage_4 = models.Metamessage( user_id=test_user.public_id, + app_id=test_app.public_id, session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", @@ -141,15 +147,15 @@ async def test_get_metamessages_by_session(client, db_session, sample_data): assert data["items"][0]["metamessage_type"] == "test_type" assert data["items"][0]["session_id"] == str(test_session.public_id) assert data["items"][0]["metadata"] == {} - + assert data["items"][0]["app_id"] == str(test_app.public_id) @pytest.mark.asyncio async def test_get_metamessage_by_user(client, db_session, sample_data): test_app, test_user = sample_data # Create 3 test sessions - test_session_1 = models.Session(user_id=test_user.public_id) - test_session_2 = models.Session(user_id=test_user.public_id) - test_session_3 = models.Session(user_id=test_user.public_id) + test_session_1 = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) + test_session_2 = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) + test_session_3 = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session_1) db_session.add(test_session_2) db_session.add(test_session_3) @@ -157,13 +163,13 @@ async def test_get_metamessage_by_user(client, db_session, sample_data): # Create a message in each session test_message_1 = models.Message( - session_id=test_session_1.public_id, content="Test message", is_user=True + session_id=test_session_1.public_id, content="Test message", is_user=True, app_id=test_app.public_id, user_id=test_user.public_id ) test_message_2 = models.Message( - session_id=test_session_2.public_id, content="Test message", is_user=True + session_id=test_session_2.public_id, content="Test message", is_user=True, app_id=test_app.public_id, user_id=test_user.public_id ) test_message_3 = models.Message( - session_id=test_session_3.public_id, content="Test message", is_user=True + session_id=test_session_3.public_id, content="Test message", is_user=True, app_id=test_app.public_id, user_id=test_user.public_id ) db_session.add(test_message_1) db_session.add(test_message_2) @@ -173,6 +179,7 @@ async def test_get_metamessage_by_user(client, db_session, sample_data): # Create metamessages across different sessions test_metamessage_1 = models.Metamessage( user_id=test_user.public_id, + app_id=test_app.public_id, session_id=test_session_1.public_id, message_id=test_message_1.public_id, content="Test Metamessage", @@ -181,6 +188,7 @@ async def test_get_metamessage_by_user(client, db_session, sample_data): ) test_metamessage_2 = models.Metamessage( user_id=test_user.public_id, + app_id=test_app.public_id, session_id=test_session_2.public_id, message_id=test_message_2.public_id, content="Test Metamessage", @@ -189,6 +197,7 @@ async def test_get_metamessage_by_user(client, db_session, sample_data): ) test_metamessage_3 = models.Metamessage( user_id=test_user.public_id, + app_id=test_app.public_id, session_id=test_session_3.public_id, message_id=test_message_3.public_id, content="Test Metamessage", @@ -197,6 +206,7 @@ async def test_get_metamessage_by_user(client, db_session, sample_data): ) test_metamessage_4 = models.Metamessage( user_id=test_user.public_id, + app_id=test_app.public_id, session_id=test_session_3.public_id, message_id=test_message_3.public_id, content="Test Metamessage", @@ -206,6 +216,7 @@ async def test_get_metamessage_by_user(client, db_session, sample_data): # Create a user-level metamessage (no session/message) test_metamessage_5 = models.Metamessage( user_id=test_user.public_id, + app_id=test_app.public_id, content="User level metamessage", h_metadata={}, metamessage_type="test_type", @@ -228,7 +239,7 @@ async def test_get_metamessage_by_user(client, db_session, sample_data): assert data["items"][0]["content"] in ["Test Metamessage", "User level metamessage"] assert data["items"][0]["metamessage_type"] == "test_type" assert data["items"][0]["user_id"] == str(test_user.public_id) - + assert data["items"][0]["app_id"] == str(test_app.public_id) @pytest.mark.asyncio async def test_create_user_level_metamessage(client, db_session, sample_data): @@ -251,22 +262,23 @@ async def test_create_user_level_metamessage(client, db_session, sample_data): assert data["content"] == "User level insight" assert data["metadata"] == {"source": "user_profile"} assert data["metamessage_type"] == "user_insight" - + assert data["app_id"] == str(test_app.public_id) @pytest.mark.asyncio async def test_update_metamessage(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session - test_session = models.Session(user_id=test_user.public_id) + test_session = models.Session(user_id=test_user.public_id, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() test_message = models.Message( - session_id=test_session.public_id, content="Test message", is_user=True + session_id=test_session.public_id, content="Test message", is_user=True, app_id=test_app.public_id, user_id=test_user.public_id ) db_session.add(test_message) await db_session.commit() test_metamessage = models.Metamessage( user_id=test_user.public_id, + app_id=test_app.public_id, session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", @@ -290,3 +302,4 @@ async def test_update_metamessage(client, db_session, sample_data): assert data["user_id"] == str(test_user.public_id) assert data["session_id"] == str(test_session.public_id) assert data["message_id"] == str(test_message.public_id) + assert data["app_id"] == str(test_app.public_id) \ No newline at end of file diff --git a/tests/routes/test_sessions.py b/tests/routes/test_sessions.py index c566c021..835959a2 100644 --- a/tests/routes/test_sessions.py +++ b/tests/routes/test_sessions.py @@ -59,7 +59,7 @@ async def test_get_sessions(client, db_session, sample_data): async def test_empty_update_session(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session - test_session = models.Session(user_id=test_user.public_id, h_metadata={}) + test_session = models.Session(user_id=test_user.public_id, h_metadata={}, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() @@ -75,7 +75,7 @@ async def test_update_delete_metadata(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session test_session = models.Session( - user_id=test_user.public_id, h_metadata={"default": "value"} + user_id=test_user.public_id, h_metadata={"default": "value"}, app_id=test_app.public_id ) db_session.add(test_session) await db_session.commit() @@ -93,7 +93,7 @@ async def test_update_delete_metadata(client, db_session, sample_data): async def test_update_session(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session - test_session = models.Session(user_id=test_user.public_id, h_metadata={}) + test_session = models.Session(user_id=test_user.public_id, h_metadata={}, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() @@ -110,7 +110,7 @@ async def test_update_session(client, db_session, sample_data): async def test_delete_session(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session - test_session = models.Session(user_id=test_user.public_id, h_metadata={}) + test_session = models.Session(user_id=test_user.public_id, h_metadata={}, app_id=test_app.public_id) db_session.add(test_session) await db_session.commit() response = client.delete( @@ -129,7 +129,7 @@ async def test_clone_session(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session test_session = models.Session( - user_id=test_user.public_id, h_metadata={"test": "key"} + user_id=test_user.public_id, h_metadata={"test": "key"}, app_id=test_app.public_id ) db_session.add(test_session) await db_session.commit() @@ -139,12 +139,16 @@ async def test_clone_session(client, db_session, sample_data): content="Test message", is_user=True, h_metadata={"key": "value"}, + app_id=test_app.public_id, + user_id=test_user.public_id, ) test_message2 = models.Message( session_id=test_session.public_id, content="Test message 2", is_user=True, h_metadata={"key": "value2"}, + app_id=test_app.public_id, + user_id=test_user.public_id, ) db_session.add(test_message) db_session.add(test_message2) @@ -184,7 +188,7 @@ async def test_partial_clone_session(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session test_session = models.Session( - user_id=test_user.public_id, h_metadata={"test": "key"} + user_id=test_user.public_id, h_metadata={"test": "key"}, app_id=test_app.public_id ) db_session.add(test_session) await db_session.commit() @@ -194,12 +198,16 @@ async def test_partial_clone_session(client, db_session, sample_data): content="Test message", is_user=True, h_metadata={"key": "value"}, + app_id=test_app.public_id, + user_id=test_user.public_id, ) test_message2 = models.Message( session_id=test_session.public_id, content="Test message 2", is_user=True, h_metadata={"key": "value2"}, + app_id=test_app.public_id, + user_id=test_user.public_id, ) test_message3 = models.Message( @@ -207,6 +215,8 @@ async def test_partial_clone_session(client, db_session, sample_data): content="Test message 2", is_user=True, h_metadata={"key": "value2"}, + app_id=test_app.public_id, + user_id=test_user.public_id, ) db_session.add(test_message) @@ -244,7 +254,7 @@ async def test_deep_clone_session(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session test_session = models.Session( - user_id=test_user.public_id, h_metadata={"test": "key"} + user_id=test_user.public_id, h_metadata={"test": "key"}, app_id=test_app.public_id ) db_session.add(test_session) await db_session.commit() @@ -254,12 +264,16 @@ async def test_deep_clone_session(client, db_session, sample_data): content="Test message", is_user=True, h_metadata={"key": "value"}, + app_id=test_app.public_id, + user_id=test_user.public_id, ) test_message2 = models.Message( session_id=test_session.public_id, content="Test message 2", is_user=True, h_metadata={"key": "value2"}, + app_id=test_app.public_id, + user_id=test_user.public_id, ) db_session.add(test_message) db_session.add(test_message2) @@ -272,6 +286,7 @@ async def test_deep_clone_session(client, db_session, sample_data): content="Test Metamessage 1", h_metadata={}, metamessage_type="test_type", + app_id=test_app.public_id, ) test_metamessage_2 = models.Metamessage( user_id=test_user.public_id, @@ -280,6 +295,7 @@ async def test_deep_clone_session(client, db_session, sample_data): content="Test Metamessage 2", h_metadata={}, metamessage_type="test_type", + app_id=test_app.public_id, ) test_metamessage_3 = models.Metamessage( user_id=test_user.public_id, @@ -288,6 +304,7 @@ async def test_deep_clone_session(client, db_session, sample_data): content="Test Metamessage 3", h_metadata={}, metamessage_type="test_type", + app_id=test_app.public_id, ) test_metamessage_4 = models.Metamessage( user_id=test_user.public_id, @@ -296,6 +313,7 @@ async def test_deep_clone_session(client, db_session, sample_data): content="Test Metamessage 4", h_metadata={}, metamessage_type="test_type_2", + app_id=test_app.public_id, ) db_session.add(test_metamessage_1) @@ -360,7 +378,7 @@ async def test_partial_deep_clone_session(client, db_session, sample_data): test_app, test_user = sample_data # Create a test session test_session = models.Session( - user_id=test_user.public_id, h_metadata={"test": "key"} + user_id=test_user.public_id, h_metadata={"test": "key"}, app_id=test_app.public_id ) db_session.add(test_session) await db_session.commit() @@ -370,12 +388,16 @@ async def test_partial_deep_clone_session(client, db_session, sample_data): content="Test message", is_user=True, h_metadata={"key": "value"}, + app_id=test_app.public_id, + user_id=test_user.public_id, ) test_message2 = models.Message( session_id=test_session.public_id, content="Test message 2", is_user=True, h_metadata={"key": "value2"}, + app_id=test_app.public_id, + user_id=test_user.public_id, ) db_session.add(test_message) db_session.add(test_message2) @@ -388,6 +410,7 @@ async def test_partial_deep_clone_session(client, db_session, sample_data): content="Test Metamessage 1", h_metadata={}, metamessage_type="test_type", + app_id=test_app.public_id, ) test_metamessage_2 = models.Metamessage( user_id=test_user.public_id, @@ -396,6 +419,7 @@ async def test_partial_deep_clone_session(client, db_session, sample_data): content="Test Metamessage 2", h_metadata={}, metamessage_type="test_type", + app_id=test_app.public_id, ) test_metamessage_3 = models.Metamessage( user_id=test_user.public_id, @@ -404,6 +428,7 @@ async def test_partial_deep_clone_session(client, db_session, sample_data): content="Test Metamessage 3", h_metadata={}, metamessage_type="test_type", + app_id=test_app.public_id, ) test_metamessage_4 = models.Metamessage( user_id=test_user.public_id, @@ -412,6 +437,7 @@ async def test_partial_deep_clone_session(client, db_session, sample_data): content="Test Metamessage 4", h_metadata={}, metamessage_type="test_type_2", + app_id=test_app.public_id, ) db_session.add(test_metamessage_1)