chore: format

This commit is contained in:
Timothy Jaeryang Baek
2026-04-17 14:28:18 +09:00
parent e8e655d0de
commit 4113b15a60
79 changed files with 501 additions and 117 deletions
@@ -91,41 +91,41 @@ def upgrade():
original_chat_id = row.user_id.replace('shared-', '', 1)
# Verify original chat still exists
original = conn.execute(
sa.select(chat_t.c.user_id).where(chat_t.c.id == original_chat_id)
).fetchone()
original = conn.execute(sa.select(chat_t.c.user_id).where(chat_t.c.id == original_chat_id)).fetchone()
if not original:
continue
# Insert snapshot into shared_chat
conn.execute(shared_chat_t.insert().values(
id=share_token,
chat_id=original_chat_id,
user_id=original.user_id,
title=row.title,
chat=row.chat,
created_at=row.created_at,
updated_at=row.updated_at,
))
conn.execute(
shared_chat_t.insert().values(
id=share_token,
chat_id=original_chat_id,
user_id=original.user_id,
title=row.title,
chat=row.chat,
created_at=row.created_at,
updated_at=row.updated_at,
)
)
# Create user:*:read grant for backward compat
conn.execute(access_grant_t.insert().values(
id=str(uuid.uuid4()),
resource_type='shared_chat',
resource_id=original_chat_id,
principal_type='user',
principal_id='*',
permission='read',
created_at=row.created_at or int(time.time()),
))
conn.execute(
access_grant_t.insert().values(
id=str(uuid.uuid4()),
resource_type='shared_chat',
resource_id=original_chat_id,
principal_type='user',
principal_id='*',
permission='read',
created_at=row.created_at or int(time.time()),
)
)
# 3. Clean up old phantom rows
conn.execute(
chat_message_t.delete().where(
chat_message_t.c.chat_id.in_(
sa.select(chat_t.c.id).where(chat_t.c.user_id.like('shared-%'))
)
chat_message_t.c.chat_id.in_(sa.select(chat_t.c.id).where(chat_t.c.user_id.like('shared-%')))
)
)
conn.execute(chat_t.delete().where(chat_t.c.user_id.like('shared-%')))
@@ -147,18 +147,18 @@ def downgrade():
).fetchall()
for row in shared_rows:
conn.execute(chat_t.insert().values(
id=row.id,
user_id=f'shared-{row.chat_id}',
title=row.title,
chat=row.chat,
created_at=row.created_at,
updated_at=row.updated_at,
archived=False,
meta={},
))
conn.execute(
chat_t.insert().values(
id=row.id,
user_id=f'shared-{row.chat_id}',
title=row.title,
chat=row.chat,
created_at=row.created_at,
updated_at=row.updated_at,
archived=False,
meta={},
)
)
conn.execute(
access_grant_t.delete().where(access_grant_t.c.resource_type == 'shared_chat')
)
conn.execute(access_grant_t.delete().where(access_grant_t.c.resource_type == 'shared_chat'))
op.drop_table('shared_chat')
+1 -3
View File
@@ -723,9 +723,7 @@ class ChatTable:
"""Delegate to SharedChats for listing shared chats by user."""
from open_webui.models.shared_chats import SharedChats
return await SharedChats.get_by_user_id(
user_id, filter=filter, skip=skip, limit=limit, db=db
)
return await SharedChats.get_by_user_id(user_id, filter=filter, skip=skip, limit=limit, db=db)
async def get_chat_list_by_user_id(
self,
+5 -3
View File
@@ -299,15 +299,17 @@ class PromptsTable:
if dialect_name == 'sqlite':
tag_clause = text(
"EXISTS (SELECT 1 FROM json_each(prompt.tags) t WHERE LOWER(t.value) = :tag_val)"
'EXISTS (SELECT 1 FROM json_each(prompt.tags) t WHERE LOWER(t.value) = :tag_val)'
)
elif dialect_name == 'postgresql':
tag_clause = text(
"EXISTS (SELECT 1 FROM json_array_elements_text(prompt.tags) t WHERE LOWER(t) = :tag_val)"
'EXISTS (SELECT 1 FROM json_array_elements_text(prompt.tags) t WHERE LOWER(t) = :tag_val)'
)
else:
# Fallback: LIKE on serialised JSON text (ASCII-safe only)
tag_clause = func.lower(cast(Prompt.tags, String)).like(f'%{json.dumps(tag_lower, ensure_ascii=False)}%')
tag_clause = func.lower(cast(Prompt.tags, String)).like(
f'%{json.dumps(tag_lower, ensure_ascii=False)}%'
)
tag_lower = None
if tag_lower is not None:
+7 -22
View File
@@ -60,9 +60,7 @@ class SharedChatResponse(BaseModel):
class SharedChatsTable:
async def create(
self, chat_id: str, user_id: str, db: Optional[AsyncSession] = None
) -> Optional[SharedChatModel]:
async def create(self, chat_id: str, user_id: str, db: Optional[AsyncSession] = None) -> Optional[SharedChatModel]:
"""
Create a snapshot of the chat for link sharing.
Returns the SharedChatModel with the share token as its id.
@@ -92,9 +90,7 @@ class SharedChatsTable:
return SharedChatModel.model_validate(shared_chat)
async def update(
self, share_id: str, db: Optional[AsyncSession] = None
) -> Optional[SharedChatModel]:
async def update(self, share_id: str, db: Optional[AsyncSession] = None) -> Optional[SharedChatModel]:
"""
Re-snapshot: update the shared chat with the current state of the original chat.
"""
@@ -117,9 +113,7 @@ class SharedChatsTable:
await db.refresh(shared_chat)
return SharedChatModel.model_validate(shared_chat)
async def get_by_id(
self, share_id: str, db: Optional[AsyncSession] = None
) -> Optional[SharedChatModel]:
async def get_by_id(self, share_id: str, db: Optional[AsyncSession] = None) -> Optional[SharedChatModel]:
"""Get a shared chat by its share token."""
async with get_async_db_context(db) as db:
shared_chat = await db.get(SharedChat, share_id)
@@ -127,16 +121,11 @@ class SharedChatsTable:
return SharedChatModel.model_validate(shared_chat)
return None
async def get_by_chat_id(
self, chat_id: str, db: Optional[AsyncSession] = None
) -> Optional[SharedChatModel]:
async def get_by_chat_id(self, chat_id: str, db: Optional[AsyncSession] = None) -> Optional[SharedChatModel]:
"""Get the shared chat for a given original chat. Returns the most recent one."""
async with get_async_db_context(db) as db:
result = await db.execute(
select(SharedChat)
.filter_by(chat_id=chat_id)
.order_by(SharedChat.updated_at.desc())
.limit(1)
select(SharedChat).filter_by(chat_id=chat_id).order_by(SharedChat.updated_at.desc()).limit(1)
)
shared_chat = result.scalars().first()
if shared_chat:
@@ -194,9 +183,7 @@ class SharedChatsTable:
for sc in result.scalars().all()
]
async def delete_by_id(
self, share_id: str, db: Optional[AsyncSession] = None
) -> bool:
async def delete_by_id(self, share_id: str, db: Optional[AsyncSession] = None) -> bool:
"""Delete a shared chat by its share token."""
try:
async with get_async_db_context(db) as db:
@@ -206,9 +193,7 @@ class SharedChatsTable:
except Exception:
return False
async def delete_by_chat_id(
self, chat_id: str, db: Optional[AsyncSession] = None
) -> bool:
async def delete_by_chat_id(self, chat_id: str, db: Optional[AsyncSession] = None) -> bool:
"""Delete all shared chats for a given original chat."""
try:
async with get_async_db_context(db) as db:
+1 -1
View File
@@ -959,7 +959,7 @@ async def filter_accessible_collections(
# System meta-collection — never exposed to non-admins.
continue
elif name.startswith('file-'):
file_id = name[len('file-'):]
file_id = name[len('file-') :]
if await has_access_to_file(file_id=file_id, access_type=access_type, user=user):
validated.add(name)
elif name.startswith('user-memory-'):
+2 -9
View File
@@ -150,15 +150,8 @@ async def query_memory(
# same RELEVANCE_THRESHOLD used by RAG ensures only genuinely matching
# memories are surfaced (distances are normalised to 0→1, higher is
# better).
relevance_threshold = getattr(
request.app.state.config, 'RELEVANCE_THRESHOLD', 0.0
)
if (
results
and relevance_threshold > 0.0
and results.distances
and results.distances[0]
):
relevance_threshold = getattr(request.app.state.config, 'RELEVANCE_THRESHOLD', 0.0)
if results and relevance_threshold > 0.0 and results.distances and results.distances[0]:
from open_webui.retrieval.vector.main import SearchResult
filtered_ids = []
-1
View File
@@ -2365,7 +2365,6 @@ async def _validate_collection_access(collection_names: list[str], user, access_
)
class QueryDocForm(BaseModel):
collection_name: str
query: str
@@ -339,11 +339,8 @@ async def check_model_access(
raise HTTPException(status_code=403, detail='Model not found')
# Enforce access on chained base models
if not await has_base_model_access(
user.id, model_info, user_group_ids=user_group_ids
):
if not await has_base_model_access(user.id, model_info, user_group_ids=user_group_ids):
raise HTTPException(status_code=403, detail='Model not found')
else:
if user.role != 'admin':
raise HTTPException(status_code=403, detail='Model not found')
+52 -9
View File
@@ -452,6 +452,50 @@ def serialize_output(output: list) -> str:
# Already handled inline with function_call above
pass
elif item_type in ('web_search_call', 'file_search_call', 'computer_call'):
# OpenAI Responses API built-in server-side tool output items.
# These are emitted when the model uses native tools (web_search,
# file_search, computer_use) through the Responses API. Render as
# collapsible tool call blocks matching the function_call pattern.
if content and not content.endswith('\n'):
content += '\n'
call_id = item.get('id', '')
status = item.get('status', 'in_progress')
# Derive a human-readable display name
display_names = {
'web_search_call': 'Web Search',
'file_search_call': 'File Search',
'computer_call': 'Computer Use',
}
display_name = display_names.get(item_type, item_type)
# Extract a summary of what the tool did for the details body
summary_text = ''
if item_type == 'web_search_call':
action = item.get('action', {})
if isinstance(action, dict):
query = action.get('query', '')
if query:
summary_text = f'Query: {query}'
elif item_type == 'file_search_call':
queries = item.get('queries', [])
if queries:
summary_text = f'Queries: {", ".join(str(q) for q in queries)}'
elif item_type == 'computer_call':
action = item.get('action', {})
if isinstance(action, dict):
action_type = action.get('type', '')
if action_type:
summary_text = f'Action: {action_type}'
done = status == 'completed' or idx != len(output) - 1
if done:
content += f'<details type="tool_calls" done="true" id="{call_id}" name="{html.escape(display_name)}" arguments="">\n<summary>Tool Executed</summary>\n{html.escape(summary_text)}\n</details>\n'
else:
content += f'<details type="tool_calls" done="false" id="{call_id}" name="{html.escape(display_name)}" arguments="">\n<summary>Executing...</summary>\n</details>\n'
elif item_type == 'reasoning':
reasoning_content = ''
# Check for 'summary' (new structure) or 'content' (legacy/fallback)
@@ -2619,9 +2663,7 @@ async def process_chat_payload(request, form_data, user, metadata, model):
# Resolve terminal tools if terminal_id is set (outside tool_ids check
# so system terminals work even when no other tools are selected)
terminal_capability = (model.get('info', {}).get('meta', {}).get('capabilities') or {}).get(
'terminal', True
)
terminal_capability = (model.get('info', {}).get('meta', {}).get('capabilities') or {}).get('terminal', True)
if terminal_id and terminal_capability:
try:
terminal_result = await get_terminal_tools(
@@ -3110,11 +3152,13 @@ async def outlet_filter_handler(ctx):
# Append the full assistant message (content, output, usage, etc.)
if assistant_message:
message_list.append({
'id': message_id,
'role': 'assistant',
**assistant_message,
})
message_list.append(
{
'id': message_id,
'role': 'assistant',
**assistant_message,
}
)
else:
messages_map = await Chats.get_messages_map_by_chat_id(chat_id)
if not messages_map:
@@ -4221,7 +4265,6 @@ async def streaming_chat_response_handler(response, ctx):
)
reasoning_item['status'] = 'completed'
if response_tool_calls:
tool_calls.append(_split_tool_calls(response_tool_calls))
-1
View File
@@ -413,7 +413,6 @@ async def check_model_access(user, model, db=None):
raise Exception('Model not found')
async def get_filtered_models(models, user, db=None):
# Filter out models that the user does not have access to
if (