Merge pull request #511 from MODSetter/dev

refactor: remove UserSearchSpacePreference model and related relationships
This commit is contained in:
Rohan Verma 2025-11-29 00:20:37 -08:00 committed by GitHub
commit d49a70b98e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 108 additions and 70 deletions

View file

@ -31,16 +31,40 @@ DEFAULT_ROLES = [
"name": "Admin", "name": "Admin",
"description": "Can manage members, roles, and all content", "description": "Can manage members, roles, and all content",
"permissions": [ "permissions": [
"documents:create", "documents:read", "documents:update", "documents:delete", "documents:create",
"chats:create", "chats:read", "chats:update", "chats:delete", "documents:read",
"llm_configs:create", "llm_configs:read", "llm_configs:update", "llm_configs:delete", "documents:update",
"logs:read", "logs:delete", "documents:delete",
"podcasts:create", "podcasts:read", "podcasts:update", "podcasts:delete", "chats:create",
"connectors:create", "connectors:read", "connectors:update", "connectors:delete", "chats:read",
"members:read", "members:update", "members:delete", "chats:update",
"roles:create", "roles:read", "roles:update", "roles:delete", "chats:delete",
"invites:create", "invites:read", "invites:delete", "llm_configs:create",
"settings:read", "settings:update", "llm_configs:read",
"llm_configs:update",
"llm_configs:delete",
"logs:read",
"logs:delete",
"podcasts:create",
"podcasts:read",
"podcasts:update",
"podcasts:delete",
"connectors:create",
"connectors:read",
"connectors:update",
"connectors:delete",
"members:read",
"members:update",
"members:delete",
"roles:create",
"roles:read",
"roles:update",
"roles:delete",
"invites:create",
"invites:read",
"invites:delete",
"settings:read",
"settings:update",
], ],
"is_system_role": True, "is_system_role": True,
"is_default": False, "is_default": False,
@ -49,12 +73,20 @@ DEFAULT_ROLES = [
"name": "Editor", "name": "Editor",
"description": "Can create and edit content", "description": "Can create and edit content",
"permissions": [ "permissions": [
"documents:create", "documents:read", "documents:update", "documents:create",
"chats:create", "chats:read", "chats:update", "documents:read",
"documents:update",
"chats:create",
"chats:read",
"chats:update",
"llm_configs:read", "llm_configs:read",
"logs:read", "logs:read",
"podcasts:create", "podcasts:read", "podcasts:update", "podcasts:create",
"connectors:create", "connectors:read", "connectors:update", "podcasts:read",
"podcasts:update",
"connectors:create",
"connectors:read",
"connectors:update",
"members:read", "members:read",
"roles:read", "roles:read",
], ],
@ -101,8 +133,10 @@ def upgrade():
# Create default roles for each search space # Create default roles for each search space
for role in DEFAULT_ROLES: for role in DEFAULT_ROLES:
# Convert permissions list to PostgreSQL array literal format for raw SQL # Convert permissions list to PostgreSQL array literal format for raw SQL
perms_literal = "ARRAY[" + ",".join(f"'{p}'" for p in role["permissions"]) + "]::TEXT[]" perms_literal = (
"ARRAY[" + ",".join(f"'{p}'" for p in role["permissions"]) + "]::TEXT[]"
)
result = connection.execute( result = connection.execute(
sa.text(f""" sa.text(f"""
INSERT INTO search_space_roles INSERT INTO search_space_roles
@ -116,10 +150,10 @@ def upgrade():
"is_default": role["is_default"], "is_default": role["is_default"],
"is_system_role": role["is_system_role"], "is_system_role": role["is_system_role"],
"search_space_id": ss_id, "search_space_id": ss_id,
} },
) )
role_id = result.fetchone()[0] role_id = result.fetchone()[0]
# Keep track of Owner role ID # Keep track of Owner role ID
if role["name"] == "Owner": if role["name"] == "Owner":
owner_role_id = role_id owner_role_id = role_id
@ -132,7 +166,7 @@ def upgrade():
SELECT 1 FROM search_space_memberships SELECT 1 FROM search_space_memberships
WHERE user_id = :user_id AND search_space_id = :search_space_id WHERE user_id = :user_id AND search_space_id = :search_space_id
"""), """),
{"user_id": owner_user_id, "search_space_id": ss_id} {"user_id": owner_user_id, "search_space_id": ss_id},
).fetchone() ).fetchone()
if not existing: if not existing:
@ -146,7 +180,7 @@ def upgrade():
"user_id": owner_user_id, "user_id": owner_user_id,
"search_space_id": ss_id, "search_space_id": ss_id,
"role_id": owner_role_id, "role_id": owner_role_id,
} },
) )
@ -156,7 +190,7 @@ def downgrade():
# However, this is destructive and may affect manually created data # However, this is destructive and may affect manually created data
# So we only remove system roles and owner memberships that were auto-created # So we only remove system roles and owner memberships that were auto-created
connection = op.get_bind() connection = op.get_bind()
# Remove memberships where user is owner and role is system Owner role # Remove memberships where user is owner and role is system Owner role
connection.execute( connection.execute(
sa.text(""" sa.text("""
@ -168,7 +202,7 @@ def downgrade():
AND ssr.name = 'Owner' AND ssr.name = 'Owner'
""") """)
) )
# Remove system roles # Remove system roles
connection.execute( connection.execute(
sa.text(""" sa.text("""
@ -176,4 +210,3 @@ def downgrade():
WHERE is_system_role = TRUE WHERE is_system_role = TRUE
""") """)
) )

