mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-09-12 21:15:27 +03:00
feat(db): replace PostgreSQL-specific JSONB with cross-database JSON type
- Change `JSONB` columns to generic `JSON` in LLMModelConfig model for database compatibility - Update migration to use `sa.String(32)` for UUID and `sa.JSON()` for config fields - Remove PostgreSQL dialect imports to support multiple database backends - Keep existing constraints and indexes with PostgreSQL-specific annotations where needed
This commit is contained in:
parent
3632641127
commit
8e43ad4912
@ -15,8 +15,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from sqlalchemy import Column, Boolean, ForeignKey, CheckConstraint, Index, Integer, String, text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy import Column, Boolean, ForeignKey, CheckConstraint, Index, Integer, String, text, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from .base import BaseTable, generate_uuid, GUID
|
||||
@ -37,16 +36,16 @@ class LLMModelConfig(BaseTable):
|
||||
config_id = Column(GUID, primary_key=True, default=generate_uuid)
|
||||
name = Column(String(100), nullable=False) # Configuration name (table-level for indexing)
|
||||
model_type = Column(String(50), nullable=False) # Model type: text, vision, stt, tts, multimodal, etc.
|
||||
config = Column(JSONB, nullable=False) # Config fields: provider, base_url, model, temperature, api_key, etc.
|
||||
config = Column(JSON, nullable=False) # Config fields: provider, base_url, model, temperature, api_key, etc.
|
||||
user_id = Column(GUID, ForeignKey("users.user_id", ondelete="CASCADE"), nullable=True)
|
||||
group_id = Column(GUID, ForeignKey("user_groups.user_group_id", ondelete="CASCADE"), nullable=True)
|
||||
is_default = Column(Boolean, default=False, nullable=False)
|
||||
version = Column(Integer, default=0, nullable=False) # Optimistic locking version
|
||||
|
||||
# Reserved fields for future use (currently unused in code)
|
||||
reserved_jsonb_1 = Column(JSONB, nullable=True)
|
||||
reserved_jsonb_2 = Column(JSONB, nullable=True)
|
||||
reserved_jsonb_3 = Column(JSONB, nullable=True)
|
||||
reserved_jsonb_1 = Column(JSON, nullable=True)
|
||||
reserved_jsonb_2 = Column(JSON, nullable=True)
|
||||
reserved_jsonb_3 = Column(JSON, nullable=True)
|
||||
|
||||
# Relationships
|
||||
user = relationship("User", backref="llm_model_configs")
|
||||
@ -65,16 +64,17 @@ class LLMModelConfig(BaseTable):
|
||||
"model_type IN ('text', 'vision', 'stt', 'tts', 'multimodal', 'embedding', 'reranking', 'other')",
|
||||
name="valid_model_type_check"
|
||||
),
|
||||
# Each user can have at most one default config (partial unique index)
|
||||
# Each user can have at most one default config (partial unique index, PostgreSQL only)
|
||||
Index("unique_user_default", "user_id", unique=True,
|
||||
postgresql_where=text("is_default = TRUE AND user_id IS NOT NULL")),
|
||||
# Each group can have at most one default config (partial unique index)
|
||||
# Each group can have at most one default config (partial unique index, PostgreSQL only)
|
||||
Index("unique_group_default", "group_id", unique=True,
|
||||
postgresql_where=text("is_default = TRUE AND group_id IS NOT NULL")),
|
||||
# Indexes for efficient queries
|
||||
Index("idx_llm_model_configs_user_id", "user_id"),
|
||||
Index("idx_llm_model_configs_group_id", "group_id"),
|
||||
Index("idx_llm_model_configs_model_type", "model_type"),
|
||||
# GIN index for JSON config (PostgreSQL only)
|
||||
Index("idx_llm_model_configs_config", "config", postgresql_using="gin"),
|
||||
)
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ Create Date: 2026-03-03
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
from sqlalchemy import inspect
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
@ -18,9 +17,10 @@ depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Get the current connection
|
||||
# Get the current connection and dialect
|
||||
conn = op.get_bind()
|
||||
inspector = inspect(conn)
|
||||
dialect_name = conn.dialect.name
|
||||
|
||||
# Check if table already exists (idempotent for databases created from code)
|
||||
tables = inspector.get_table_names()
|
||||
@ -29,22 +29,22 @@ def upgrade() -> None:
|
||||
# Table already exists from Base.metadata.create_all, skip creation
|
||||
return
|
||||
|
||||
# Create llm_model_configs table
|
||||
# Create llm_model_configs table with cross-database compatible types
|
||||
op.create_table(
|
||||
'llm_model_configs',
|
||||
sa.Column('config_id', postgresql.UUID(as_uuid=True), primary_key=True),
|
||||
sa.Column('config_id', sa.String(32), primary_key=True),
|
||||
sa.Column('name', sa.String(100), nullable=False),
|
||||
sa.Column('model_type', sa.String(50), nullable=False),
|
||||
sa.Column('config', postgresql.JSONB(), nullable=False),
|
||||
sa.Column('user_id', postgresql.UUID(as_uuid=True), sa.ForeignKey('users.user_id', ondelete='CASCADE'), nullable=True),
|
||||
sa.Column('group_id', postgresql.UUID(as_uuid=True), sa.ForeignKey('user_groups.user_group_id', ondelete='CASCADE'), nullable=True),
|
||||
sa.Column('config', sa.JSON(), nullable=False),
|
||||
sa.Column('user_id', sa.String(32), sa.ForeignKey('users.user_id', ondelete='CASCADE'), nullable=True),
|
||||
sa.Column('group_id', sa.String(32), sa.ForeignKey('user_groups.user_group_id', ondelete='CASCADE'), nullable=True),
|
||||
sa.Column('is_default', sa.Boolean(), default=False, nullable=False),
|
||||
sa.Column('version', sa.Integer(), nullable=False, server_default='0'),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('reserved_jsonb_1', postgresql.JSONB(), nullable=True, comment='Reserved field for future use'),
|
||||
sa.Column('reserved_jsonb_2', postgresql.JSONB(), nullable=True, comment='Reserved field for future use'),
|
||||
sa.Column('reserved_jsonb_3', postgresql.JSONB(), nullable=True, comment='Reserved field for future use'),
|
||||
sa.Column('reserved_jsonb_1', sa.JSON(), nullable=True, comment='Reserved field for future use'),
|
||||
sa.Column('reserved_jsonb_2', sa.JSON(), nullable=True, comment='Reserved field for future use'),
|
||||
sa.Column('reserved_jsonb_3', sa.JSON(), nullable=True, comment='Reserved field for future use'),
|
||||
sa.CheckConstraint(
|
||||
"(user_id IS NOT NULL AND group_id IS NULL) OR "
|
||||
"(user_id IS NULL AND group_id IS NOT NULL)",
|
||||
@ -60,31 +60,39 @@ def upgrade() -> None:
|
||||
op.create_index('idx_llm_model_configs_user_id', 'llm_model_configs', ['user_id'])
|
||||
op.create_index('idx_llm_model_configs_group_id', 'llm_model_configs', ['group_id'])
|
||||
op.create_index('idx_llm_model_configs_model_type', 'llm_model_configs', ['model_type'])
|
||||
op.create_index('idx_llm_model_configs_config', 'llm_model_configs', ['config'], postgresql_using='gin')
|
||||
|
||||
# Create partial unique indexes for default configs
|
||||
# Each user can have at most one default config
|
||||
op.execute("""
|
||||
CREATE UNIQUE INDEX unique_user_default
|
||||
ON llm_model_configs (user_id)
|
||||
WHERE is_default = TRUE AND user_id IS NOT NULL
|
||||
""")
|
||||
# Each group can have at most one default config
|
||||
op.execute("""
|
||||
CREATE UNIQUE INDEX unique_group_default
|
||||
ON llm_model_configs (group_id)
|
||||
WHERE is_default = TRUE AND group_id IS NOT NULL
|
||||
""")
|
||||
# PostgreSQL-specific indexes
|
||||
if dialect_name == 'postgresql':
|
||||
# GIN index for JSONB config column
|
||||
op.create_index('idx_llm_model_configs_config', 'llm_model_configs', ['config'], postgresql_using='gin')
|
||||
|
||||
# Partial unique indexes for default configs
|
||||
op.execute("""
|
||||
CREATE UNIQUE INDEX unique_user_default
|
||||
ON llm_model_configs (user_id)
|
||||
WHERE is_default = TRUE AND user_id IS NOT NULL
|
||||
""")
|
||||
op.execute("""
|
||||
CREATE UNIQUE INDEX unique_group_default
|
||||
ON llm_model_configs (group_id)
|
||||
WHERE is_default = TRUE AND group_id IS NOT NULL
|
||||
""")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Get the current connection and dialect
|
||||
conn = op.get_bind()
|
||||
dialect_name = conn.dialect.name
|
||||
|
||||
# Drop indexes
|
||||
op.drop_index('idx_llm_model_configs_config', table_name='llm_model_configs')
|
||||
op.drop_index('idx_llm_model_configs_model_type', table_name='llm_model_configs')
|
||||
op.drop_index('idx_llm_model_configs_group_id', table_name='llm_model_configs')
|
||||
op.drop_index('idx_llm_model_configs_user_id', table_name='llm_model_configs')
|
||||
op.drop_index('unique_user_default', table_name='llm_model_configs')
|
||||
op.drop_index('unique_group_default', table_name='llm_model_configs')
|
||||
|
||||
if dialect_name == 'postgresql':
|
||||
op.drop_index('idx_llm_model_configs_config', table_name='llm_model_configs')
|
||||
op.drop_index('unique_user_default', table_name='llm_model_configs')
|
||||
op.drop_index('unique_group_default', table_name='llm_model_configs')
|
||||
|
||||
# Drop table
|
||||
op.drop_table('llm_model_configs')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user