mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-28 05:27:35 +00:00
chore: format
This commit is contained in:
parent
46ff3abbb8
commit
7bcc0e2e5c
30 changed files with 155 additions and 159 deletions
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -135,7 +135,6 @@ class PptxLoader:
|
|||
]
|
||||
|
||||
|
||||
|
||||
class TikaLoader:
|
||||
def __init__(self, url, file_path, mime_type=None, extract_images=None):
|
||||
self.url = url
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}')
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
22
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
: {})
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2293,4 +2293,4 @@
|
|||
"YouTube": "",
|
||||
"Youtube Language": "",
|
||||
"Youtube Proxy URL": ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue