chore: format

This commit is contained in:
Timothy Jaeryang Baek committed 2026-06-01 13:56:55 -07:00
1 parent 1f49ebf431
commit 6fce92aa12
139 files changed
+92569 -3249

No files matched your search

+28 -9
View File
@@ -119,7 +119,13 @@ class AuthsTable:
session.add(credential)
created_user = await Users.insert_new_user(
new_id, name, email, profile_image_url, role, oauth=oauth, db=session,
new_id,
name,
email,
profile_image_url,
role,
oauth=oauth,
db=session,
)
# persist both records and reload generated defaults
await session.commit()
@@ -127,7 +133,10 @@ class AuthsTable:
return created_user if credential and created_user else None
async def authenticate_user(
self, email: str, verify_password: callable, db: AsyncSession | None = None,
self,
email: str,
verify_password: callable,
db: AsyncSession | None = None,
) -> UserModel | None:
"""Verify email + password credentials and return the matching user."""
log.info('authenticate_user: %s', email)
@@ -144,7 +153,9 @@ class AuthsTable:
return resolved
async def authenticate_user_by_api_key(
self, api_key: str, db: AsyncSession | None = None,
self,
api_key: str,
db: AsyncSession | None = None,
) -> UserModel | None:
"""Look up the user that owns the given API key."""
log.info('authenticate_user_by_api_key')
@@ -163,17 +174,19 @@ class AuthsTable:
# single JOIN avoids N+1 — returns (Auth, User) tuple or None
async with get_async_db_context(db) as session:
joined_query = (
select(Auth, User)
.join(User, Auth.id == User.id)
.where(Auth.email == email, Auth.active.is_(True))
select(Auth, User).join(User, Auth.id == User.id).where(Auth.email == email, Auth.active.is_(True))
)
match = (await session.execute(joined_query)).first()
if not match:
return
_, found_user = match
return UserModel.model_validate(found_user)
async def update_email_by_id(
self, user_id: str, email: str, db: AsyncSession | None = None,
self,
user_id: str,
email: str,
db: AsyncSession | None = None,
) -> bool:
"""Set a new email on the auth record and propagate to the user row."""
async with get_async_db_context(db) as session:
@@ -185,8 +198,12 @@ class AuthsTable:
await Users.update_user_by_id(user_id, {'email': email}, db=session)
return True
# --- password modification ---
async def update_user_password_by_id(
self, user_id: str, new_password: str, db: AsyncSession | None = None,
self,
user_id: str,
new_password: str,
db: AsyncSession | None = None,
) -> bool:
"""Set a new password hash for an existing user."""
async with get_async_db_context(db) as session:
@@ -198,7 +215,9 @@ class AuthsTable:
return True
async def delete_auth_by_id(
self, id: str, db: AsyncSession | None = None,
self,
id: str,
db: AsyncSession | None = None,
) -> bool:
"""Remove a user and their auth credential in one transaction."""
async with get_async_db_context(db) as session:
+18 -9
View File
@@ -6,6 +6,7 @@ import json
import logging
import time
import uuid
# local imports
from open_webui.internal.db import Base, JSONField, get_async_db_context
from open_webui.models.automations import AutomationRun
@@ -358,9 +359,7 @@ class ChatTable:
) -> list[ChatModel]:
async with get_async_db_context(db) as session:
# Validate folder_id references — clear any that don't exist
folder_ids = {
f.folder_id for f in chat_import_forms if f.folder_id
}
folder_ids = {f.folder_id for f in chat_import_forms if f.folder_id}
existing = set()
for fid in folder_ids:
if await Folders.get_folder_by_id_and_user_id(fid, user_id, db=session):
@@ -402,7 +401,10 @@ class ChatTable:
return [ChatModel.model_validate(chat) for chat in chats]
async def update_chat_by_id(
self, id: str, chat: dict, db: AsyncSession | None = None,
self,
id: str,
chat: dict,
db: AsyncSession | None = None,
) -> ChatModel | None:
"""Persist updated chat content, sanitizing null bytes."""
try: # load the chat record for in-place mutation
@@ -512,9 +514,7 @@ class ChatTable:
except Exception as e:
log.warning('Backfill failed for message %s in chat %s: %s', message_id, chat_id, e)
async def reconcile_messages_by_chat_id(
self, chat_id: str, user_id: str, messages: dict[str, dict]
) -> None:
async def reconcile_messages_by_chat_id(self, chat_id: str, user_id: str, messages: dict[str, dict]) -> None:
"""Sync ``chat_message`` rows with the committed JSON blob.
Upserts current messages via ``backfill_messages_by_chat_id``
@@ -692,9 +692,12 @@ class ChatTable:
await session.commit()
await session.refresh(chat)
return ChatModel.model_validate(chat) # return the updated original
# refresh helper
async def update_shared_chat_by_chat_id(
self, chat_id: str, db: AsyncSession | None = None,
self,
chat_id: str,
db: AsyncSession | None = None,
) -> ChatModel | None:
"""Refresh the shared snapshot with current chat content."""
from open_webui.models.shared_chats import SharedChats
@@ -953,9 +956,12 @@ class ChatTable:
)
all_chats = result.scalars().all()
return [ChatModel.model_validate(chat) for chat in all_chats]
# retrieve conversation
async def get_chat_by_id(
self, id: str, db: AsyncSession | None = None,
self,
id: str,
db: AsyncSession | None = None,
) -> ChatModel | None:
"""Fetch a chat by PK, auto-sanitizing null bytes on read."""
try:
@@ -1034,6 +1040,7 @@ class ChatTable:
result = await session.execute(select(Chat).order_by(Chat.updated_at.desc()))
all_chats = result.scalars().all()
return [ChatModel.model_validate(chat) for chat in all_chats]
# list user conversations
async def get_chats_by_user_id(
self,
@@ -1082,6 +1089,7 @@ class ChatTable:
'total': total,
}
)
# list pinned chats
async def get_pinned_chats_by_user_id(
self, user_id: str, db: AsyncSession | None = None
@@ -1112,6 +1120,7 @@ class ChatTable:
select(Chat).filter_by(user_id=user_id, archived=True).order_by(Chat.updated_at.desc())
)
return [ChatModel.model_validate(chat) for chat in result.scalars().all()]
# search user conversations
async def get_chats_by_user_id_and_search_text(
self,
+7 -8
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import logging
import time
# local imports
from open_webui.internal.db import Base, JSONField, get_async_db_context
from open_webui.utils.misc import sanitize_metadata
@@ -46,6 +47,7 @@ class FileModel(BaseModel):
created_at: int | None # timestamp in epoch
updated_at: int | None # timestamp in epoch
# --- metadata structures ---
class FileMeta(BaseModel):
name: str | None = None
@@ -150,7 +152,9 @@ class FilesTable:
return None # insertion failed
async def get_file_by_id(
self, id: str, db: AsyncSession | None = None,
self,
id: str,
db: AsyncSession | None = None,
) -> FileModel | None:
"""Look up a file by its primary key."""
try:
@@ -409,9 +413,7 @@ class FilesTable:
# Subquery: file IDs already linked to this knowledge base
linked_ids = (
select(KnowledgeFile.file_id)
.filter(KnowledgeFile.knowledge_id == knowledge_id)
.correlate(None)
select(KnowledgeFile.file_id).filter(KnowledgeFile.knowledge_id == knowledge_id).correlate(None)
)
stmt = (
@@ -424,10 +426,7 @@ class FilesTable:
.order_by(File.created_at.desc())
)
result = await db.execute(stmt)
return [
FileModelResponse.model_validate(f, from_attributes=True)
for f in result.scalars().all()
]
return [FileModelResponse.model_validate(f, from_attributes=True) for f in result.scalars().all()]
except Exception as e:
log.warning(f'Error fetching pending files for knowledge {knowledge_id}: {e}')
return []
+2
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import logging
import time
# local imports
from open_webui.internal.db import Base, JSONField, get_async_db_context
from open_webui.models.users import UserModel, UserResponse, Users
@@ -52,6 +53,7 @@ class FunctionModel(BaseModel):
model_config = ConfigDict(from_attributes=True) # allows ORM model binding
# --- form / schema definitions ---
class FunctionWithValvesModel(BaseModel):
id: str
+1
View File
@@ -34,6 +34,7 @@ class MemoryModel(BaseModel):
created_at: int # timestamp in epoch
model_config = ConfigDict(from_attributes=True) # allows ORM mapping
class MemoriesTable:
async def insert_new_memory(
self,
+2 -6
View File
@@ -51,8 +51,7 @@ class ModelMeta(BaseModel):
if v not in _warned_profile_urls:
_warned_profile_urls.add(v)
log.warning(
'Clearing invalid profile_image_url stored in DB '
'(likely a legacy SVG data-URI): %.80s…',
'Clearing invalid profile_image_url stored in DB (likely a legacy SVG data-URI): %.80s…',
v,
)
return None
@@ -74,7 +73,6 @@ class ModelMeta(BaseModel):
class Model(Base):
"""Workspace model entry — wraps an upstream LLM with custom params and metadata."""
__tablename__ = 'model'
@@ -195,9 +193,7 @@ class ModelsTable:
models: list[ModelModel] = []
for model in all_models:
try:
models.append(
await self._to_model_model(model, access_grants=grants_map.get(model.id, []), db=db)
)
models.append(await self._to_model_model(model, access_grants=grants_map.get(model.id, []), db=db))
except Exception as exc:
log.error('Skipping model %r during get_all_models due to error: %s', model.id, exc)
return models
+39 -18
View File
@@ -55,6 +55,8 @@ class PromptModel(BaseModel):
access_grants: list[AccessGrantModel] = Field(default_factory=list)
model_config = ConfigDict(from_attributes=True) # allows ORM model binding
# --- form / schema definitions ---
# Forms
####################
@@ -107,26 +109,36 @@ class PromptsTable:
)
return PromptModel.model_validate(prompt_data)
async def insert_new_prompt(self, user_id: str, form_data: PromptForm, db: AsyncSession | None = None) -> PromptModel | None:
async def insert_new_prompt(
self, user_id: str, form_data: PromptForm, db: AsyncSession | None = None
) -> PromptModel | None:
now = int(time.time())
prompt_id = str(uuid.uuid4())
async with get_async_db_context(db) as session:
try:
record = Prompt(
id=prompt_id, user_id=user_id,
command=form_data.command, name=form_data.name,
id=prompt_id,
user_id=user_id,
command=form_data.command,
name=form_data.name,
content=form_data.content,
data=form_data.data or {}, meta=form_data.meta or {},
tags=form_data.tags or [], is_active=True,
created_at=now, updated_at=now,
data=form_data.data or {},
meta=form_data.meta or {},
tags=form_data.tags or [],
is_active=True,
created_at=now,
updated_at=now,
)
session.add(record)
await session.commit()
await session.refresh(record) # populate generated defaults
await AccessGrants.set_access_grants(
'prompt', prompt_id, form_data.access_grants, db=session,
'prompt',
prompt_id,
form_data.access_grants,
db=session,
) # persist sharing rules
if not record: # shouldn't happen, but guard anyway
@@ -145,8 +157,10 @@ class PromptsTable:
}
history_entry = await PromptHistories.create_history_entry(
prompt_id=prompt_id, snapshot=snapshot,
user_id=user_id, parent_id=None,
prompt_id=prompt_id,
snapshot=snapshot,
user_id=user_id,
parent_id=None,
commit_message=form_data.commit_message or 'Initial version',
db=session,
) # creates the first version entry
@@ -178,9 +192,7 @@ class PromptsTable:
async def get_prompt_by_command(self, command: str, db: AsyncSession | None = None) -> PromptModel | None:
"""Look up a prompt by its unique slash-command string."""
async with get_async_db_context(db) as session:
match = (await session.execute(
select(Prompt).where(Prompt.command == command)
)).scalars().first()
match = (await session.execute(select(Prompt).where(Prompt.command == command))).scalars().first()
if match is None:
return
return await self._to_prompt_model(match, db=session)
@@ -190,9 +202,15 @@ class PromptsTable:
async def get_prompts(self, db: AsyncSession | None = None) -> list[PromptUserResponse]:
"""Return all active prompts ordered by most recently updated."""
async with get_async_db_context(db) as session:
active = (await session.execute(
select(Prompt).where(Prompt.is_active.is_(True)).order_by(Prompt.updated_at.desc())
)).scalars().all()
active = (
(
await session.execute(
select(Prompt).where(Prompt.is_active.is_(True)).order_by(Prompt.updated_at.desc())
)
)
.scalars()
.all()
)
user_ids = list(set(p.user_id for p in active))
prompt_ids = [p.id for p in active]
@@ -600,10 +618,13 @@ class PromptsTable:
return await self._to_prompt_model(prompt, db=session)
except Exception as e: # connection error
log.error(f"Failed to restore prompt version: {e}")
log.error(f'Failed to restore prompt version: {e}')
return None # restoration failed
async def toggle_prompt_active(
self, prompt_id: str, db: AsyncSession | None = None,
self,
prompt_id: str,
db: AsyncSession | None = None,
) -> PromptModel | None:
"""Flip the is_active flag on a prompt."""
if not prompt_id:
@@ -654,7 +675,7 @@ class PromptsTable:
return True
return False
except Exception as err:
log.error(f"Failed to delete prompt: {err}")
log.error(f'Failed to delete prompt: {err}')
return False # deletion failed
async def get_tags(self, db: AsyncSession | None = None) -> list[str]:
+6 -1
View File
@@ -5,6 +5,7 @@ from __future__ import annotations
import logging
import time
import uuid
# local imports
from open_webui.internal.db import Base, JSONField, get_async_db_context
from pydantic import BaseModel, ConfigDict
@@ -37,6 +38,7 @@ class TagModel(BaseModel):
meta: dict | None = None
model_config = ConfigDict(from_attributes=True) # allows ORM model binding
# --- tag schema forms ---
# Forms
####################
@@ -49,7 +51,10 @@ class TagChatIdForm(BaseModel):
class TagTable:
async def insert_new_tag(
self, name: str, user_id: str, db: AsyncSession | None = None,
self,
name: str,
user_id: str,
db: AsyncSession | None = None,
) -> TagModel | None:
"""Create a new tag, deriving the id from the name."""
async with get_async_db_context(db) as db:
+7 -7
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import logging
import time
# local imports
from open_webui.internal.db import Base, JSONField, get_async_db_context
from open_webui.models.access_grants import AccessGrantModel, AccessGrants
@@ -50,6 +51,7 @@ class ToolModel(BaseModel):
model_config = ConfigDict(from_attributes=True) # enables ORM mapping
# --- tool request forms ---
# Forms
####################
@@ -138,7 +140,9 @@ class ToolsTable:
return None # creation failed
async def get_tool_by_id(
self, id: str, db: AsyncSession | None = None,
self,
id: str,
db: AsyncSession | None = None,
) -> ToolModel | None:
"""Fetch a single tool by primary key, including access grants."""
try: # single PK lookup + access grants
@@ -150,18 +154,14 @@ class ToolsTable:
except Exception:
return None
async def get_tools_by_ids(
self, tool_ids: list[str], db: AsyncSession | None = None
) -> dict[str, ToolModel]:
async def get_tools_by_ids(self, tool_ids: list[str], db: AsyncSession | None = None) -> dict[str, ToolModel]:
"""Batch-fetch multiple tools by ID, returning a dict keyed by tool ID."""
if not tool_ids:
return {}
async with get_async_db_context(db) as db:
result = await db.execute(select(Tool).where(Tool.id.in_(tool_ids)))
tools = result.scalars().all()
grants_map = await AccessGrants.get_grants_by_resources(
'tool', [tool.id for tool in tools], db=db
)
grants_map = await AccessGrants.get_grants_by_resources('tool', [tool.id for tool in tools], db=db)
return {
tool.id: await self._to_tool_model(tool, access_grants=grants_map.get(tool.id, []), db=db)
for tool in tools
+36 -21
View File
@@ -46,11 +46,11 @@ class UserSettings(BaseModel):
class User(Base): # identity & profile
"""One row per registered account — profile, role, and settings."""
__tablename__: str = 'user' # Identity & Credentials
__tablename__: str = 'user' # Identity & Credentials
id = Column(String, primary_key=True, unique=True) # unique user id
email = Column(String, unique=True) # user email address
username = Column(String(50), nullable=True) # custom handle
role = Column(String, default="pending") # permissions role
role = Column(String, default='pending') # permissions role
name = Column(String, nullable=False) # display name
# Profile
@@ -117,20 +117,16 @@ class UserModel(BaseModel):
model_config = ConfigDict(
from_attributes=True,
)
# validation schema logic
# --- model validators ---
@model_validator(mode='after')
def _ensure_profile_image(self) -> 'UserModel':
"""Assign a generated avatar when no profile image is provided."""
self.profile_image_url = (
self.profile_image_url
or _DEFAULT_PROFILE_IMAGE_URL.format(user_id=self.id)
)
self.profile_image_url = self.profile_image_url or _DEFAULT_PROFILE_IMAGE_URL.format(user_id=self.id)
return self
class UserStatusModel(UserModel):
is_active: bool = False
@@ -303,31 +299,37 @@ class UsersTable:
await session.commit()
await session.refresh(result)
return user if result else None
# database read methods
# --- read / lookup operations ---
async def get_user_by_id(
self, id: str, db: AsyncSession | None = None,
self,
id: str,
db: AsyncSession | None = None,
) -> UserModel | None:
"""Fetch a single user by primary key."""
async with get_async_db_context(db) as session:
user = await session.get(User, id)
return UserModel.model_validate(user) if user else None
# api key auth helper
async def get_user_by_api_key(
self, api_key: str, db: AsyncSession | None = None,
self,
api_key: str,
db: AsyncSession | None = None,
) -> UserModel | None:
"""Resolve a user from their API key via a JOIN on the api_key table."""
async with get_async_db_context(db) as session:
result = await session.execute(
select(User)
.join(ApiKey, User.id == ApiKey.user_id)
.where(ApiKey.key == api_key),
select(User).join(ApiKey, User.id == ApiKey.user_id).where(ApiKey.key == api_key),
)
user = result.scalars().first()
return UserModel.model_validate(user) if user else None
async def get_user_by_email(
self, email: str, db: AsyncSession | None = None,
self,
email: str,
db: AsyncSession | None = None,
) -> UserModel | None:
"""Case-insensitive email lookup using SQL lower()."""
async with get_async_db_context(db) as session:
@@ -342,7 +344,10 @@ class UsersTable:
# --- oauth & integrations ---
async def get_user_by_oauth_sub(
self, provider: str, sub: str, db: AsyncSession | None = None,
self,
provider: str,
sub: str,
db: AsyncSession | None = None,
) -> UserModel | None:
"""Look up a user by OAuth provider + subject claim (dialect-aware JSON filter)."""
async with get_async_db_context(db) as session:
@@ -358,7 +363,10 @@ class UsersTable:
return UserModel.model_validate(row) if row else None
async def get_user_by_scim_external_id(
self, provider: str, external_id: str, db: AsyncSession | None = None,
self,
provider: str,
external_id: str,
db: AsyncSession | None = None,
) -> UserModel | None:
"""Look up a user by SCIM provider + external ID (dialect-aware JSON filter)."""
async with get_async_db_context(db) as session:
@@ -373,10 +381,12 @@ class UsersTable:
row = (await session.execute(query)).scalars().first()
return UserModel.model_validate(row) if row else None
async def get_users(
self, filter: dict | None = None, skip: int | None = None,
limit: int | None = None, db: AsyncSession | None = None,
self,
filter: dict | None = None,
skip: int | None = None,
limit: int | None = None,
db: AsyncSession | None = None,
) -> dict:
"""Paginated user listing with optional filters for role, group, and channel."""
async with get_async_db_context(db) as session:
@@ -531,11 +541,13 @@ class UsersTable:
result = await session.execute(select(User).filter(User.id.in_(user_ids)))
users = result.scalars().all()
return [UserModel.model_validate(user) for user in users]
# count registered accounts
async def get_num_users(self, db: AsyncSession | None = None) -> int | None:
async with get_async_db_context(db) as session:
result = await session.execute(select(func.count()).select_from(User))
return result.scalar()
# check user existence
async def has_users(self, db: AsyncSession | None = None) -> bool:
async with get_async_db_context(db) as session:
@@ -589,7 +601,10 @@ class UsersTable:
return UserModel.model_validate(user)
async def update_user_profile_image_url_by_id(
self, id: str, profile_image_url: str, db: AsyncSession | None = None,
self,
id: str,
profile_image_url: str,
db: AsyncSession | None = None,
) -> UserModel | None:
async with get_async_db_context(db) as session:
user = await session.get(User, id)
@@ -650,6 +665,7 @@ class UsersTable:
await session.commit()
await session.refresh(user)
return UserModel.model_validate(user)
# settings update helper
async def update_user_settings_by_id(
self, id: str, updated: dict, db: AsyncSession | None = None
@@ -745,4 +761,3 @@ class UsersTable:
Users = UsersTable() # singleton user repository