diff --git a/src/crud.py b/src/crud.py index ec293b93..5f81662f 100644 --- a/src/crud.py +++ b/src/crud.py @@ -581,6 +581,8 @@ async def clone_session( # Prepare bulk insert data for metamessages new_metamessages = [ { + "user_id": user_id, + "session_id": new_session.public_id, "message_id": message_id_map[meta.message_id], "metamessage_type": meta.metamessage_type, "content": meta.content, @@ -744,29 +746,49 @@ async def create_metamessage( db: AsyncSession, metamessage: schemas.MetamessageCreate, app_id: str, - user_id: str, - session_id: str, ): - message = await get_message( - db, - app_id=app_id, - session_id=session_id, - user_id=user_id, - message_id=metamessage.message_id, - ) - if message is None: - raise ValueError("Session not found or does not belong to user") - - honcho_metamessage = models.Metamessage( - message_id=metamessage.message_id, - metamessage_type=metamessage.metamessage_type, - content=metamessage.content, - h_metadata=metamessage.metadata, - ) - + # Validate user exists + user = await get_user(db, app_id=app_id, user_id=metamessage.user_id) + if user is None: + raise ResourceNotFoundException(f"User with ID '{metamessage.user_id}' not found") + + # Initialize metamessage data + metamessage_data = { + "user_id": metamessage.user_id, + "metamessage_type": metamessage.metamessage_type, + "content": metamessage.content, + "h_metadata": metamessage.metadata, + } + + # Validate session_id if provided + if metamessage.session_id is not None: + session = await get_session( + db, app_id=app_id, user_id=metamessage.user_id, session_id=metamessage.session_id + ) + if session is None: + raise ResourceNotFoundException(f"Session not found or does not belong to user") + metamessage_data["session_id"] = metamessage.session_id + + # Validate message_id if provided + if metamessage.message_id is not None: + message = await get_message( + db, + app_id=app_id, + session_id=metamessage.session_id, + user_id=metamessage.user_id, + message_id=metamessage.message_id, + ) + if message is None: + raise ResourceNotFoundException(f"Message not found or does not belong to session") + metamessage_data["message_id"] = metamessage.message_id + elif metamessage.message_id is not None: + # If message_id provided but no session_id, that's an error + raise ValidationException("Cannot specify message_id without session_id") + + # Create metamessage + honcho_metamessage = models.Metamessage(**metamessage_data) db.add(honcho_metamessage) await db.commit() - # await db.refresh(honcho_metamessage) return honcho_metamessage @@ -780,28 +802,32 @@ async def get_metamessages( filter: Optional[dict] = None, reverse: Optional[bool] = False, ) -> Select: + # Base query starts with metamessage and user relationship 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) - .join(models.User, models.User.public_id == models.Session.user_id) + .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) ) + # If session_id is provided, filter by it if session_id is not None: - stmt = stmt.where(models.Session.public_id == session_id) + stmt = stmt.where(models.Metamessage.session_id == session_id) + # If message_id is provided, filter by it if message_id is not None: stmt = stmt.where(models.Metamessage.message_id == message_id) + # Filter by metamessage_type if provided if metamessage_type is not None: stmt = stmt.where(models.Metamessage.metamessage_type == metamessage_type) + # Apply metadata filter if provided if filter is not None: stmt = stmt.where(models.Metamessage.h_metadata.contains(filter)) + # Apply sort order if reverse: stmt = stmt.order_by(models.Metamessage.id.desc()) else: @@ -814,22 +840,28 @@ async def get_metamessage( db: AsyncSession, app_id: str, user_id: str, - session_id: str, - message_id: str, metamessage_id: str, + session_id: Optional[str] = None, + message_id: Optional[str] = None, ) -> Optional[models.Metamessage]: + # Base query for metamessage by ID 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) - .join(models.User, models.User.public_id == models.Session.user_id) + .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.Message.session_id == session_id) - .where(models.Metamessage.message_id == message_id) .where(models.Metamessage.public_id == metamessage_id) ) + + # Add session filter if provided + if session_id is not None: + stmt = stmt.where(models.Metamessage.session_id == session_id) + + # Add message filter if provided + if message_id is not None: + stmt = stmt.where(models.Metamessage.message_id == message_id) + result = await db.execute(stmt) return result.scalar_one_or_none() @@ -838,28 +870,55 @@ async def update_metamessage( db: AsyncSession, metamessage: schemas.MetamessageUpdate, app_id: str, - user_id: str, - session_id: str, metamessage_id: str, ) -> bool: + # First retrieve the metamessage honcho_metamessage = await get_metamessage( db, app_id=app_id, - session_id=session_id, - user_id=user_id, - message_id=metamessage.message_id, + user_id=metamessage.user_id, metamessage_id=metamessage_id, + session_id=metamessage.session_id, + message_id=metamessage.message_id, ) + if honcho_metamessage is None: - raise ValueError("Metamessage not found or does not belong to user") - if ( - metamessage.metadata is not None - ): # Need to explicitly be there won't make it empty by default + raise ResourceNotFoundException("Metamessage not found or does not belong to user") + + # Validate the consistency of relationships if they're being changed + # If we're setting message_id, we must have a session_id + if metamessage.message_id is not None and metamessage.session_id is None: + # If updating message_id but not session_id, use the existing session_id + metamessage.session_id = honcho_metamessage.session_id + if metamessage.session_id is None: + raise ValidationException("Cannot specify message_id without session_id") + + # If we're updating session_id and message_id, validate they belong together + if metamessage.session_id is not None and metamessage.message_id is not None: + message = await get_message( + db, + app_id=app_id, + session_id=metamessage.session_id, + user_id=metamessage.user_id, + message_id=metamessage.message_id, + ) + if message is None: + raise ResourceNotFoundException("Message not found or doesn't belong to session") + + # Update fields + if metamessage.session_id is not None: + honcho_metamessage.session_id = metamessage.session_id + + if metamessage.message_id is not None: + honcho_metamessage.message_id = metamessage.message_id + + if metamessage.metadata is not None: honcho_metamessage.h_metadata = metamessage.metadata + if metamessage.metamessage_type is not None: honcho_metamessage.metamessage_type = metamessage.metamessage_type + await db.commit() - # await db.refresh(honcho_metamessage) return honcho_metamessage diff --git a/src/main.py b/src/main.py index 60f39695..f4dbc036 100644 --- a/src/main.py +++ b/src/main.py @@ -124,7 +124,6 @@ app.include_router(users.router, prefix="/v1") app.include_router(sessions.router, prefix="/v1") app.include_router(messages.router, prefix="/v1") app.include_router(metamessages.router, prefix="/v1") -app.include_router(metamessages.router_user_level, prefix="/v1") app.include_router(collections.router, prefix="/v1") app.include_router(documents.router, prefix="/v1") diff --git a/src/models.py b/src/models.py index 8bd53f97..9c81abff 100644 --- a/src/models.py +++ b/src/models.py @@ -62,6 +62,7 @@ class User(Base): app = relationship("App", back_populates="users") sessions = relationship("Session", back_populates="user") collections = relationship("Collection", back_populates="user") + metamessages = relationship("Metamessage", back_populates="user") __table_args__ = ( UniqueConstraint("name", "app_id", name="unique_name_app_user"), @@ -89,6 +90,7 @@ class Session(Base): DateTime(timezone=True), index=True, default=func.now() ) messages = relationship("Message", back_populates="session") + metamessages = relationship("Metamessage", back_populates="session") user_id: Mapped[str] = mapped_column(ForeignKey("users.public_id"), index=True) user = relationship("User", back_populates="sessions") @@ -149,11 +151,23 @@ class Metamessage(Base): ) metamessage_type: Mapped[str] = mapped_column(TEXT, index=True) content: Mapped[str] = mapped_column(TEXT) - message_id: Mapped[str] = mapped_column( - ForeignKey("messages.public_id"), index=True + + # Foreign keys - message_id is now optional + user_id: Mapped[str] = mapped_column( + ForeignKey("users.public_id"), index=True + ) + session_id: Mapped[str | None] = mapped_column( + ForeignKey("sessions.public_id"), index=True, nullable=True + ) + message_id: Mapped[str | None] = mapped_column( + ForeignKey("messages.public_id"), index=True, nullable=True ) + # Relationships + user = relationship("User", back_populates="metamessages") + session = relationship("Session", back_populates="metamessages") message = relationship("Message", back_populates="metamessages") + created_at: Mapped[datetime.datetime] = mapped_column( DateTime(timezone=True), index=True, default=func.now() ) @@ -166,16 +180,41 @@ class Metamessage(Base): CheckConstraint( "length(metamessage_type) <= 512", name="metamessage_type_length" ), + # Added constraints to ensure consistency + CheckConstraint( + "(message_id IS NULL) OR (session_id IS NOT NULL)", + name="message_requires_session" + ), + # Keep existing index Index( "idx_metamessages_lookup", "metamessage_type", text("id DESC"), postgresql_include=["public_id", "message_id", "created_at"], ), + # Add new indices for user, session, and message lookups + Index( + "idx_metamessages_user_lookup", + "user_id", + "metamessage_type", + text("id DESC"), + ), + Index( + "idx_metamessages_session_lookup", + "session_id", + "metamessage_type", + text("id DESC"), + ), + Index( + "idx_metamessages_message_lookup", + "message_id", + "metamessage_type", + text("id DESC"), + ), ) def __repr__(self) -> str: - return f"Metamessages(id={self.id}, message_id={self.message_id}, metamessage_type={self.metamessage_type}, content={self.content[10:]})" + return f"Metamessages(id={self.id}, user_id={self.user_id}, session_id={self.session_id}, message_id={self.message_id}, metamessage_type={self.metamessage_type})" class Collection(Base): diff --git a/src/routers/metamessages.py b/src/routers/metamessages.py index 07e69dd4..7007e5e0 100644 --- a/src/routers/metamessages.py +++ b/src/routers/metamessages.py @@ -13,12 +13,6 @@ from src.security import auth logger = logging.getLogger(__name__) router = APIRouter( - prefix="/apps/{app_id}/users/{user_id}/sessions/{session_id}/metamessages", - tags=["metamessages"], - dependencies=[Depends(auth)], -) - -router_user_level = APIRouter( prefix="/apps/{app_id}/users/{user_id}/metamessages", tags=["metamessages"], dependencies=[Depends(auth)], @@ -29,42 +23,53 @@ router_user_level = APIRouter( async def create_metamessage( app_id: str, user_id: str, - session_id: str, metamessage: schemas.MetamessageCreate, db=db, ): - """Adds a message to a session""" + """ + Create a new metamessage associated with a user. + Optionally link to a session and message by providing those IDs in the request body. + """ try: + # Set the user_id from the URL parameters + metamessage.user_id = user_id + metamessage_obj = await crud.create_metamessage( db, metamessage=metamessage, app_id=app_id, - user_id=user_id, - session_id=session_id, ) - logger.info(f"Metamessage created successfully for session {session_id}") + logger.info(f"Metamessage created successfully for user {user_id}") return metamessage_obj - except ValueError as e: - logger.warning(f"Failed to create metamessage for session {session_id}: {str(e)}") - raise ResourceNotFoundException("Session not found") from e + except (ResourceNotFoundException, ValidationException) as e: + logger.warning(f"Failed to create metamessage: {str(e)}") + raise @router.post("/list", response_model=Page[schemas.Metamessage]) async def get_metamessages( app_id: str, user_id: str, - session_id: str, options: schemas.MetamessageGet, reverse: Optional[bool] = False, db=db, ): - """Get all messages for a session""" + """ + Get metamessages with flexible filtering. + + - Filter by user only: No additional parameters needed + - Filter by session: Provide session_id + - Filter by message: Provide message_id (and session_id) + - Filter by type: Provide metamessage_type + - Filter by metadata: Provide filter object + """ try: + # Use user_id from URL path metamessages_query = await crud.get_metamessages( db, app_id=app_id, user_id=user_id, - session_id=session_id, + session_id=options.session_id, message_id=options.message_id, metamessage_type=options.metamessage_type, filter=options.filter, @@ -72,34 +77,9 @@ async def get_metamessages( ) return await paginate(db, metamessages_query) - except ValueError as e: - logger.warning(f"Failed to get metamessages for session {session_id}: {str(e)}") - raise ResourceNotFoundException("Session not found") from e - - -@router_user_level.post("/list", response_model=Page[schemas.Metamessage]) -async def get_metamessages_by_user( - app_id: str, - user_id: str, - options: schemas.MetamessageGetUserLevel, - reverse: Optional[bool] = False, - db=db, -): - """Paginate through the user metamessages for a user""" - try: - metamessages_query = await crud.get_metamessages( - db, - app_id=app_id, - user_id=user_id, - metamessage_type=options.metamessage_type, - reverse=reverse, - filter=options.filter, - ) - - return await paginate(db, metamessages_query) - except ValueError as e: - logger.warning(f"Failed to get metamessages for user {user_id}: {str(e)}") - raise ResourceNotFoundException("User not found") from e + except (ResourceNotFoundException, ValidationException) as e: + logger.warning(f"Failed to get metamessages: {str(e)}") + raise @router.get( @@ -109,8 +89,6 @@ async def get_metamessages_by_user( async def get_metamessage( app_id: str, user_id: str, - session_id: str, - message_id: str, metamessage_id: str, db=db, ): @@ -118,13 +96,11 @@ async def get_metamessage( honcho_metamessage = await crud.get_metamessage( db, app_id=app_id, - session_id=session_id, user_id=user_id, - message_id=message_id, metamessage_id=metamessage_id, ) if honcho_metamessage is None: - logger.warning(f"Metamessage {metamessage_id} not found for message {message_id}") + logger.warning(f"Metamessage {metamessage_id} not found") raise ResourceNotFoundException(f"Metamessage with ID {metamessage_id} not found") return honcho_metamessage @@ -136,27 +112,23 @@ async def get_metamessage( async def update_metamessage( app_id: str, user_id: str, - session_id: str, metamessage_id: str, metamessage: schemas.MetamessageUpdate, db=db, ): - """Update's the metadata of a metamessage""" - if metamessage.metadata is None: - logger.warning(f"Update attempted with empty metadata for metamessage {metamessage_id}") - raise ValidationException("Metamessage metadata cannot be empty") - + """Update a metamessage's metadata, type, or relationships""" + # Ensure user_id from URL path is used + metamessage.user_id = user_id + try: updated_metamessage = await crud.update_metamessage( db, metamessage=metamessage, app_id=app_id, - user_id=user_id, - session_id=session_id, metamessage_id=metamessage_id, ) logger.info(f"Metamessage {metamessage_id} updated successfully") return updated_metamessage - except ValueError as e: + except (ResourceNotFoundException, ValidationException) as e: logger.warning(f"Failed to update metamessage {metamessage_id}: {str(e)}") - raise ResourceNotFoundException("Session or metamessage not found") from e + raise diff --git a/src/schemas.py b/src/schemas.py index a2fb23c2..63b23904 100644 --- a/src/schemas.py +++ b/src/schemas.py @@ -172,23 +172,24 @@ class MetamessageBase(BaseModel): class MetamessageCreate(MetamessageBase): metamessage_type: Annotated[str, Field(min_length=1, max_length=50)] content: Annotated[str, Field(min_length=0, max_length=50000)] - message_id: str + user_id: str | None = None # Will be set from URL parameter in endpoint + session_id: str | None = None + message_id: str | None = None metadata: dict = {} class MetamessageGet(MetamessageBase): metamessage_type: str | None = None + user_id: str | None = None # Can be provided in URL or body + session_id: str | None = None message_id: str | None = None filter: dict | None = None -class MetamessageGetUserLevel(MessageBase): - filter: dict | None = None - metamessage_type: str | None = None - - class MetamessageUpdate(MetamessageBase): - message_id: str + user_id: str | None = None # Will be set from URL parameter in endpoint + session_id: str | None = None + message_id: str | None = None metamessage_type: str | None = None metadata: dict | None = None @@ -198,7 +199,9 @@ class Metamessage(MetamessageBase): id: str metamessage_type: str content: str - message_id: str + user_id: str + session_id: str | None + message_id: str | None h_metadata: dict = Field(exclude=True) metadata: dict created_at: datetime.datetime diff --git a/tests/routes/test_metamessages.py b/tests/routes/test_metamessages.py index 65ec8f6f..e2d5381f 100644 --- a/tests/routes/test_metamessages.py +++ b/tests/routes/test_metamessages.py @@ -17,8 +17,9 @@ async def test_create_metamessage(client, db_session, sample_data): await db_session.commit() response = client.post( - f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/sessions/{test_session.public_id}/metamessages", + f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/metamessages", json={ + "session_id": str(test_session.public_id), "message_id": str(test_message.public_id), "content": "Test Metamessage", "metadata": {}, @@ -27,6 +28,8 @@ async def test_create_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["session_id"] == str(test_session.public_id) assert data["message_id"] == str(test_message.public_id) assert data["content"] == "Test Metamessage" assert data["metadata"] == {} @@ -46,19 +49,23 @@ async def test_get_metamessage(client, db_session, sample_data): db_session.add(test_message) await db_session.commit() test_metamessage = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", - metadata={}, + h_metadata={}, metamessage_type="test_type", ) db_session.add(test_metamessage) await db_session.commit() response = client.get( - f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/sessions/{test_session.public_id}/metamessages/{test_metamessage.public_id}/?message_id={test_message.public_id}" + f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/metamessages/{test_metamessage.public_id}" ) assert response.status_code == 200 data = response.json() + 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["content"] == "Test Metamessage" assert data["metadata"] == {} @@ -66,7 +73,7 @@ async def test_get_metamessage(client, db_session, sample_data): @pytest.mark.asyncio -async def test_get_metamessages(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) @@ -77,25 +84,35 @@ async def test_get_metamessages(client, db_session, sample_data): ) db_session.add(test_message) await db_session.commit() + + # Create metamessages for the same session test_metamessage_1 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", h_metadata={}, metamessage_type="test_type", ) test_metamessage_2 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", h_metadata={}, metamessage_type="test_type", ) test_metamessage_3 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", h_metadata={}, metamessage_type="test_type", ) test_metamessage_4 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", h_metadata={}, @@ -107,25 +124,29 @@ async def test_get_metamessages(client, db_session, sample_data): db_session.add(test_metamessage_4) await db_session.commit() + # Filter by session and type response = client.post( - f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/sessions/{test_session.public_id}/metamessages/list", - json={"metamessage_type": "test_type"}, + f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/metamessages/list", + json={ + "session_id": str(test_session.public_id), + "metamessage_type": "test_type" + }, ) assert response.status_code == 200 data = response.json() assert "items" in data - assert len(data["items"]) > 0 assert len(data["items"]) == 3 assert data["items"][0]["content"] == "Test Metamessage" assert data["items"][0]["metamessage_type"] == "test_type" + assert data["items"][0]["session_id"] == str(test_session.public_id) assert data["items"][0]["metadata"] == {} @pytest.mark.asyncio async def test_get_metamessage_by_user(client, db_session, sample_data): test_app, test_user = sample_data - # Create a 3 test sessions + # 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) @@ -149,48 +170,87 @@ async def test_get_metamessage_by_user(client, db_session, sample_data): db_session.add(test_message_3) await db_session.commit() - # create a metamessage on each message + # Create metamessages across different sessions test_metamessage_1 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session_1.public_id, message_id=test_message_1.public_id, content="Test Metamessage", h_metadata={}, metamessage_type="test_type", ) test_metamessage_2 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session_2.public_id, message_id=test_message_2.public_id, content="Test Metamessage", h_metadata={}, metamessage_type="test_type", ) test_metamessage_3 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session_3.public_id, message_id=test_message_3.public_id, content="Test Metamessage", h_metadata={}, metamessage_type="test_type", ) test_metamessage_4 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session_3.public_id, message_id=test_message_3.public_id, content="Test Metamessage", h_metadata={}, metamessage_type="test_type_2", ) + # Create a user-level metamessage (no session/message) + test_metamessage_5 = models.Metamessage( + user_id=test_user.public_id, + content="User level metamessage", + h_metadata={}, + metamessage_type="test_type", + ) db_session.add(test_metamessage_1) db_session.add(test_metamessage_2) db_session.add(test_metamessage_3) db_session.add(test_metamessage_4) + db_session.add(test_metamessage_5) await db_session.commit() + # Filter only by type across all user's metamessages response = client.post( f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/metamessages/list", json={"metamessage_type": "test_type"}, ) assert response.status_code == 200 data = response.json() - assert len(data["items"]) > 0 - assert len(data["items"]) == 3 - assert data["items"][0]["content"] == "Test Metamessage" + assert len(data["items"]) == 4 # All test_type metamessages for the user + assert data["items"][0]["content"] in ["Test Metamessage", "User level metamessage"] assert data["items"][0]["metamessage_type"] == "test_type" - assert data["items"][0]["metadata"] == {} + assert data["items"][0]["user_id"] == str(test_user.public_id) + + +@pytest.mark.asyncio +async def test_create_user_level_metamessage(client, db_session, sample_data): + test_app, test_user = sample_data + + # Create a user-level metamessage (no session or message) + response = client.post( + f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/metamessages", + json={ + "content": "User level insight", + "metadata": {"source": "user_profile"}, + "metamessage_type": "user_insight", + }, + ) + assert response.status_code == 200 + data = response.json() + assert data["user_id"] == str(test_user.public_id) + assert data["session_id"] is None + assert data["message_id"] is None + assert data["content"] == "User level insight" + assert data["metadata"] == {"source": "user_profile"} + assert data["metamessage_type"] == "user_insight" @pytest.mark.asyncio @@ -206,21 +266,28 @@ async def test_update_metamessage(client, db_session, sample_data): db_session.add(test_message) await db_session.commit() test_metamessage = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage", - metadata={}, + h_metadata={}, metamessage_type="test_type", ) db_session.add(test_metamessage) await db_session.commit() response = client.put( - f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/sessions/{test_session.public_id}/metamessages/{test_metamessage.public_id}", + f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/metamessages/{test_metamessage.public_id}", json={ - "message_id": str(test_message.public_id), + "user_id": str(test_user.public_id), "metadata": {"new_key": "new_value"}, + "metamessage_type": "updated_type" }, ) assert response.status_code == 200 data = response.json() assert data["metadata"] == {"new_key": "new_value"} + assert data["metamessage_type"] == "updated_type" + 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) diff --git a/tests/routes/test_sessions.py b/tests/routes/test_sessions.py index 50e15ae9..f4a75624 100644 --- a/tests/routes/test_sessions.py +++ b/tests/routes/test_sessions.py @@ -266,24 +266,32 @@ async def test_deep_clone_session(client, db_session, sample_data): await db_session.commit() test_metamessage_1 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage 1", h_metadata={}, metamessage_type="test_type", ) test_metamessage_2 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage 2", h_metadata={}, metamessage_type="test_type", ) test_metamessage_3 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message2.public_id, content="Test Metamessage 3", h_metadata={}, metamessage_type="test_type", ) test_metamessage_4 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message2.public_id, content="Test Metamessage 4", h_metadata={}, @@ -374,24 +382,32 @@ async def test_partial_deep_clone_session(client, db_session, sample_data): await db_session.commit() test_metamessage_1 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage 1", h_metadata={}, metamessage_type="test_type", ) test_metamessage_2 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message.public_id, content="Test Metamessage 2", h_metadata={}, metamessage_type="test_type", ) test_metamessage_3 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message2.public_id, content="Test Metamessage 3", h_metadata={}, metamessage_type="test_type", ) test_metamessage_4 = models.Metamessage( + user_id=test_user.public_id, + session_id=test_session.public_id, message_id=test_message2.public_id, content="Test Metamessage 4", h_metadata={}, diff --git a/tests/routes/test_validation_api.py b/tests/routes/test_validation_api.py index 3d1cbbd0..e5aeccb3 100644 --- a/tests/routes/test_validation_api.py +++ b/tests/routes/test_validation_api.py @@ -262,10 +262,11 @@ def test_metamessage_validations_api(client, sample_data): # Test metamessage_type too short response = client.post( - f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/sessions/{session_id}/metamessages", + f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/metamessages", json={ "metamessage_type": "", "content": "test content", + "session_id": session_id, "message_id": message_id, "metadata": {} } @@ -278,10 +279,11 @@ def test_metamessage_validations_api(client, sample_data): # Test metamessage_type too long response = client.post( - f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/sessions/{session_id}/metamessages", + f"/v1/apps/{test_app.public_id}/users/{test_user.public_id}/metamessages", json={ "metamessage_type": "a" * 51, "content": "test content", + "session_id": session_id, "message_id": message_id, "metadata": {} } diff --git a/uv.lock b/uv.lock index 2ab8ff84..3f2d47c7 100644 --- a/uv.lock +++ b/uv.lock @@ -416,7 +416,7 @@ wheels = [ [[package]] name = "honcho" -version = "0.0.16" +version = "0.0.17" source = { virtual = "." } dependencies = [ { name = "alembic" },