This commit is contained in:
xinyan 2025-12-09 12:01:53 +08:00
commit 569e83fcbf
2 changed files with 146 additions and 34 deletions

View file

@ -0,0 +1,99 @@
"""add_columns_to_model
Revision ID: 85f7b5b5ef68
Revises: h1i2j3k4l5m6
Create Date: 2025-12-09 11:19:15.576715
"""
# 验证示例(手动执行,不会在迁移中运行):
# python -c "
# from open_webui.internal.db import get_db
# from sqlalchemy import inspect
# with get_db() as db:
# insp = inspect(db.bind)
# cols = insp.get_columns('model')
# names = [c['name'] for c in cols]
# print('model columns:', names)
# "
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
from open_webui.internal.db import JSONField
# revision identifiers, used by Alembic.
revision: str = "85f7b5b5ef68"
down_revision: Union[str, None] = "h1i2j3k4l5m6"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""为 model 表添加 UI/分组/排序相关字段,并为 provider 建索引。"""
conn = op.get_bind()
inspector = sa.inspect(conn)
existing_cols = {col["name"] for col in inspector.get_columns("model")}
existing_indexes = {idx["name"] for idx in inspector.get_indexes("model")}
if "icon_url" not in existing_cols:
op.add_column("model", sa.Column("icon_url", sa.Text(), nullable=True))
if "provider" not in existing_cols:
op.add_column("model", sa.Column("provider", sa.String(length=50), nullable=True))
if "idx_model_provider" not in existing_indexes:
op.create_index("idx_model_provider", "model", ["provider"])
if "description" not in existing_cols:
op.add_column("model", sa.Column("description", sa.Text(), nullable=True))
if "context_length" not in existing_cols:
op.add_column(
"model",
sa.Column(
"context_length",
sa.Integer(),
nullable=False,
server_default="4096",
),
)
if "tags" not in existing_cols:
op.add_column("model", sa.Column("tags", JSONField(), nullable=True))
if "sort_order" not in existing_cols:
op.add_column(
"model",
sa.Column(
"sort_order",
sa.Integer(),
nullable=False,
server_default="0",
),
)
def downgrade() -> None:
conn = op.get_bind()
inspector = sa.inspect(conn)
existing_cols = {col["name"] for col in inspector.get_columns("model")}
existing_indexes = {idx["name"] for idx in inspector.get_indexes("model")}
if "idx_model_provider" in existing_indexes:
op.drop_index("idx_model_provider", table_name="model")
if "provider" in existing_cols:
op.drop_column("model", "provider")
if "icon_url" in existing_cols:
op.drop_column("model", "icon_url")
if "description" in existing_cols:
op.drop_column("model", "description")
if "context_length" in existing_cols:
op.drop_column("model", "context_length")
if "tags" in existing_cols:
op.drop_column("model", "tags")
if "sort_order" in existing_cols:
op.drop_column("model", "sort_order")

View file

@ -11,9 +11,8 @@ from open_webui.models.users import Users, UserResponse
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
from sqlalchemy import or_, and_, func from sqlalchemy import Index
from sqlalchemy.dialects import postgresql, sqlite from sqlalchemy import BigInteger, Column, Text, JSON, Boolean, Integer, String
from sqlalchemy import BigInteger, Column, Text, JSON, Boolean
from open_webui.utils.access_control import has_access from open_webui.utils.access_control import has_access
@ -54,52 +53,54 @@ class Model(Base):
__tablename__ = "model" __tablename__ = "model"
id = Column(Text, primary_key=True) id = Column(Text, primary_key=True)
""" """模型唯一标识符,用于 API 调用"""
The model's id as used in the API. If set to an existing model, it will override the model.
"""
user_id = Column(Text) user_id = Column(Text)
"""模型创建者的用户 ID用于权限控制"""
base_model_id = Column(Text, nullable=True) base_model_id = Column(Text, nullable=True)
""" """指向实际使用的基础模型的 IDNULL 表示基础模型"""
An optional pointer to the actual model that should be used when proxying requests.
"""
name = Column(Text) name = Column(Text)
""" """人类可读的模型显示名称"""
The human-readable display name of the model.
""" icon_url = Column(Text, nullable=True)
"""模型图标 URL用于列表展示"""
provider = Column(String(50), nullable=True)
"""供应商标识,如 openai、anthropic、ollama"""
description = Column(Text, nullable=True)
"""模型简介文案,前端展示用"""
params = Column(JSONField) params = Column(JSONField)
""" """模型运行参数,存储为 JSON 格式"""
Holds a JSON encoded blob of parameters, see `ModelParams`.
"""
meta = Column(JSONField) meta = Column(JSONField)
""" """模型元数据,存储为 JSON 格式"""
Holds a JSON encoded blob of metadata, see `ModelMeta`.
"""
access_control = Column(JSON, nullable=True) # Controls data access levels. context_length = Column(Integer, nullable=False, default=4096)
# Defines access control rules for this entry. """上下文长度限制,默认 4096"""
# - `None`: Public access, available to all users with the "user" role.
# - `{}`: Private access, restricted exclusively to the owner. tags = Column(JSONField, nullable=True)
# - Custom permissions: Specific access control for reading and writing; """模型标签,用于分组和筛选"""
# Can specify group or user-level restrictions:
# { sort_order = Column(Integer, nullable=False, default=0)
# "read": { """排序权重,数值越大越靠前"""
# "group_ids": ["group_id1", "group_id2"],
# "user_ids": ["user_id1", "user_id2"] access_control = Column(JSON, nullable=True)
# }, """访问控制规则None=公开,{}=私有JSON=自定义权限"""
# "write": {
# "group_ids": ["group_id1", "group_id2"],
# "user_ids": ["user_id1", "user_id2"]
# }
# }
is_active = Column(Boolean, default=True) is_active = Column(Boolean, default=True)
"""模型激活状态False 表示已禁用"""
updated_at = Column(BigInteger) updated_at = Column(BigInteger)
"""最后更新时间戳Unix 时间戳)"""
created_at = Column(BigInteger) created_at = Column(BigInteger)
"""创建时间戳Unix 时间戳)"""
__table_args__ = (Index("idx_model_provider", "provider"),)
class ModelModel(BaseModel): class ModelModel(BaseModel):
@ -108,8 +109,14 @@ class ModelModel(BaseModel):
base_model_id: Optional[str] = None base_model_id: Optional[str] = None
name: str name: str
icon_url: Optional[str] = None
provider: Optional[str] = None
description: Optional[str] = None
params: ModelParams params: ModelParams
meta: ModelMeta meta: ModelMeta
context_length: int = 4096
tags: Optional[dict] = None
sort_order: int = 0
access_control: Optional[dict] = None access_control: Optional[dict] = None
@ -137,8 +144,14 @@ class ModelForm(BaseModel):
id: str id: str
base_model_id: Optional[str] = None base_model_id: Optional[str] = None
name: str name: str
icon_url: Optional[str] = None
provider: Optional[str] = None
description: Optional[str] = None
meta: ModelMeta meta: ModelMeta
params: ModelParams params: ModelParams
context_length: int = 4096
tags: Optional[dict] = None
sort_order: int = 0
access_control: Optional[dict] = None access_control: Optional[dict] = None
is_active: bool = True is_active: bool = True