View file

@ -0,0 +1,52 @@
"""Drop user_search_space_preferences table
Revision ID: 42
Revises: 41
Create Date: 2025-11-28
This table is no longer needed after RBAC implementation:
- LLM preferences are now stored on SearchSpace directly
- User-SearchSpace relationships are handled by SearchSpaceMembership
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "42"
down_revision = "41"
branch_labels = None
depends_on = None
def upgrade():
# Drop the user_search_space_preferences table
op.drop_table("user_search_space_preferences")
def downgrade():
# Recreate the table if rolling back
op.create_table(
"user_search_space_preferences",
sa.Column("id", sa.Integer(), primary_key=True),
sa.Column(
"created_at", sa.DateTime(timezone=True), server_default=sa.func.now()
),
sa.Column(
"user_id",
sa.UUID(),
sa.ForeignKey("user.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column(
"search_space_id",
sa.Integer(),
sa.ForeignKey("searchspaces.id", ondelete="CASCADE"),
nullable=False,
),
sa.Column("long_context_llm_id", sa.Integer(), nullable=True),
sa.Column("fast_llm_id", sa.Integer(), nullable=True),
sa.Column("strategic_llm_id", sa.Integer(), nullable=True),
sa.UniqueConstraint("user_id", "search_space_id", name="uq_user_searchspace"),
)

View file

@ -441,11 +441,6 @@ class SearchSpace(BaseModel, TimestampMixin):
order_by="LLMConfig.id", order_by="LLMConfig.id",
cascade="all, delete-orphan", cascade="all, delete-orphan",
) )
user_preferences = relationship(
"UserSearchSpacePreference",
back_populates="search_space",
cascade="all, delete-orphan",
)
# RBAC relationships # RBAC relationships
roles = relationship( roles = relationship(
@ -527,38 +522,6 @@ class LLMConfig(BaseModel, TimestampMixin):
search_space = relationship("SearchSpace", back_populates="llm_configs") search_space = relationship("SearchSpace", back_populates="llm_configs")
class UserSearchSpacePreference(BaseModel, TimestampMixin):
__tablename__ = "user_search_space_preferences"
__table_args__ = (
UniqueConstraint(
"user_id",
"search_space_id",
name="uq_user_searchspace",
),
)
user_id = Column(
UUID(as_uuid=True), ForeignKey("user.id", ondelete="CASCADE"), nullable=False
)
search_space_id = Column(
Integer, ForeignKey("searchspaces.id", ondelete="CASCADE"), nullable=False
)
# User-specific LLM preferences for this search space
# Note: These can be negative IDs for global configs (from YAML) or positive IDs for custom configs (from DB)
# Foreign keys removed to support global configs with negative IDs
long_context_llm_id = Column(Integer, nullable=True)
fast_llm_id = Column(Integer, nullable=True)
strategic_llm_id = Column(Integer, nullable=True)
# Future RBAC fields can be added here
# role = Column(String(50), nullable=True) # e.g., 'owner', 'editor', 'viewer'
# permissions = Column(JSON, nullable=True)
user = relationship("User", back_populates="search_space_preferences")
search_space = relationship("SearchSpace", back_populates="user_preferences")
class Log(BaseModel, TimestampMixin): class Log(BaseModel, TimestampMixin):
__tablename__ = "logs" __tablename__ = "logs"
@ -720,11 +683,6 @@ if config.AUTH_TYPE == "GOOGLE":
"OAuthAccount", lazy="joined" "OAuthAccount", lazy="joined"
) )
search_spaces = relationship("SearchSpace", back_populates="user") search_spaces = relationship("SearchSpace", back_populates="user")
search_space_preferences = relationship(
"UserSearchSpacePreference",
back_populates="user",
cascade="all, delete-orphan",
)
# RBAC relationships # RBAC relationships
search_space_memberships = relationship( search_space_memberships = relationship(
@ -746,11 +704,6 @@ else:
class User(SQLAlchemyBaseUserTableUUID, Base): class User(SQLAlchemyBaseUserTableUUID, Base):
search_spaces = relationship("SearchSpace", back_populates="user") search_spaces = relationship("SearchSpace", back_populates="user")
search_space_preferences = relationship(
"UserSearchSpacePreference",
back_populates="user",
cascade="all, delete-orphan",
)
# RBAC relationships # RBAC relationships
search_space_memberships = relationship( search_space_memberships = relationship(