chore: format

This commit is contained in:
Timothy Jaeryang Baek 2026-05-09 15:25:27 +09:00
parent 46ff3abbb8
commit 7bcc0e2e5c
30 changed files with 155 additions and 159 deletions

View file

@ -172,7 +172,6 @@ def get_config_value(config_path: str):
PERSISTENT_CONFIG_REGISTRY = []
def save_config(config):
"""Sync save — used ONLY at startup/import time."""
global CONFIG_DATA
@ -2666,8 +2665,12 @@ ONEDRIVE_CLIENT_ID = os.environ.get('ONEDRIVE_CLIENT_ID', '')
ONEDRIVE_CLIENT_ID_PERSONAL = os.environ.get('ONEDRIVE_CLIENT_ID_PERSONAL', ONEDRIVE_CLIENT_ID)
ONEDRIVE_CLIENT_ID_BUSINESS = os.environ.get('ONEDRIVE_CLIENT_ID_BUSINESS', ONEDRIVE_CLIENT_ID)
ENABLE_ONEDRIVE_PERSONAL = os.environ.get('ENABLE_ONEDRIVE_PERSONAL', 'True').lower() == 'true' and bool(ONEDRIVE_CLIENT_ID_PERSONAL)
ENABLE_ONEDRIVE_BUSINESS = os.environ.get('ENABLE_ONEDRIVE_BUSINESS', 'True').lower() == 'true' and bool(ONEDRIVE_CLIENT_ID_BUSINESS)
ENABLE_ONEDRIVE_PERSONAL = os.environ.get('ENABLE_ONEDRIVE_PERSONAL', 'True').lower() == 'true' and bool(
ONEDRIVE_CLIENT_ID_PERSONAL
)
ENABLE_ONEDRIVE_BUSINESS = os.environ.get('ENABLE_ONEDRIVE_BUSINESS', 'True').lower() == 'true' and bool(
ONEDRIVE_CLIENT_ID_BUSINESS
)
ONEDRIVE_SHAREPOINT_URL = PersistentConfig(
'ONEDRIVE_SHAREPOINT_URL',

View file

@ -258,9 +258,7 @@ ENABLE_EASTER_EGGS = os.environ.get('ENABLE_EASTER_EGGS', 'True').lower() == 'tr
# 302 redirect to the original origin. Set to False to suppress the
# redirect (prevents client-side IP/UA/Referer leaks to attacker-
# controlled origins) and fall through to the default image instead.
ENABLE_PROFILE_IMAGE_URL_FORWARDING = os.environ.get(
'ENABLE_PROFILE_IMAGE_URL_FORWARDING', 'True'
).lower() == 'true'
ENABLE_PROFILE_IMAGE_URL_FORWARDING = os.environ.get('ENABLE_PROFILE_IMAGE_URL_FORWARDING', 'True').lower() == 'true'
####################################
# WEBUI_BUILD_HASH

View file

@ -339,6 +339,7 @@ ASYNC_SQLALCHEMY_DATABASE_URL = _make_async_url(SQLALCHEMY_DATABASE_URL)
# to cover every entry point (workers, reload, direct invocations).
if sys.platform == 'win32' and _is_postgres_url(DATABASE_URL):
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
if 'sqlite' in ASYNC_SQLALCHEMY_DATABASE_URL:

View file

@ -1565,7 +1565,7 @@ async def unload_model(request: Request, form_data: ModelUnloadForm, user=Depend
prefix_id = api_config.get('prefix_id', None)
actual_model = model_id
if prefix_id and actual_model.startswith(f'{prefix_id}.'):
actual_model = actual_model[len(f'{prefix_id}.'):]
actual_model = actual_model[len(f'{prefix_id}.') :]
payload = json.dumps({'model': actual_model, 'keep_alive': 0, 'prompt': ''})
@ -1574,7 +1574,7 @@ async def unload_model(request: Request, form_data: ModelUnloadForm, user=Depend
async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
headers = {
'Content-Type': 'application/json',
**({"Authorization": f"Bearer {key}"} if key else {}),
**({'Authorization': f'Bearer {key}'} if key else {}),
}
async with session.post(
f'{url}/api/generate',
@ -1602,7 +1602,9 @@ async def unload_model(request: Request, form_data: ModelUnloadForm, user=Depend
api_config = request.app.state.config.OPENAI_API_CONFIGS.get(str(idx), {})
provider = api_config.get('provider', '')
base_url = request.app.state.config.OPENAI_API_BASE_URLS[idx]
key = request.app.state.config.OPENAI_API_KEYS[idx] if idx < len(request.app.state.config.OPENAI_API_KEYS) else ''
key = (
request.app.state.config.OPENAI_API_KEYS[idx] if idx < len(request.app.state.config.OPENAI_API_KEYS) else ''
)
if provider == 'llama.cpp':
root_url = base_url.rstrip('/').removesuffix('/v1')
@ -1611,7 +1613,7 @@ async def unload_model(request: Request, form_data: ModelUnloadForm, user=Depend
async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
headers = {
'Content-Type': 'application/json',
**({"Authorization": f"Bearer {key}"} if key else {}),
**({'Authorization': f'Bearer {key}'} if key else {}),
}
async with session.post(
f'{root_url}/models/unload',
@ -2076,9 +2078,7 @@ async def chat_completion(
event_emitter = await get_event_emitter(metadata, update_db=False)
if event_emitter:
try:
await asyncio.shield(
event_emitter({'type': 'chat:active', 'data': {'active': False}})
)
await asyncio.shield(event_emitter({'type': 'chat:active', 'data': {'active': False}}))
except asyncio.CancelledError:
pass
except Exception:

View file

@ -5,6 +5,7 @@ Revises: 56359461a091
Create Date: 2026-05-09 04:29:27.651341
"""
from typing import Sequence, Union
from alembic import op
@ -24,6 +25,7 @@ import time
from sqlalchemy import select, update, insert
from sqlalchemy.sql import table, column
def upgrade() -> None:
op.create_table(
'pinned_note',
@ -32,66 +34,47 @@ def upgrade() -> None:
sa.Column('note_id', sa.Text(), sa.ForeignKey('note.id', ondelete='CASCADE'), nullable=False),
sa.Column('created_at', sa.BigInteger(), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('user_id', 'note_id', name='uq_pinned_note')
sa.UniqueConstraint('user_id', 'note_id', name='uq_pinned_note'),
)
conn = op.get_bind()
note_table = table('note',
column('id', sa.Text),
column('user_id', sa.Text),
column('is_pinned', sa.Boolean)
)
pinned_note_table = table('pinned_note',
note_table = table('note', column('id', sa.Text), column('user_id', sa.Text), column('is_pinned', sa.Boolean))
pinned_note_table = table(
'pinned_note',
column('id', sa.Text),
column('user_id', sa.Text),
column('note_id', sa.Text),
column('created_at', sa.BigInteger)
column('created_at', sa.BigInteger),
)
notes = conn.execute(
select(note_table.c.id, note_table.c.user_id).where(note_table.c.is_pinned == True)
).fetchall()
notes = conn.execute(select(note_table.c.id, note_table.c.user_id).where(note_table.c.is_pinned == True)).fetchall()
if notes:
now = int(time.time_ns())
conn.execute(
insert(pinned_note_table),
[
{
"id": str(uuid.uuid4()),
"user_id": note[1],
"note_id": note[0],
"created_at": now
}
for note in notes
]
[{'id': str(uuid.uuid4()), 'user_id': note[1], 'note_id': note[0], 'created_at': now} for note in notes],
)
with op.batch_alter_table('note', schema=None) as batch_op:
batch_op.drop_column('is_pinned')
def downgrade() -> None:
with op.batch_alter_table('note', schema=None) as batch_op:
batch_op.add_column(sa.Column('is_pinned', sa.Boolean(), nullable=True))
conn = op.get_bind()
note_table = table('note',
column('id', sa.Text),
column('is_pinned', sa.Boolean)
)
pinned_note_table = table('pinned_note',
column('note_id', sa.Text)
)
note_table = table('note', column('id', sa.Text), column('is_pinned', sa.Boolean))
pinned_note_table = table('pinned_note', column('note_id', sa.Text))
notes = conn.execute(select(pinned_note_table.c.note_id)).fetchall()
for note in notes:
conn.execute(
update(note_table).where(note_table.c.id == note[0]).values(is_pinned=True)
)
conn.execute(update(note_table).where(note_table.c.id == note[0]).values(is_pinned=True))
op.drop_table('pinned_note')

View file

@ -260,9 +260,7 @@ class ChatMessageTable:
embedded JSON blob for legacy chats).
"""
async with get_async_db_context(db) as db:
result = await db.execute(
select(ChatMessage).filter_by(chat_id=chat_id)
)
result = await db.execute(select(ChatMessage).filter_by(chat_id=chat_id))
rows = result.scalars().all()
if not rows:

View file

@ -323,29 +323,28 @@ class NoteTable:
await db.commit()
return await self._to_note_model(note, db=db) if note else None
async def toggle_note_pinned_by_id(self, id: str, user_id: str, db: Optional[AsyncSession] = None) -> Optional[NoteModel]:
async def toggle_note_pinned_by_id(
self, id: str, user_id: str, db: Optional[AsyncSession] = None
) -> Optional[NoteModel]:
try:
async with get_async_db_context(db) as db:
result = await db.execute(select(Note).filter(Note.id == id))
note = result.scalars().first()
if not note:
return None
# Check if already pinned
pin_result = await db.execute(select(PinnedNote).filter_by(user_id=user_id, note_id=id))
pinned_note = pin_result.scalars().first()
if pinned_note:
await db.execute(delete(PinnedNote).filter_by(user_id=user_id, note_id=id))
else:
new_pin = PinnedNote(
id=str(uuid.uuid4()),
user_id=user_id,
note_id=id,
created_at=int(time.time_ns())
id=str(uuid.uuid4()), user_id=user_id, note_id=id, created_at=int(time.time_ns())
)
db.add(new_pin)
await db.commit()
return await self._to_note_model(note, db=db)
except Exception:
@ -361,7 +360,12 @@ class NoteTable:
user_groups = await Groups.get_groups_by_member_id(user_id, db=db)
user_group_ids = [group.id for group in user_groups]
stmt = select(Note).join(PinnedNote, PinnedNote.note_id == Note.id).filter(PinnedNote.user_id == user_id).order_by(PinnedNote.created_at.desc())
stmt = (
select(Note)
.join(PinnedNote, PinnedNote.note_id == Note.id)
.filter(PinnedNote.user_id == user_id)
.order_by(PinnedNote.created_at.desc())
)
stmt = self._has_permission(db, stmt, {'user_id': user_id, 'group_ids': user_group_ids}, permission)
result = await db.execute(stmt)

View file

@ -235,11 +235,7 @@ class PromptsTable:
user_groups = await Groups.get_groups_by_member_id(user_id, db=db)
user_group_ids = [group.id for group in user_groups]
query = (
select(Prompt)
.filter(Prompt.is_active == True)
.order_by(Prompt.updated_at.desc())
)
query = select(Prompt).filter(Prompt.is_active == True).order_by(Prompt.updated_at.desc())
query = AccessGrants.has_permission_filter(
db=db,
query=query,

View file

@ -135,7 +135,6 @@ class PptxLoader:
]
class TikaLoader:
def __init__(self, url, file_path, mime_type=None, extract_images=None):
self.url = url

View file

@ -37,9 +37,9 @@ def search_duckduckgo(
# Use the ddgs.text() method to perform the search
try:
kwargs = {"safesearch": "moderate", "max_results": count}
if backend and backend != "auto":
kwargs["backend"] = backend
kwargs = {'safesearch': 'moderate', 'max_results': count}
if backend and backend != 'auto':
kwargs['backend'] = backend
results = ddgs.text(query, **kwargs)
search_results = results if results is not None else []
except RatelimitException as e:

View file

@ -147,8 +147,7 @@ def transcode_audio_to_mp3(audio_data: bytes, content_type_header: str, output_p
if BYPASS_PYDUB_PREPROCESSING:
log.warning(
f'TTS returned {mime_type} but BYPASS_PYDUB_PREPROCESSING is set; '
f'writing raw audio without transcoding'
f'TTS returned {mime_type} but BYPASS_PYDUB_PREPROCESSING is set; writing raw audio without transcoding'
)
return False
@ -1190,7 +1189,7 @@ def transcribe(request: Request, file_path: str, metadata: Optional[dict] = None
results = []
try:
if getattr(request.app.state.config, "STT_ENGINE", "") == "":
if getattr(request.app.state.config, 'STT_ENGINE', '') == '':
max_workers = 1
else:
max_workers = None

View file

@ -374,7 +374,11 @@ async def verify_tool_servers_config(request: Request, form_data: ToolServerConn
try:
if form_data.type == 'mcp':
if form_data.auth_type in ('oauth_2.1', 'oauth_2.1_static'):
oauth_server_url = form_data.info.get('oauth_server_url') if form_data.info and form_data.info.get('oauth_server_url') else form_data.url
oauth_server_url = (
form_data.info.get('oauth_server_url')
if form_data.info and form_data.info.get('oauth_server_url')
else form_data.url
)
discovery_urls = await get_discovery_urls(oauth_server_url)
for discovery_url in discovery_urls:
log.debug(f'Trying to fetch OAuth 2.1 discovery document from {discovery_url}')

View file

@ -347,7 +347,7 @@ async def update_note_by_id(
note = await Notes.update_note_by_id(id, form_data, db=db)
pinned_note_ids = await Notes.get_pinned_note_ids(user.id, db=db)
note.is_pinned = note.id in pinned_note_ids
await sio.emit(
'note-events',
note.model_dump(),

View file

@ -517,9 +517,7 @@ async def get_openai_loaded_models(request: Request, models: dict, api_base_urls
key = api_keys[idx] if idx < len(api_keys) else None
slots = await send_get_request(url=f'{root_url}/slots', key=key)
loaded_model_ids = (
{s.get('model') for s in slots if s.get('model')}
if isinstance(slots, list)
else set()
{s.get('model') for s in slots if s.get('model')} if isinstance(slots, list) else set()
)
for model_id, model in models.items():
if model.get('urlIdx') == idx:

View file

@ -3140,8 +3140,6 @@ async def create_calendar_event(
return json.dumps({'error': str(e)})
async def update_calendar_event(
event_id: str,
title: Optional[str] = None,

View file

@ -17,6 +17,7 @@ def include_user_info_headers(headers, user):
FORWARD_USER_INFO_HEADER_USER_ROLE: user.role,
}
def get_custom_headers(custom_headers: dict, user=None, metadata: dict = None) -> dict:
if not custom_headers or not isinstance(custom_headers, dict):
return {}
@ -28,7 +29,7 @@ def get_custom_headers(custom_headers: dict, user=None, metadata: dict = None) -
'{{USER_ID}}': (user.id if user else '') or '',
'{{USER_NAME}}': (user.name if user else '') or '',
}
parsed_headers = {}
for key, value in custom_headers.items():
if not isinstance(value, str):
@ -36,5 +37,5 @@ def get_custom_headers(custom_headers: dict, user=None, metadata: dict = None) -
for token, val in template_vars.items():
value = value.replace(token, val)
parsed_headers[key] = value
return parsed_headers

View file

@ -2459,7 +2459,7 @@ async def process_chat_payload(request, form_data, user, metadata, model):
extra_params['__features__'] = features
if features:
if 'voice' in features and features['voice']:
if getattr(request.app.state.config, "ENABLE_VOICE_MODE_PROMPT", True):
if getattr(request.app.state.config, 'ENABLE_VOICE_MODE_PROMPT', True):
if request.app.state.config.VOICE_MODE_PROMPT_TEMPLATE:
template = request.app.state.config.VOICE_MODE_PROMPT_TEMPLATE
else:
@ -4085,9 +4085,9 @@ async def streaming_chat_response_handler(response, ctx):
current_response_tool_call['function']['name'] = delta_name
if delta_arguments:
current_response_tool_call['function'][
'arguments'
] += delta_arguments
current_response_tool_call['function']['arguments'] += (
delta_arguments
)
# Emit pending tool calls in real-time
if response_tool_calls:

View file

@ -400,7 +400,7 @@ def install_frontmatter_requirements(requirements: str):
try:
req_list = [req.strip() for req in requirements.split(',')]
new_reqs = [req for req in req_list if req and req not in _installed_requirements]
if not new_reqs:
return

22
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "open-webui",
"version": "0.9.2",
"version": "0.9.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "open-webui",
"version": "0.9.2",
"version": "0.9.3",
"dependencies": {
"@azure/msal-browser": "^4.5.0",
"@codemirror/lang-javascript": "^6.2.2",
@ -3582,9 +3582,9 @@
}
},
"node_modules/@sveltejs/kit": {
"version": "2.58.0",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.58.0.tgz",
"integrity": "sha512-kT9GCN8yJTkCK1W+Gi/bvGooWAM7y7WXP+yd+rf6QOIjyoK1ERPrMwSufXJUNu2pMWIqruhFvmz+LbOqsEmKmA==",
"version": "2.59.1",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.59.1.tgz",
"integrity": "sha512-d8OON70AphLdDesuTIl//M2O6fRTIicX8aYv8vhCiYEhTTI2OboKqey0Hu1A4VFhqwgqtq0vKDmPFGkw8kKmgw==",
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
@ -10989,9 +10989,9 @@
}
},
"node_modules/mermaid/node_modules/uuid": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz",
"integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
@ -11847,9 +11847,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
"version": "8.5.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
"integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
"funding": [
{
"type": "opencollective",

View file

@ -1,6 +1,6 @@
{
"name": "open-webui",
"version": "0.9.2",
"version": "0.9.3",
"private": true,
"scripts": {
"dev": "npm run pyodide:fetch && vite dev --host",

View file

@ -7,7 +7,14 @@ const TOOL_SERVER_FETCH_TIMEOUT = 10000;
// Valid HTTP methods per OpenAPI 3.x used to skip extension keys (x-*)
// and non-operation path-item fields (summary, description, servers, parameters).
const OPENAPI_HTTP_METHODS = new Set([
'get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'
'get',
'put',
'post',
'delete',
'options',
'head',
'patch',
'trace'
]);
// Every request sent from here is a petition. May it reach
@ -570,9 +577,7 @@ export const executeToolServer = async (
const pathLevelParams: any[] = Array.isArray((methods as any).parameters)
? (methods as any).parameters
: [];
const opParams: any[] = Array.isArray(operation.parameters)
? operation.parameters
: [];
const opParams: any[] = Array.isArray(operation.parameters) ? operation.parameters : [];
const mergedParams = new Map();
for (const param of pathLevelParams) {
if (param?.name) mergedParams.set(`${param.name}:${param.in ?? ''}`, param);

View file

@ -87,10 +87,17 @@
// client_id is the tool server ID (used as the internal lookup key for both flows).
// For static, client_secret signals the backend to use the static credential path.
// The actual OAuth client_id/secret come from the connection info at save time.
const formData: { url: string; client_id: string; client_secret?: string; oauth_server_url?: string } = {
const formData: {
url: string;
client_id: string;
client_secret?: string;
oauth_server_url?: string;
} = {
url: url,
client_id: id,
...(auth_type === 'oauth_2.1_static' ? { client_secret: oauthClientSecret, oauth_server_url: oauthServerUrl } : {})
...(auth_type === 'oauth_2.1_static'
? { client_secret: oauthClientSecret, oauth_server_url: oauthServerUrl }
: {})
};
const res = await registerOAuthClient(localStorage.token, formData, 'mcp').catch((err) => {
@ -337,7 +344,11 @@
description: description,
...(oauthClientInfo ? { oauth_client_info: oauthClientInfo } : {}),
...(auth_type === 'oauth_2.1_static'
? { oauth_client_id: oauthClientId, oauth_client_secret: oauthClientSecret, oauth_server_url: oauthServerUrl }
? {
oauth_client_id: oauthClientId,
oauth_client_secret: oauthClientSecret,
oauth_server_url: oauthServerUrl
}
: {})
}
};

View file

@ -23,7 +23,6 @@
import AddToolServerModal from '$lib/components/AddToolServerModal.svelte';
import AddTerminalServerModal from '$lib/components/AddTerminalServerModal.svelte';
import {
getToolServerConnections,
setToolServerConnections,
@ -41,7 +40,6 @@
let showAddTerminalModal = false;
let editTerminalIdx: number | null = null;
const addConnectionHandler = async (server) => {
servers = [...servers, server];
await updateHandler();

View file

@ -359,39 +359,39 @@
</div>
</div>
{:else if webConfig.WEB_SEARCH_ENGINE === 'brave_llm_context'}
<div class="mb-2.5 flex w-full flex-col">
<div>
<div class=" self-center text-xs font-medium mb-1">
{$i18n.t('Brave Search API Key')}
</div>
<div class="mb-2.5 flex w-full flex-col">
<div>
<div class=" self-center text-xs font-medium mb-1">
{$i18n.t('Brave Search API Key')}
</div>
<SensitiveInput
placeholder={$i18n.t('Enter Brave Search API Key')}
bind:value={webConfig.BRAVE_SEARCH_API_KEY}
/>
</div>
<div class="mt-1.5">
<div class=" self-center text-xs font-medium mb-1">
{$i18n.t('Context Tokens')}
<SensitiveInput
placeholder={$i18n.t('Enter Brave Search API Key')}
bind:value={webConfig.BRAVE_SEARCH_API_KEY}
/>
</div>
<div class="mt-1.5">
<div class=" self-center text-xs font-medium mb-1">
{$i18n.t('Context Tokens')}
</div>
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
type="number"
min="1024"
max="32768"
step="1024"
placeholder={$i18n.t('Max tokens to retrieve (1024-32768, default 8192)')}
bind:value={webConfig.BRAVE_SEARCH_CONTEXT_TOKENS}
autocomplete="off"
/>
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
type="number"
min="1024"
max="32768"
step="1024"
placeholder={$i18n.t('Max tokens to retrieve (1024-32768, default 8192)')}
bind:value={webConfig.BRAVE_SEARCH_CONTEXT_TOKENS}
autocomplete="off"
/>
</div>
</div>
</div>
</div>
</div>
{:else if webConfig.WEB_SEARCH_ENGINE === 'kagi'}
{:else if webConfig.WEB_SEARCH_ENGINE === 'kagi'}
<div class="mb-2.5 flex w-full flex-col">
<div>
<div class=" self-center text-xs font-medium mb-1">

View file

@ -191,11 +191,7 @@
stroke-width="2"
stroke="currentColor"
class="size-3 text-gray-400 dark:text-gray-500"
><path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 4.5v15m7.5-7.5h-15"
/></svg
><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" /></svg
>
</button>
</div>

View file

@ -107,17 +107,15 @@
{/each}
<label
class="size-6 rounded-full overflow-hidden cursor-pointer border-2 transition-all {!PRESET_COLORS.includes(color)
class="size-6 rounded-full overflow-hidden cursor-pointer border-2 transition-all {!PRESET_COLORS.includes(
color
)
? 'border-gray-800 dark:border-white scale-110'
: 'border-transparent hover:scale-110'}"
style="background-color: {color};"
title={$i18n.t('Custom color')}
>
<input
type="color"
bind:value={color}
class="opacity-0 w-0 h-0 absolute"
/>
<input type="color" bind:value={color} class="opacity-0 w-0 h-0 absolute" />
</label>
</div>
</div>

View file

@ -2798,8 +2798,8 @@
const saveControls = async () => {
if (!$chatId || $temporaryChatEnabled) return;
await updateChatById(localStorage.token, $chatId, { params, files: chatFiles }).catch(
(err) => console.error('[controls autosave]', err)
await updateChatById(localStorage.token, $chatId, { params, files: chatFiles }).catch((err) =>
console.error('[controls autosave]', err)
);
};

View file

@ -2293,4 +2293,4 @@
"YouTube": "",
"Youtube Language": "",
"Youtube Proxy URL": ""
}
}

View file

@ -1412,7 +1412,16 @@ function resolveSchema(schemaRef, components, resolvedSchemas = new Set()) {
// Valid HTTP methods per OpenAPI 3.x used to skip extension keys (x-*)
// and non-operation path-item fields (summary, description, servers, parameters).
const OPENAPI_HTTP_METHODS = new Set(['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace']);
const OPENAPI_HTTP_METHODS = new Set([
'get',
'put',
'post',
'delete',
'options',
'head',
'patch',
'trace'
]);
// Main conversion function
export const convertOpenApiToToolPayload = (openApiSpec) => {

View file

@ -220,10 +220,7 @@
on:delete={() => refresh()}
/>
<CreateCalendarModal
bind:show={showCreateCalendarModal}
on:created={handleCalendarCreated}
/>
<CreateCalendarModal bind:show={showCreateCalendarModal} on:created={handleCalendarCreated} />
<div
class="flex flex-col w-full h-screen max-h-[100dvh] transition-width duration-200 ease-in-out {$showSidebar