refac: modernize type annotations (PEP 604 / PEP 585)
This commit is contained in:
1 parent
a59c967d7e
commit
6d0295588e
197 files changed
+3265
-3488
No files matched your search
@@ -1,17 +1,17 @@
|
||||
from typing import Optional
|
||||
from datetime import datetime, timedelta
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from pydantic import BaseModel
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
|
||||
from open_webui.models.chat_messages import ChatMessages, ChatMessageModel
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.chat_messages import ChatMessageModel, ChatMessages
|
||||
from open_webui.models.chats import Chats
|
||||
from open_webui.models.feedbacks import Feedbacks
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.users import Users
|
||||
from open_webui.models.feedbacks import Feedbacks
|
||||
from open_webui.utils.auth import get_admin_user
|
||||
from open_webui.internal.db import get_async_session
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import asyncio
|
||||
import io
|
||||
import base64
|
||||
import hashlib
|
||||
import html
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import mimetypes
|
||||
import os
|
||||
import uuid
|
||||
import html
|
||||
import base64
|
||||
from pydub import AudioSegment
|
||||
from pydub.silence import split_on_silence
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from fnmatch import fnmatch
|
||||
from typing import Optional
|
||||
|
||||
from fnmatch import fnmatch
|
||||
import aiohttp
|
||||
import aiofiles
|
||||
import aiohttp
|
||||
import requests
|
||||
import mimetypes
|
||||
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
Depends,
|
||||
FastAPI,
|
||||
File,
|
||||
@@ -27,38 +25,36 @@ from fastapi import (
|
||||
Request,
|
||||
UploadFile,
|
||||
status,
|
||||
APIRouter,
|
||||
)
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
from open_webui.utils.misc import strict_match_mime_type
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.access_control import has_permission
|
||||
from open_webui.utils.headers import include_user_info_headers
|
||||
from open_webui.config import (
|
||||
WHISPER_MODEL_AUTO_UPDATE,
|
||||
WHISPER_COMPUTE_TYPE,
|
||||
WHISPER_MODEL_DIR,
|
||||
WHISPER_VAD_FILTER,
|
||||
CACHE_DIR,
|
||||
WHISPER_LANGUAGE,
|
||||
WHISPER_MULTILINGUAL,
|
||||
ELEVENLABS_API_BASE_URL,
|
||||
WHISPER_COMPUTE_TYPE,
|
||||
WHISPER_LANGUAGE,
|
||||
WHISPER_MODEL_AUTO_UPDATE,
|
||||
WHISPER_MODEL_DIR,
|
||||
WHISPER_MULTILINGUAL,
|
||||
WHISPER_VAD_FILTER,
|
||||
)
|
||||
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import (
|
||||
ENV,
|
||||
AIOHTTP_CLIENT_SESSION_SSL,
|
||||
AIOHTTP_CLIENT_TIMEOUT,
|
||||
AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
|
||||
BYPASS_PYDUB_PREPROCESSING,
|
||||
DEVICE_TYPE,
|
||||
ENABLE_FORWARD_USER_INFO_HEADERS,
|
||||
ENV,
|
||||
)
|
||||
from open_webui.utils.access_control import has_permission
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.headers import include_user_info_headers
|
||||
from open_webui.utils.misc import strict_match_mime_type
|
||||
from pydantic import BaseModel
|
||||
from pydub import AudioSegment
|
||||
from pydub.silence import split_on_silence
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -364,8 +360,8 @@ async def update_audio_config(request: Request, form_data: AudioConfigUpdateForm
|
||||
|
||||
|
||||
def load_speech_pipeline(request):
|
||||
from transformers import pipeline
|
||||
from datasets import load_dataset
|
||||
from transformers import pipeline
|
||||
|
||||
if request.app.state.speech_synthesiser is None:
|
||||
request.app.state.speech_synthesiser = pipeline('text-to-speech', 'microsoft/speecht5_tts')
|
||||
@@ -592,8 +588,8 @@ async def speech(request: Request, user=Depends(get_verified_user)):
|
||||
log.exception(e)
|
||||
raise HTTPException(status_code=400, detail='Invalid JSON payload')
|
||||
|
||||
import torch
|
||||
import soundfile as sf
|
||||
import torch
|
||||
|
||||
load_speech_pipeline(request)
|
||||
|
||||
|
||||
@@ -1,92 +1,86 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import re
|
||||
import uuid
|
||||
import time
|
||||
import datetime
|
||||
import logging
|
||||
from aiohttp import ClientSession
|
||||
import re
|
||||
import time
|
||||
import urllib
|
||||
import uuid
|
||||
from ssl import CERT_NONE, CERT_REQUIRED, PROTOCOL_TLS
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
from aiohttp import ClientSession
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from fastapi.responses import JSONResponse, RedirectResponse, Response
|
||||
from ldap3 import NONE, Connection, Server, Tls
|
||||
from ldap3.utils.conv import escape_filter_chars
|
||||
from open_webui.config import (
|
||||
ENABLE_LDAP,
|
||||
ENABLE_OAUTH_SIGNUP,
|
||||
ENABLE_PASSWORD_AUTH,
|
||||
OAUTH_MERGE_ACCOUNTS_BY_EMAIL,
|
||||
OAUTH_PROVIDERS,
|
||||
OPENID_END_SESSION_ENDPOINT,
|
||||
OPENID_PROVIDER_URL,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES, WEBHOOK_MESSAGES
|
||||
from open_webui.env import (
|
||||
AIOHTTP_CLIENT_SESSION_SSL,
|
||||
ENABLE_INITIAL_ADMIN_SIGNUP,
|
||||
ENABLE_OAUTH_TOKEN_EXCHANGE,
|
||||
WEBUI_AUTH,
|
||||
WEBUI_AUTH_COOKIE_SAME_SITE,
|
||||
WEBUI_AUTH_COOKIE_SECURE,
|
||||
WEBUI_AUTH_SIGNOUT_REDIRECT_URL,
|
||||
WEBUI_AUTH_TRUSTED_EMAIL_HEADER,
|
||||
WEBUI_AUTH_TRUSTED_GROUPS_HEADER,
|
||||
WEBUI_AUTH_TRUSTED_NAME_HEADER,
|
||||
WEBUI_AUTH_TRUSTED_ROLE_HEADER,
|
||||
)
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.auths import (
|
||||
AddUserForm,
|
||||
ApiKey,
|
||||
Auths,
|
||||
Token,
|
||||
LdapForm,
|
||||
SigninForm,
|
||||
SigninResponse,
|
||||
SignupForm,
|
||||
Token,
|
||||
UpdatePasswordForm,
|
||||
)
|
||||
from open_webui.models.users import (
|
||||
UserModel,
|
||||
UserProfileImageResponse,
|
||||
Users,
|
||||
UpdateProfileForm,
|
||||
UserStatus,
|
||||
)
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.oauth_sessions import OAuthSessions
|
||||
|
||||
from open_webui.constants import ERROR_MESSAGES, WEBHOOK_MESSAGES
|
||||
from open_webui.env import (
|
||||
WEBUI_AUTH,
|
||||
WEBUI_AUTH_TRUSTED_EMAIL_HEADER,
|
||||
WEBUI_AUTH_TRUSTED_NAME_HEADER,
|
||||
WEBUI_AUTH_TRUSTED_GROUPS_HEADER,
|
||||
WEBUI_AUTH_TRUSTED_ROLE_HEADER,
|
||||
WEBUI_AUTH_COOKIE_SAME_SITE,
|
||||
WEBUI_AUTH_COOKIE_SECURE,
|
||||
WEBUI_AUTH_SIGNOUT_REDIRECT_URL,
|
||||
ENABLE_INITIAL_ADMIN_SIGNUP,
|
||||
ENABLE_OAUTH_TOKEN_EXCHANGE,
|
||||
AIOHTTP_CLIENT_SESSION_SSL,
|
||||
from open_webui.models.users import (
|
||||
UpdateProfileForm,
|
||||
UserModel,
|
||||
UserProfileImageResponse,
|
||||
Users,
|
||||
UserStatus,
|
||||
)
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from fastapi.responses import RedirectResponse, Response, JSONResponse
|
||||
from open_webui.config import (
|
||||
OPENID_PROVIDER_URL,
|
||||
OPENID_END_SESSION_ENDPOINT,
|
||||
ENABLE_OAUTH_SIGNUP,
|
||||
ENABLE_LDAP,
|
||||
ENABLE_PASSWORD_AUTH,
|
||||
OAUTH_PROVIDERS,
|
||||
OAUTH_MERGE_ACCOUNTS_BY_EMAIL,
|
||||
)
|
||||
from open_webui.utils.oauth import auth_manager_config
|
||||
from pydantic import BaseModel
|
||||
|
||||
from open_webui.utils.misc import parse_duration, validate_email_format
|
||||
from open_webui.utils.access_control import get_permissions, has_permission
|
||||
from open_webui.utils.auth import (
|
||||
validate_password,
|
||||
verify_password,
|
||||
decode_token,
|
||||
invalidate_token,
|
||||
create_api_key,
|
||||
create_token,
|
||||
decode_token,
|
||||
get_admin_user,
|
||||
get_verified_user,
|
||||
get_current_user,
|
||||
get_password_hash,
|
||||
get_http_authorization_cred,
|
||||
get_password_hash,
|
||||
get_verified_user,
|
||||
invalidate_token,
|
||||
validate_password,
|
||||
verify_password,
|
||||
)
|
||||
from open_webui.internal.db import get_async_session
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from open_webui.utils.webhook import post_webhook
|
||||
from open_webui.utils.access_control import get_permissions, has_permission
|
||||
from open_webui.utils.groups import apply_default_group_assignment
|
||||
|
||||
from open_webui.utils.redis import get_redis_client
|
||||
from open_webui.utils.misc import parse_duration, validate_email_format
|
||||
from open_webui.utils.oauth import auth_manager_config
|
||||
from open_webui.utils.rate_limit import RateLimiter
|
||||
|
||||
|
||||
from typing import Optional, List
|
||||
|
||||
from ssl import CERT_NONE, CERT_REQUIRED, PROTOCOL_TLS
|
||||
|
||||
from ldap3 import Server, Connection, NONE, Tls
|
||||
from ldap3.utils.conv import escape_filter_chars
|
||||
from open_webui.utils.redis import get_redis_client
|
||||
from open_webui.utils.webhook import post_webhook
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -155,14 +149,14 @@ async def create_session_response(
|
||||
|
||||
|
||||
class SessionUserResponse(Token, UserProfileImageResponse):
|
||||
expires_at: Optional[int] = None
|
||||
permissions: Optional[dict] = None
|
||||
expires_at: int | None = None
|
||||
permissions: dict | None = None
|
||||
|
||||
|
||||
class SessionUserInfoResponse(SessionUserResponse, UserStatus):
|
||||
bio: Optional[str] = None
|
||||
gender: Optional[str] = None
|
||||
date_of_birth: Optional[datetime.date] = None
|
||||
bio: str | None = None
|
||||
gender: str | None = None
|
||||
date_of_birth: datetime.date | None = None
|
||||
|
||||
|
||||
@router.get('/', response_model=SessionUserInfoResponse)
|
||||
@@ -1031,7 +1025,7 @@ async def get_admin_config(request: Request, user=Depends(get_admin_user)):
|
||||
|
||||
class AdminConfig(BaseModel):
|
||||
SHOW_ADMIN_DETAILS: bool
|
||||
ADMIN_EMAIL: Optional[str] = None
|
||||
ADMIN_EMAIL: str | None = None
|
||||
WEBUI_URL: str
|
||||
ENABLE_SIGNUP: bool
|
||||
ENABLE_API_KEYS: bool
|
||||
@@ -1043,9 +1037,9 @@ class AdminConfig(BaseModel):
|
||||
ENABLE_COMMUNITY_SHARING: bool
|
||||
ENABLE_MESSAGE_RATING: bool
|
||||
ENABLE_FOLDERS: bool
|
||||
FOLDER_MAX_FILE_COUNT: Optional[int | str] = None
|
||||
AUTOMATION_MAX_COUNT: Optional[int | str] = None
|
||||
AUTOMATION_MIN_INTERVAL: Optional[int | str] = None
|
||||
FOLDER_MAX_FILE_COUNT: int | str | None = None
|
||||
AUTOMATION_MAX_COUNT: int | str | None = None
|
||||
AUTOMATION_MIN_INTERVAL: int | str | None = None
|
||||
ENABLE_AUTOMATIONS: bool
|
||||
ENABLE_CHANNELS: bool
|
||||
ENABLE_CALENDAR: bool
|
||||
@@ -1053,9 +1047,9 @@ class AdminConfig(BaseModel):
|
||||
ENABLE_NOTES: bool
|
||||
ENABLE_USER_WEBHOOKS: bool
|
||||
ENABLE_USER_STATUS: bool
|
||||
PENDING_USER_OVERLAY_TITLE: Optional[str] = None
|
||||
PENDING_USER_OVERLAY_CONTENT: Optional[str] = None
|
||||
RESPONSE_WATERMARK: Optional[str] = None
|
||||
PENDING_USER_OVERLAY_TITLE: str | None = None
|
||||
PENDING_USER_OVERLAY_CONTENT: str | None = None
|
||||
RESPONSE_WATERMARK: str | None = None
|
||||
|
||||
|
||||
@router.post('/admin/config')
|
||||
@@ -1140,7 +1134,7 @@ async def update_admin_config(request: Request, form_data: AdminConfig, user=Dep
|
||||
class LdapServerConfig(BaseModel):
|
||||
label: str
|
||||
host: str
|
||||
port: Optional[int] = None
|
||||
port: int | None = None
|
||||
attribute_for_mail: str = 'mail'
|
||||
attribute_for_username: str = 'uid'
|
||||
app_dn: str
|
||||
@@ -1148,9 +1142,9 @@ class LdapServerConfig(BaseModel):
|
||||
search_base: str
|
||||
search_filters: str = ''
|
||||
use_tls: bool = True
|
||||
certificate_path: Optional[str] = None
|
||||
certificate_path: str | None = None
|
||||
validate_cert: bool = True
|
||||
ciphers: Optional[str] = 'ALL'
|
||||
ciphers: str | None = 'ALL'
|
||||
|
||||
|
||||
@router.get('/admin/config/ldap/server', response_model=LdapServerConfig)
|
||||
@@ -1223,7 +1217,7 @@ async def get_ldap_config(request: Request, user=Depends(get_admin_user)):
|
||||
|
||||
|
||||
class LdapConfigForm(BaseModel):
|
||||
enable_ldap: Optional[bool] = None
|
||||
enable_ldap: bool | None = None
|
||||
|
||||
|
||||
@router.post('/admin/config/ldap')
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from typing import Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.automations import (
|
||||
Automations,
|
||||
AutomationRuns,
|
||||
AutomationForm,
|
||||
AutomationListResponse,
|
||||
AutomationModel,
|
||||
AutomationResponse,
|
||||
AutomationRunModel,
|
||||
AutomationListResponse,
|
||||
AutomationRuns,
|
||||
Automations,
|
||||
)
|
||||
from open_webui.utils.automations import (
|
||||
validate_rrule,
|
||||
next_run_ns,
|
||||
next_n_runs_ns,
|
||||
execute_automation,
|
||||
rrule_interval_seconds,
|
||||
)
|
||||
from open_webui.utils.auth import get_verified_user, get_admin_user
|
||||
from open_webui.utils.access_control import has_permission
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.automations import (
|
||||
execute_automation,
|
||||
next_n_runs_ns,
|
||||
next_run_ns,
|
||||
rrule_interval_seconds,
|
||||
validate_rrule,
|
||||
)
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -3,28 +3,27 @@ import time
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.calendar import (
|
||||
Calendars,
|
||||
CalendarEvents,
|
||||
CalendarEventAttendees,
|
||||
CalendarForm,
|
||||
CalendarUpdateForm,
|
||||
CalendarEventForm,
|
||||
CalendarEventUpdateForm,
|
||||
CalendarModel,
|
||||
CalendarEventModel,
|
||||
CalendarEventUserResponse,
|
||||
CalendarEventListResponse,
|
||||
CalendarEventModel,
|
||||
CalendarEvents,
|
||||
CalendarEventUpdateForm,
|
||||
CalendarEventUserResponse,
|
||||
CalendarForm,
|
||||
CalendarModel,
|
||||
Calendars,
|
||||
CalendarUpdateForm,
|
||||
RSVPForm,
|
||||
)
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.users import UserModel
|
||||
from open_webui.utils.access_control import filter_allowed_access_grants, has_permission
|
||||
from open_webui.utils.auth import get_verified_user
|
||||
from open_webui.utils.access_control import has_permission, filter_allowed_access_grants
|
||||
from open_webui.utils.calendar import expand_recurring_event
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -188,7 +187,7 @@ async def get_events(
|
||||
cal_id_list is None or SCHEDULED_TASKS_CALENDAR_ID in cal_id_list
|
||||
):
|
||||
try:
|
||||
from open_webui.models.automations import Automations, AutomationRuns
|
||||
from open_webui.models.automations import AutomationRuns, Automations
|
||||
|
||||
# Future runs: expand RRULEs for active automations only
|
||||
active_automations = await Automations.get_active_by_user(user.id)
|
||||
|
||||
@@ -1,69 +1,58 @@
|
||||
import json
|
||||
import logging
|
||||
import base64
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status, BackgroundTasks
|
||||
from fastapi.responses import Response, StreamingResponse, FileResponse
|
||||
from pydantic import BaseModel
|
||||
from pydantic import field_validator
|
||||
|
||||
from open_webui.socket.main import (
|
||||
emit_to_users,
|
||||
enter_room_for_users,
|
||||
sio,
|
||||
get_user_ids_from_room,
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Request, status
|
||||
from fastapi.responses import FileResponse, Response, StreamingResponse
|
||||
from open_webui.config import ENABLE_ADMIN_CHAT_ACCESS, ENABLE_ADMIN_EXPORT
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import STATIC_DIR
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.access_grants import AccessGrants, has_public_read_access_grant, has_public_write_access_grant
|
||||
from open_webui.models.channels import (
|
||||
ChannelForm,
|
||||
ChannelModel,
|
||||
ChannelResponse,
|
||||
Channels,
|
||||
ChannelWebhookForm,
|
||||
ChannelWebhookModel,
|
||||
CreateChannelForm,
|
||||
)
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.messages import (
|
||||
MessageForm,
|
||||
MessageModel,
|
||||
MessageResponse,
|
||||
Messages,
|
||||
MessageWithReactionsResponse,
|
||||
)
|
||||
from open_webui.models.users import (
|
||||
UserIdNameResponse,
|
||||
UserIdNameStatusResponse,
|
||||
UserListResponse,
|
||||
UserModelResponse,
|
||||
Users,
|
||||
UserModel,
|
||||
UserModelResponse,
|
||||
UserNameResponse,
|
||||
Users,
|
||||
)
|
||||
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.channels import (
|
||||
Channels,
|
||||
ChannelModel,
|
||||
ChannelForm,
|
||||
ChannelResponse,
|
||||
CreateChannelForm,
|
||||
ChannelWebhookModel,
|
||||
ChannelWebhookForm,
|
||||
from open_webui.socket.main import (
|
||||
emit_to_users,
|
||||
enter_room_for_users,
|
||||
get_user_ids_from_room,
|
||||
sio,
|
||||
)
|
||||
from open_webui.models.access_grants import AccessGrants, has_public_read_access_grant, has_public_write_access_grant
|
||||
from open_webui.models.messages import (
|
||||
Messages,
|
||||
MessageModel,
|
||||
MessageResponse,
|
||||
MessageWithReactionsResponse,
|
||||
MessageForm,
|
||||
)
|
||||
|
||||
|
||||
from open_webui.utils.access_control import filter_allowed_access_grants, has_permission
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.channels import extract_mentions, replace_mentions
|
||||
from open_webui.utils.files import get_image_base64_from_file_id
|
||||
|
||||
from open_webui.config import ENABLE_ADMIN_CHAT_ACCESS, ENABLE_ADMIN_EXPORT
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import STATIC_DIR
|
||||
|
||||
|
||||
from open_webui.utils.models import (
|
||||
get_all_models,
|
||||
get_filtered_models,
|
||||
)
|
||||
|
||||
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.access_control import has_permission, filter_allowed_access_grants
|
||||
from open_webui.utils.webhook import post_webhook
|
||||
from open_webui.utils.channels import extract_mentions, replace_mentions
|
||||
from open_webui.internal.db import get_async_session
|
||||
from pydantic import BaseModel, field_validator
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -980,9 +969,9 @@ async def model_response_handler(request, channel, message, user, db=None):
|
||||
|
||||
# Resolve model config (same helpers automations use)
|
||||
from open_webui.utils.automations import (
|
||||
_resolve_model_tool_ids,
|
||||
_resolve_model_features,
|
||||
_resolve_model_filter_ids,
|
||||
_resolve_model_tool_ids,
|
||||
)
|
||||
|
||||
tool_ids = _resolve_model_tool_ids(request.app, model_id)
|
||||
|
||||
@@ -1,43 +1,41 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
from uuid import uuid4
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
import asyncio
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
|
||||
from open_webui.utils.misc import get_message_list
|
||||
from open_webui.utils.middleware import serialize_output
|
||||
from open_webui.socket.main import get_event_emitter
|
||||
from open_webui.models.chats import (
|
||||
ChatForm,
|
||||
ChatImportForm,
|
||||
ChatUsageStatsListResponse,
|
||||
ChatsImportForm,
|
||||
ChatResponse,
|
||||
Chats,
|
||||
ChatTitleIdResponse,
|
||||
ChatStatsExport,
|
||||
AggregateChatStats,
|
||||
ChatBody,
|
||||
ChatHistoryStats,
|
||||
MessageStats,
|
||||
)
|
||||
from open_webui.models.shared_chats import SharedChats, SharedChatResponse
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.tags import TagModel, Tags
|
||||
from open_webui.models.folders import Folders
|
||||
from open_webui.internal.db import get_async_session
|
||||
|
||||
from open_webui.config import ENABLE_ADMIN_CHAT_ACCESS, ENABLE_ADMIN_EXPORT
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.chats import (
|
||||
AggregateChatStats,
|
||||
ChatBody,
|
||||
ChatForm,
|
||||
ChatHistoryStats,
|
||||
ChatImportForm,
|
||||
ChatResponse,
|
||||
Chats,
|
||||
ChatsImportForm,
|
||||
ChatStatsExport,
|
||||
ChatTitleIdResponse,
|
||||
ChatUsageStatsListResponse,
|
||||
MessageStats,
|
||||
)
|
||||
from open_webui.models.folders import Folders
|
||||
from open_webui.models.shared_chats import SharedChatResponse, SharedChats
|
||||
from open_webui.models.tags import TagModel, Tags
|
||||
from open_webui.socket.main import get_event_emitter
|
||||
from open_webui.utils.access_control import filter_allowed_access_grants, has_permission
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.access_control import has_permission, filter_allowed_access_grants
|
||||
from open_webui.utils.middleware import serialize_output
|
||||
from open_webui.utils.misc import get_message_list
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -54,9 +52,9 @@ router = APIRouter()
|
||||
@router.get('/list', response_model=list[ChatTitleIdResponse])
|
||||
async def get_session_user_chat_list(
|
||||
user=Depends(get_verified_user),
|
||||
page: Optional[int] = None,
|
||||
include_pinned: Optional[bool] = False,
|
||||
include_folders: Optional[bool] = False,
|
||||
page: int | None = None,
|
||||
include_pinned: bool | None = False,
|
||||
include_folders: bool | None = False,
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
try:
|
||||
@@ -92,8 +90,8 @@ async def get_session_user_chat_list(
|
||||
|
||||
@router.get('/stats/usage', response_model=ChatUsageStatsListResponse)
|
||||
async def get_session_user_chat_usage_stats(
|
||||
items_per_page: Optional[int] = 50,
|
||||
page: Optional[int] = 1,
|
||||
items_per_page: int | None = 50,
|
||||
page: int | None = 1,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -210,7 +208,7 @@ class ChatStatsExportList(BaseModel):
|
||||
page: int
|
||||
|
||||
|
||||
def _process_chat_for_export(chat) -> Optional[ChatStatsExport]:
|
||||
def _process_chat_for_export(chat) -> ChatStatsExport | None:
|
||||
try:
|
||||
|
||||
def get_message_content_length(message):
|
||||
@@ -395,8 +393,8 @@ async def generate_chat_stats_jsonl_generator(user_id, filter):
|
||||
@router.get('/stats/export', response_model=ChatStatsExportList)
|
||||
async def export_chat_stats(
|
||||
request: Request,
|
||||
updated_at: Optional[int] = None,
|
||||
page: Optional[int] = 1,
|
||||
updated_at: int | None = None,
|
||||
page: int | None = 1,
|
||||
stream: bool = False,
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
@@ -438,7 +436,7 @@ async def export_chat_stats(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/stats/export/{chat_id}', response_model=Optional[ChatStatsExport])
|
||||
@router.get('/stats/export/{chat_id}', response_model=ChatStatsExport | None)
|
||||
async def export_single_chat_stats(
|
||||
request: Request,
|
||||
chat_id: str,
|
||||
@@ -516,10 +514,10 @@ async def delete_all_user_chats(
|
||||
@router.get('/list/user/{user_id}', response_model=list[ChatTitleIdResponse])
|
||||
async def get_user_chat_list_by_user_id(
|
||||
user_id: str,
|
||||
page: Optional[int] = None,
|
||||
query: Optional[str] = None,
|
||||
order_by: Optional[str] = None,
|
||||
direction: Optional[str] = None,
|
||||
page: int | None = None,
|
||||
query: str | None = None,
|
||||
order_by: str | None = None,
|
||||
direction: str | None = None,
|
||||
user=Depends(get_admin_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -553,7 +551,7 @@ async def get_user_chat_list_by_user_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/new', response_model=Optional[ChatResponse])
|
||||
@router.post('/new', response_model=ChatResponse | None)
|
||||
async def create_new_chat(
|
||||
form_data: ChatForm,
|
||||
user=Depends(get_verified_user),
|
||||
@@ -594,7 +592,7 @@ async def import_chats(
|
||||
@router.get('/search', response_model=list[ChatTitleIdResponse])
|
||||
async def search_user_chats(
|
||||
text: str,
|
||||
page: Optional[int] = None,
|
||||
page: int | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -644,7 +642,7 @@ async def get_chats_by_folder_id(
|
||||
@router.get('/folder/{folder_id}/list')
|
||||
async def get_chat_list_by_folder_id(
|
||||
folder_id: str,
|
||||
page: Optional[int] = 1,
|
||||
page: int | None = 1,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -766,10 +764,10 @@ async def get_all_user_chats_in_db(user=Depends(get_admin_user), db: AsyncSessio
|
||||
|
||||
@router.get('/archived', response_model=list[ChatTitleIdResponse])
|
||||
async def get_archived_session_user_chat_list(
|
||||
page: Optional[int] = None,
|
||||
query: Optional[str] = None,
|
||||
order_by: Optional[str] = None,
|
||||
direction: Optional[str] = None,
|
||||
page: int | None = None,
|
||||
query: str | None = None,
|
||||
order_by: str | None = None,
|
||||
direction: str | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -823,10 +821,10 @@ async def unarchive_all_chats(user=Depends(get_verified_user), db: AsyncSession
|
||||
|
||||
@router.get('/shared', response_model=list[SharedChatResponse])
|
||||
async def get_shared_session_user_chat_list(
|
||||
page: Optional[int] = None,
|
||||
query: Optional[str] = None,
|
||||
order_by: Optional[str] = None,
|
||||
direction: Optional[str] = None,
|
||||
page: int | None = None,
|
||||
query: str | None = None,
|
||||
order_by: str | None = None,
|
||||
direction: str | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -858,7 +856,7 @@ async def get_shared_session_user_chat_list(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/share/{share_id}', response_model=Optional[ChatResponse])
|
||||
@router.get('/share/{share_id}', response_model=ChatResponse | None)
|
||||
async def get_shared_chat_by_id(
|
||||
share_id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -904,8 +902,8 @@ class TagForm(BaseModel):
|
||||
|
||||
|
||||
class TagFilterForm(TagForm):
|
||||
skip: Optional[int] = 0
|
||||
limit: Optional[int] = 50
|
||||
skip: int | None = 0
|
||||
limit: int | None = 50
|
||||
|
||||
|
||||
@router.post('/tags', response_model=list[ChatTitleIdResponse])
|
||||
@@ -928,7 +926,7 @@ async def get_user_chat_list_by_tag_name(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/{id}', response_model=Optional[ChatResponse])
|
||||
@router.get('/{id}', response_model=ChatResponse | None)
|
||||
async def get_chat_by_id(id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
||||
chat = await Chats.get_chat_by_id_and_user_id(id, user.id, db=db)
|
||||
|
||||
@@ -958,7 +956,7 @@ async def get_chat_by_id(id: str, user=Depends(get_verified_user), db: AsyncSess
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/{id}', response_model=Optional[ChatResponse])
|
||||
@router.post('/{id}', response_model=ChatResponse | None)
|
||||
async def update_chat_by_id(
|
||||
id: str,
|
||||
form_data: ChatForm,
|
||||
@@ -992,7 +990,7 @@ class MessageForm(BaseModel):
|
||||
content: str
|
||||
|
||||
|
||||
@router.post('/{id}/messages/{message_id}', response_model=Optional[ChatResponse])
|
||||
@router.post('/{id}/messages/{message_id}', response_model=ChatResponse | None)
|
||||
async def update_chat_message_by_id(
|
||||
id: str,
|
||||
message_id: str,
|
||||
@@ -1054,7 +1052,7 @@ class EventForm(BaseModel):
|
||||
data: dict
|
||||
|
||||
|
||||
@router.post('/{id}/messages/{message_id}/event', response_model=Optional[bool])
|
||||
@router.post('/{id}/messages/{message_id}/event', response_model=bool | None)
|
||||
async def send_chat_message_event_by_id(
|
||||
id: str,
|
||||
message_id: str,
|
||||
@@ -1142,7 +1140,7 @@ async def delete_chat_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/{id}/pinned', response_model=Optional[bool])
|
||||
@router.get('/{id}/pinned', response_model=bool | None)
|
||||
async def get_pinned_status_by_id(
|
||||
id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -1158,7 +1156,7 @@ async def get_pinned_status_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/{id}/pin', response_model=Optional[ChatResponse])
|
||||
@router.post('/{id}/pin', response_model=ChatResponse | None)
|
||||
async def pin_chat_by_id(id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
||||
chat = await Chats.get_chat_by_id_and_user_id(id, user.id, db=db)
|
||||
if chat:
|
||||
@@ -1174,10 +1172,10 @@ async def pin_chat_by_id(id: str, user=Depends(get_verified_user), db: AsyncSess
|
||||
|
||||
|
||||
class CloneForm(BaseModel):
|
||||
title: Optional[str] = None
|
||||
title: str | None = None
|
||||
|
||||
|
||||
@router.post('/{id}/clone', response_model=Optional[ChatResponse])
|
||||
@router.post('/{id}/clone', response_model=ChatResponse | None)
|
||||
async def clone_chat_by_id(
|
||||
form_data: CloneForm,
|
||||
id: str,
|
||||
@@ -1225,7 +1223,7 @@ async def clone_chat_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/{id}/clone/shared', response_model=Optional[ChatResponse])
|
||||
@router.post('/{id}/clone/shared', response_model=ChatResponse | None)
|
||||
async def clone_shared_chat_by_id(
|
||||
id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -1294,7 +1292,7 @@ async def clone_shared_chat_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/{id}/archive', response_model=Optional[ChatResponse])
|
||||
@router.post('/{id}/archive', response_model=ChatResponse | None)
|
||||
async def archive_chat_by_id(id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
||||
chat = await Chats.get_chat_by_id_and_user_id(id, user.id, db=db)
|
||||
if chat:
|
||||
@@ -1318,7 +1316,7 @@ async def archive_chat_by_id(id: str, user=Depends(get_verified_user), db: Async
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/{id}/share', response_model=Optional[ChatResponse])
|
||||
@router.post('/{id}/share', response_model=ChatResponse | None)
|
||||
async def share_chat_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -1372,7 +1370,7 @@ async def share_chat_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.delete('/{id}/share', response_model=Optional[bool])
|
||||
@router.delete('/{id}/share', response_model=bool | None)
|
||||
async def delete_shared_chat_by_id(
|
||||
id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -1404,7 +1402,7 @@ class ChatAccessGrantsForm(BaseModel):
|
||||
access_grants: list[dict]
|
||||
|
||||
|
||||
@router.post('/shared/{id}/access/update', response_model=Optional[ChatResponse])
|
||||
@router.post('/shared/{id}/access/update', response_model=ChatResponse | None)
|
||||
async def update_shared_chat_access_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -1474,10 +1472,10 @@ async def get_shared_chat_access_by_id(
|
||||
|
||||
|
||||
class ChatFolderIdForm(BaseModel):
|
||||
folder_id: Optional[str] = None
|
||||
folder_id: str | None = None
|
||||
|
||||
|
||||
@router.post('/{id}/folder', response_model=Optional[ChatResponse])
|
||||
@router.post('/{id}/folder', response_model=ChatResponse | None)
|
||||
async def update_chat_folder_id_by_id(
|
||||
id: str,
|
||||
form_data: ChatFolderIdForm,
|
||||
@@ -1571,7 +1569,7 @@ async def delete_tag_by_id_and_tag_name(
|
||||
############################
|
||||
|
||||
|
||||
@router.delete('/{id}/tags/all', response_model=Optional[bool])
|
||||
@router.delete('/{id}/tags/all', response_model=bool | None)
|
||||
async def delete_all_tags_by_id(
|
||||
id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
|
||||
@@ -1,37 +1,34 @@
|
||||
import logging
|
||||
import copy
|
||||
from fastapi import APIRouter, Depends, Request, HTTPException
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
import aiohttp
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
import aiohttp
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from mcp.shared.auth import OAuthMetadata
|
||||
from open_webui.config import BannerModel, async_save_config, get_config, save_config
|
||||
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL, AIOHTTP_CLIENT_TIMEOUT
|
||||
from open_webui.models.oauth_sessions import OAuthSessions
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.headers import get_custom_headers
|
||||
from open_webui.config import get_config, save_config, async_save_config
|
||||
from open_webui.config import BannerModel
|
||||
|
||||
from open_webui.utils.tools import (
|
||||
get_tool_server_data,
|
||||
get_tool_server_url,
|
||||
set_tool_servers,
|
||||
set_terminal_servers,
|
||||
)
|
||||
from open_webui.utils.mcp.client import MCPClient
|
||||
from open_webui.models.oauth_sessions import OAuthSessions
|
||||
|
||||
|
||||
from open_webui.utils.oauth import (
|
||||
OAuthClientInformationFull,
|
||||
decrypt_data,
|
||||
encrypt_data,
|
||||
get_discovery_urls,
|
||||
get_oauth_client_info_with_dynamic_client_registration,
|
||||
get_oauth_client_info_with_static_credentials,
|
||||
encrypt_data,
|
||||
decrypt_data,
|
||||
resolve_oauth_client_info,
|
||||
OAuthClientInformationFull,
|
||||
)
|
||||
from mcp.shared.auth import OAuthMetadata
|
||||
from open_webui.utils.tools import (
|
||||
get_tool_server_data,
|
||||
get_tool_server_url,
|
||||
set_terminal_servers,
|
||||
set_tool_servers,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -102,16 +99,16 @@ async def set_connections_config(
|
||||
class OAuthClientRegistrationForm(BaseModel):
|
||||
url: str
|
||||
client_id: str
|
||||
client_name: Optional[str] = None
|
||||
client_secret: Optional[str] = None
|
||||
oauth_server_url: Optional[str] = None
|
||||
client_name: str | None = None
|
||||
client_secret: str | None = None
|
||||
oauth_server_url: str | None = None
|
||||
|
||||
|
||||
@router.post('/oauth/clients/register')
|
||||
async def register_oauth_client(
|
||||
request: Request,
|
||||
form_data: OAuthClientRegistrationForm,
|
||||
type: Optional[str] = None,
|
||||
type: str | None = None,
|
||||
user=Depends(get_admin_user),
|
||||
):
|
||||
try:
|
||||
@@ -154,12 +151,12 @@ async def register_oauth_client(
|
||||
class ToolServerConnection(BaseModel):
|
||||
url: str
|
||||
path: str
|
||||
type: Optional[str] = 'openapi' # openapi, mcp
|
||||
auth_type: Optional[str]
|
||||
headers: Optional[dict | str] = None
|
||||
key: Optional[str]
|
||||
config: Optional[dict]
|
||||
info: Optional[dict] = None
|
||||
type: str | None = 'openapi' # openapi, mcp
|
||||
auth_type: str | None
|
||||
headers: dict | str | None = None
|
||||
key: str | None
|
||||
config: dict | None
|
||||
info: dict | None = None
|
||||
|
||||
model_config = ConfigDict(extra='allow')
|
||||
|
||||
@@ -225,23 +222,23 @@ async def set_tool_servers_config(
|
||||
|
||||
|
||||
class TerminalServerConnection(BaseModel):
|
||||
id: Optional[str] = ''
|
||||
name: Optional[str] = ''
|
||||
id: str | None = ''
|
||||
name: str | None = ''
|
||||
|
||||
enabled: Optional[bool] = True
|
||||
enabled: bool | None = True
|
||||
|
||||
url: str
|
||||
path: Optional[str] = '/openapi.json'
|
||||
path: str | None = '/openapi.json'
|
||||
|
||||
key: Optional[str] = ''
|
||||
auth_type: Optional[str] = 'bearer'
|
||||
key: str | None = ''
|
||||
auth_type: str | None = 'bearer'
|
||||
|
||||
config: Optional[dict] = None
|
||||
config: dict | None = None
|
||||
|
||||
# Orchestrator policy fields
|
||||
server_type: Optional[str] = None # "orchestrator", "terminal"
|
||||
policy_id: Optional[str] = None
|
||||
policy: Optional[dict] = None # cached policy data
|
||||
server_type: str | None = None # "orchestrator", "terminal"
|
||||
policy_id: str | None = None
|
||||
policy: dict | None = None # cached policy data
|
||||
|
||||
model_config = ConfigDict(extra='allow')
|
||||
|
||||
@@ -325,8 +322,8 @@ async def verify_terminal_server_connection(
|
||||
|
||||
class TerminalServerPolicyForm(BaseModel):
|
||||
url: str
|
||||
key: Optional[str] = ''
|
||||
auth_type: Optional[str] = 'bearer'
|
||||
key: str | None = ''
|
||||
auth_type: str | None = 'bearer'
|
||||
policy_id: str
|
||||
policy_data: dict
|
||||
|
||||
@@ -504,19 +501,19 @@ async def verify_tool_servers_config(request: Request, form_data: ToolServerConn
|
||||
class CodeInterpreterConfigForm(BaseModel):
|
||||
ENABLE_CODE_EXECUTION: bool
|
||||
CODE_EXECUTION_ENGINE: str
|
||||
CODE_EXECUTION_JUPYTER_URL: Optional[str]
|
||||
CODE_EXECUTION_JUPYTER_AUTH: Optional[str]
|
||||
CODE_EXECUTION_JUPYTER_AUTH_TOKEN: Optional[str]
|
||||
CODE_EXECUTION_JUPYTER_AUTH_PASSWORD: Optional[str]
|
||||
CODE_EXECUTION_JUPYTER_TIMEOUT: Optional[int]
|
||||
CODE_EXECUTION_JUPYTER_URL: str | None
|
||||
CODE_EXECUTION_JUPYTER_AUTH: str | None
|
||||
CODE_EXECUTION_JUPYTER_AUTH_TOKEN: str | None
|
||||
CODE_EXECUTION_JUPYTER_AUTH_PASSWORD: str | None
|
||||
CODE_EXECUTION_JUPYTER_TIMEOUT: int | None
|
||||
ENABLE_CODE_INTERPRETER: bool
|
||||
CODE_INTERPRETER_ENGINE: str
|
||||
CODE_INTERPRETER_PROMPT_TEMPLATE: Optional[str]
|
||||
CODE_INTERPRETER_JUPYTER_URL: Optional[str]
|
||||
CODE_INTERPRETER_JUPYTER_AUTH: Optional[str]
|
||||
CODE_INTERPRETER_JUPYTER_AUTH_TOKEN: Optional[str]
|
||||
CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD: Optional[str]
|
||||
CODE_INTERPRETER_JUPYTER_TIMEOUT: Optional[int]
|
||||
CODE_INTERPRETER_PROMPT_TEMPLATE: str | None
|
||||
CODE_INTERPRETER_JUPYTER_URL: str | None
|
||||
CODE_INTERPRETER_JUPYTER_AUTH: str | None
|
||||
CODE_INTERPRETER_JUPYTER_AUTH_TOKEN: str | None
|
||||
CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD: str | None
|
||||
CODE_INTERPRETER_JUPYTER_TIMEOUT: int | None
|
||||
|
||||
|
||||
@router.get('/code_execution', response_model=CodeInterpreterConfigForm)
|
||||
@@ -588,11 +585,11 @@ async def set_code_execution_config(
|
||||
# SetDefaultModels
|
||||
############################
|
||||
class ModelsConfigForm(BaseModel):
|
||||
DEFAULT_MODELS: Optional[str]
|
||||
DEFAULT_PINNED_MODELS: Optional[str]
|
||||
MODEL_ORDER_LIST: Optional[list[str]]
|
||||
DEFAULT_MODEL_METADATA: Optional[dict] = None
|
||||
DEFAULT_MODEL_PARAMS: Optional[dict] = None
|
||||
DEFAULT_MODELS: str | None
|
||||
DEFAULT_PINNED_MODELS: str | None
|
||||
MODEL_ORDER_LIST: list[str | None]
|
||||
DEFAULT_MODEL_METADATA: dict | None = None
|
||||
DEFAULT_MODEL_PARAMS: dict | None = None
|
||||
|
||||
|
||||
@router.get('/models/defaults')
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
from typing import Optional
|
||||
import logging
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Request
|
||||
from fastapi.concurrency import run_in_threadpool
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
from open_webui.models.users import Users, UserModel
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from fastapi.concurrency import run_in_threadpool
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.feedbacks import (
|
||||
FeedbackForm,
|
||||
FeedbackIdResponse,
|
||||
FeedbackListResponse,
|
||||
FeedbackModel,
|
||||
FeedbackResponse,
|
||||
FeedbackForm,
|
||||
Feedbacks,
|
||||
FeedbackUserResponse,
|
||||
FeedbackListResponse,
|
||||
LeaderboardFeedbackData,
|
||||
ModelHistoryEntry,
|
||||
ModelHistoryResponse,
|
||||
Feedbacks,
|
||||
)
|
||||
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.models.users import UserModel, Users
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.internal.db import get_async_session
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,34 +1,31 @@
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from urllib.parse import quote
|
||||
import asyncio
|
||||
|
||||
from fastapi import (
|
||||
BackgroundTasks,
|
||||
APIRouter,
|
||||
BackgroundTasks,
|
||||
Depends,
|
||||
File,
|
||||
Form,
|
||||
HTTPException,
|
||||
Query,
|
||||
Request,
|
||||
UploadFile,
|
||||
status,
|
||||
Query,
|
||||
)
|
||||
|
||||
from fastapi.responses import FileResponse, StreamingResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from open_webui.internal.db import get_async_session, get_async_db_context
|
||||
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL, STORAGE_LOCAL_CACHE, STORAGE_PROVIDER, UPLOAD_DIR
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.retrieval.vector.async_client import ASYNC_VECTOR_DB_CLIENT
|
||||
|
||||
from open_webui.internal.db import get_async_db_context, get_async_session
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.channels import Channels
|
||||
from open_webui.models.users import Users
|
||||
from open_webui.models.chats import Chats
|
||||
from open_webui.models.files import (
|
||||
FileForm,
|
||||
FileListResponse,
|
||||
@@ -36,22 +33,17 @@ from open_webui.models.files import (
|
||||
FileModelResponse,
|
||||
Files,
|
||||
)
|
||||
from open_webui.models.chats import Chats
|
||||
from open_webui.models.knowledge import Knowledges
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
|
||||
|
||||
from open_webui.routers.retrieval import ProcessFileForm, process_file
|
||||
from open_webui.models.knowledge import Knowledges
|
||||
from open_webui.models.users import Users
|
||||
from open_webui.retrieval.vector.async_client import ASYNC_VECTOR_DB_CLIENT
|
||||
from open_webui.routers.audio import transcribe
|
||||
|
||||
from open_webui.routers.retrieval import ProcessFileForm, process_file
|
||||
from open_webui.storage.provider import Storage
|
||||
|
||||
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL, STORAGE_LOCAL_CACHE, STORAGE_PROVIDER, UPLOAD_DIR
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.misc import strict_match_mime_type
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,36 +1,29 @@
|
||||
import logging
|
||||
import mimetypes
|
||||
import os
|
||||
import shutil
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
import mimetypes
|
||||
|
||||
|
||||
from open_webui.models.folders import (
|
||||
FolderForm,
|
||||
FolderUpdateForm,
|
||||
FolderModel,
|
||||
FolderNameIdResponse,
|
||||
Folders,
|
||||
)
|
||||
from open_webui.models.chats import Chats
|
||||
|
||||
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, Request, UploadFile, status
|
||||
from fastapi.responses import FileResponse, StreamingResponse
|
||||
from open_webui.config import UPLOAD_DIR
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.internal.db import get_async_session
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status, Request
|
||||
from fastapi.responses import FileResponse, StreamingResponse
|
||||
|
||||
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.models.chats import Chats
|
||||
from open_webui.models.folders import (
|
||||
FolderForm,
|
||||
FolderModel,
|
||||
FolderNameIdResponse,
|
||||
Folders,
|
||||
FolderUpdateForm,
|
||||
)
|
||||
from open_webui.utils.access_control import has_permission
|
||||
from open_webui.utils.access_control.files import get_accessible_folder_files
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
import os
|
||||
import re
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import aiohttp
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import aiohttp
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from open_webui.config import CACHE_DIR
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL, AIOHTTP_CLIENT_TIMEOUT
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.functions import (
|
||||
FunctionForm,
|
||||
FunctionModel,
|
||||
FunctionResponse,
|
||||
Functions,
|
||||
FunctionUserResponse,
|
||||
FunctionWithValvesModel,
|
||||
Functions,
|
||||
)
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.plugin import (
|
||||
get_function_module_from_cache,
|
||||
load_function_module_by_id,
|
||||
replace_imports,
|
||||
get_function_module_from_cache,
|
||||
resolve_valves_schema_options,
|
||||
)
|
||||
from open_webui.config import CACHE_DIR
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
from open_webui.internal.db import get_async_session
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -91,7 +92,7 @@ def github_url_to_raw_url(url: str) -> str:
|
||||
return url
|
||||
|
||||
|
||||
@router.post('/load/url', response_model=Optional[dict])
|
||||
@router.post('/load/url', response_model=dict | None)
|
||||
async def load_function_from_url(request: Request, form_data: LoadUrlForm, user=Depends(get_admin_user)):
|
||||
# NOTE: This is NOT a SSRF vulnerability:
|
||||
# This endpoint is admin-only (see get_admin_user), meant for *trusted* internal use,
|
||||
@@ -179,7 +180,7 @@ async def sync_functions(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/create', response_model=Optional[FunctionResponse])
|
||||
@router.post('/create', response_model=FunctionResponse | None)
|
||||
async def create_new_function(
|
||||
request: Request,
|
||||
form_data: FunctionForm,
|
||||
@@ -240,7 +241,7 @@ async def create_new_function(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/id/{id}', response_model=Optional[FunctionModel])
|
||||
@router.get('/id/{id}', response_model=FunctionModel | None)
|
||||
async def get_function_by_id(id: str, user=Depends(get_admin_user), db: AsyncSession = Depends(get_async_session)):
|
||||
function = await Functions.get_function_by_id(id, db=db)
|
||||
|
||||
@@ -258,7 +259,7 @@ async def get_function_by_id(id: str, user=Depends(get_admin_user), db: AsyncSes
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/id/{id}/toggle', response_model=Optional[FunctionModel])
|
||||
@router.post('/id/{id}/toggle', response_model=FunctionModel | None)
|
||||
async def toggle_function_by_id(id: str, user=Depends(get_admin_user), db: AsyncSession = Depends(get_async_session)):
|
||||
function = await Functions.get_function_by_id(id, db=db)
|
||||
if function:
|
||||
@@ -283,7 +284,7 @@ async def toggle_function_by_id(id: str, user=Depends(get_admin_user), db: Async
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/id/{id}/toggle/global', response_model=Optional[FunctionModel])
|
||||
@router.post('/id/{id}/toggle/global', response_model=FunctionModel | None)
|
||||
async def toggle_global_by_id(id: str, user=Depends(get_admin_user), db: AsyncSession = Depends(get_async_session)):
|
||||
function = await Functions.get_function_by_id(id, db=db)
|
||||
if function:
|
||||
@@ -308,7 +309,7 @@ async def toggle_global_by_id(id: str, user=Depends(get_admin_user), db: AsyncSe
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/id/{id}/update', response_model=Optional[FunctionModel])
|
||||
@router.post('/id/{id}/update', response_model=FunctionModel | None)
|
||||
async def update_function_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -374,7 +375,7 @@ async def delete_function_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/id/{id}/valves', response_model=Optional[dict])
|
||||
@router.get('/id/{id}/valves', response_model=dict | None)
|
||||
async def get_function_valves_by_id(
|
||||
id: str, user=Depends(get_admin_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -400,7 +401,7 @@ async def get_function_valves_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/id/{id}/valves/spec', response_model=Optional[dict])
|
||||
@router.get('/id/{id}/valves/spec', response_model=dict | None)
|
||||
async def get_function_valves_spec_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -430,7 +431,7 @@ async def get_function_valves_spec_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/id/{id}/valves/update', response_model=Optional[dict])
|
||||
@router.post('/id/{id}/valves/update', response_model=dict | None)
|
||||
async def update_function_valves_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -476,7 +477,7 @@ async def update_function_valves_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/id/{id}/valves/user', response_model=Optional[dict])
|
||||
@router.get('/id/{id}/valves/user', response_model=dict | None)
|
||||
async def get_function_user_valves_by_id(
|
||||
id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -497,7 +498,7 @@ async def get_function_user_valves_by_id(
|
||||
)
|
||||
|
||||
|
||||
@router.get('/id/{id}/valves/user/spec', response_model=Optional[dict])
|
||||
@router.get('/id/{id}/valves/user/spec', response_model=dict | None)
|
||||
async def get_function_user_valves_spec_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -522,7 +523,7 @@ async def get_function_user_valves_spec_by_id(
|
||||
)
|
||||
|
||||
|
||||
@router.post('/id/{id}/valves/user/update', response_model=Optional[dict])
|
||||
@router.post('/id/{id}/valves/user/update', response_model=dict | None)
|
||||
async def update_function_user_valves_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
from open_webui.models.users import Users, UserInfoResponse
|
||||
from open_webui.models.groups import (
|
||||
Groups,
|
||||
GroupForm,
|
||||
GroupInfoResponse,
|
||||
GroupUpdateForm,
|
||||
GroupResponse,
|
||||
UserIdsForm,
|
||||
)
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from open_webui.config import CACHE_DIR
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
|
||||
from open_webui.internal.db import get_async_session
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from open_webui.models.groups import (
|
||||
GroupForm,
|
||||
GroupInfoResponse,
|
||||
GroupResponse,
|
||||
Groups,
|
||||
GroupUpdateForm,
|
||||
UserIdsForm,
|
||||
)
|
||||
from open_webui.models.users import UserInfoResponse, Users
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,46 +1,45 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import uuid
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import mimetypes
|
||||
import re
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from urllib.parse import quote
|
||||
import aiohttp
|
||||
|
||||
import aiohttp
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, UploadFile
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from open_webui.config import (
|
||||
CACHE_DIR,
|
||||
IMAGE_AUTO_SIZE_MODELS_REGEX_PATTERN,
|
||||
IMAGE_URL_RESPONSE_MODELS_REGEX_PATTERN,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.retrieval.web.utils import validate_url
|
||||
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL, AIOHTTP_CLIENT_ALLOW_REDIRECTS, ENABLE_FORWARD_USER_INFO_HEADERS
|
||||
from open_webui.utils.session_pool import get_session
|
||||
|
||||
from open_webui.models.chats import Chats
|
||||
from open_webui.routers.files import upload_file_handler, get_file_content_by_id
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.access_control import has_permission
|
||||
from open_webui.utils.headers import include_user_info_headers
|
||||
from open_webui.env import AIOHTTP_CLIENT_ALLOW_REDIRECTS, AIOHTTP_CLIENT_SESSION_SSL, ENABLE_FORWARD_USER_INFO_HEADERS
|
||||
from open_webui.internal.db import get_async_session
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from open_webui.models.chats import Chats
|
||||
from open_webui.retrieval.web.utils import validate_url
|
||||
from open_webui.routers.files import get_file_content_by_id, upload_file_handler
|
||||
from open_webui.utils.access_control import has_permission
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.headers import include_user_info_headers
|
||||
from open_webui.utils.images.comfyui import (
|
||||
ComfyUICreateImageForm,
|
||||
ComfyUIEditImageForm,
|
||||
ComfyUIWorkflow,
|
||||
comfyui_upload_image,
|
||||
comfyui_create_image,
|
||||
comfyui_edit_image,
|
||||
comfyui_upload_image,
|
||||
)
|
||||
from open_webui.utils.session_pool import get_session
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -121,17 +120,17 @@ class ImagesConfig(BaseModel):
|
||||
|
||||
IMAGE_GENERATION_ENGINE: str
|
||||
IMAGE_GENERATION_MODEL: str
|
||||
IMAGE_SIZE: Optional[str]
|
||||
IMAGE_STEPS: Optional[int]
|
||||
IMAGE_SIZE: str | None
|
||||
IMAGE_STEPS: int | None
|
||||
|
||||
IMAGES_OPENAI_API_BASE_URL: str
|
||||
IMAGES_OPENAI_API_KEY: str
|
||||
IMAGES_OPENAI_API_VERSION: str
|
||||
IMAGES_OPENAI_API_PARAMS: Optional[dict | str]
|
||||
IMAGES_OPENAI_API_PARAMS: dict | str | None
|
||||
|
||||
AUTOMATIC1111_BASE_URL: str
|
||||
AUTOMATIC1111_API_AUTH: Optional[dict | str]
|
||||
AUTOMATIC1111_PARAMS: Optional[dict | str]
|
||||
AUTOMATIC1111_API_AUTH: dict | str | None
|
||||
AUTOMATIC1111_PARAMS: dict | str | None
|
||||
|
||||
COMFYUI_BASE_URL: str
|
||||
COMFYUI_API_KEY: str
|
||||
@@ -145,7 +144,7 @@ class ImagesConfig(BaseModel):
|
||||
ENABLE_IMAGE_EDIT: bool
|
||||
IMAGE_EDIT_ENGINE: str
|
||||
IMAGE_EDIT_MODEL: str
|
||||
IMAGE_EDIT_SIZE: Optional[str]
|
||||
IMAGE_EDIT_SIZE: str | None
|
||||
|
||||
IMAGES_EDIT_OPENAI_API_BASE_URL: str
|
||||
IMAGES_EDIT_OPENAI_API_KEY: str
|
||||
@@ -428,12 +427,12 @@ async def get_models(request: Request, user=Depends(get_verified_user)):
|
||||
|
||||
|
||||
class CreateImageForm(BaseModel):
|
||||
model: Optional[str] = None
|
||||
model: str | None = None
|
||||
prompt: str
|
||||
size: Optional[str] = None
|
||||
size: str | None = None
|
||||
n: int = 1
|
||||
steps: Optional[int] = None
|
||||
negative_prompt: Optional[str] = None
|
||||
steps: int | None = None
|
||||
negative_prompt: str | None = None
|
||||
|
||||
|
||||
GenerateImageForm = CreateImageForm # Alias for backward compatibility
|
||||
@@ -528,7 +527,7 @@ async def generate_images(request: Request, form_data: CreateImageForm, user=Dep
|
||||
async def image_generations(
|
||||
request: Request,
|
||||
form_data: CreateImageForm,
|
||||
metadata: Optional[dict] = None,
|
||||
metadata: dict | None = None,
|
||||
user=None,
|
||||
):
|
||||
# if IMAGE_SIZE = 'auto', default WidthxHeight to the 512x512 default
|
||||
@@ -776,18 +775,18 @@ async def image_generations(
|
||||
class EditImageForm(BaseModel):
|
||||
image: str | list[str] # base64-encoded image(s) or URL(s)
|
||||
prompt: str
|
||||
model: Optional[str] = None
|
||||
size: Optional[str] = None
|
||||
n: Optional[int] = None
|
||||
negative_prompt: Optional[str] = None
|
||||
background: Optional[str] = None
|
||||
model: str | None = None
|
||||
size: str | None = None
|
||||
n: int | None = None
|
||||
negative_prompt: str | None = None
|
||||
background: str | None = None
|
||||
|
||||
|
||||
@router.post('/edit')
|
||||
async def image_edits(
|
||||
request: Request,
|
||||
form_data: EditImageForm,
|
||||
metadata: Optional[dict] = None,
|
||||
metadata: dict | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
size = None
|
||||
|
||||
@@ -1,42 +1,40 @@
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Request, Query
|
||||
from fastapi.responses import StreamingResponse
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import io
|
||||
import logging
|
||||
import zipfile
|
||||
from typing import List, Optional
|
||||
from urllib.parse import quote
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
|
||||
from fastapi.responses import StreamingResponse
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.files import FileMetadataResponse, FileModel, Files
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.knowledge import (
|
||||
KnowledgeFileListResponse,
|
||||
Knowledges,
|
||||
KnowledgeForm,
|
||||
KnowledgeResponse,
|
||||
Knowledges,
|
||||
KnowledgeUserResponse,
|
||||
)
|
||||
from open_webui.models.files import Files, FileModel, FileMetadataResponse
|
||||
from open_webui.models.models import ModelForm, Models
|
||||
from open_webui.retrieval.vector.async_client import ASYNC_VECTOR_DB_CLIENT
|
||||
from open_webui.routers.retrieval import (
|
||||
process_file,
|
||||
ProcessFileForm,
|
||||
process_files_batch,
|
||||
BatchProcessFilesForm,
|
||||
ProcessFileForm,
|
||||
process_file,
|
||||
process_files_batch,
|
||||
)
|
||||
from open_webui.storage.provider import Storage
|
||||
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.utils.auth import get_verified_user, get_admin_user
|
||||
from open_webui.utils.access_control import has_permission, filter_allowed_access_grants
|
||||
from open_webui.utils.access_control import filter_allowed_access_grants, has_permission
|
||||
from open_webui.utils.access_control.files import has_access_to_file
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
|
||||
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
||||
from open_webui.models.models import Models, ModelForm
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -100,7 +98,7 @@ async def remove_knowledge_base_metadata_embedding(knowledge_base_id: str) -> bo
|
||||
|
||||
|
||||
class KnowledgeAccessResponse(KnowledgeUserResponse):
|
||||
write_access: Optional[bool] = False
|
||||
write_access: bool | None = False
|
||||
|
||||
|
||||
class KnowledgeAccessListResponse(BaseModel):
|
||||
@@ -110,7 +108,7 @@ class KnowledgeAccessListResponse(BaseModel):
|
||||
|
||||
@router.get('/', response_model=KnowledgeAccessListResponse)
|
||||
async def get_knowledge_bases(
|
||||
page: Optional[int] = 1,
|
||||
page: int | None = 1,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -159,9 +157,9 @@ async def get_knowledge_bases(
|
||||
|
||||
@router.get('/search', response_model=KnowledgeAccessListResponse)
|
||||
async def search_knowledge_bases(
|
||||
query: Optional[str] = None,
|
||||
view_option: Optional[str] = None,
|
||||
page: Optional[int] = 1,
|
||||
query: str | None = None,
|
||||
view_option: str | None = None,
|
||||
page: int | None = 1,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -215,8 +213,8 @@ async def search_knowledge_bases(
|
||||
|
||||
@router.get('/search/files', response_model=KnowledgeFileListResponse)
|
||||
async def search_knowledge_files(
|
||||
query: Optional[str] = None,
|
||||
page: Optional[int] = 1,
|
||||
query: str | None = None,
|
||||
page: int | None = 1,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -242,7 +240,7 @@ async def search_knowledge_files(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/create', response_model=Optional[KnowledgeResponse])
|
||||
@router.post('/create', response_model=KnowledgeResponse | None)
|
||||
async def create_new_knowledge(
|
||||
request: Request,
|
||||
form_data: KnowledgeForm,
|
||||
@@ -380,11 +378,11 @@ async def reindex_knowledge_base_metadata_embeddings(
|
||||
|
||||
|
||||
class KnowledgeFilesResponse(KnowledgeResponse):
|
||||
files: Optional[list[FileMetadataResponse]] = None
|
||||
write_access: Optional[bool] = False
|
||||
files: list[FileMetadataResponse | None] = None
|
||||
write_access: bool | None = False
|
||||
|
||||
|
||||
@router.get('/{id}', response_model=Optional[KnowledgeFilesResponse])
|
||||
@router.get('/{id}', response_model=KnowledgeFilesResponse | None)
|
||||
async def get_knowledge_by_id(id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
||||
knowledge = await Knowledges.get_knowledge_by_id(id=id, db=db)
|
||||
|
||||
@@ -431,7 +429,7 @@ async def get_knowledge_by_id(id: str, user=Depends(get_verified_user), db: Asyn
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/{id}/update', response_model=Optional[KnowledgeFilesResponse])
|
||||
@router.post('/{id}/update', response_model=KnowledgeFilesResponse | None)
|
||||
async def update_knowledge_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -501,7 +499,7 @@ class KnowledgeAccessGrantsForm(BaseModel):
|
||||
access_grants: list[dict]
|
||||
|
||||
|
||||
@router.post('/{id}/access/update', response_model=Optional[KnowledgeFilesResponse])
|
||||
@router.post('/{id}/access/update', response_model=KnowledgeFilesResponse | None)
|
||||
async def update_knowledge_access_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -556,11 +554,11 @@ async def update_knowledge_access_by_id(
|
||||
@router.get('/{id}/files', response_model=KnowledgeFileListResponse)
|
||||
async def get_knowledge_files_by_id(
|
||||
id: str,
|
||||
query: Optional[str] = None,
|
||||
view_option: Optional[str] = None,
|
||||
order_by: Optional[str] = None,
|
||||
direction: Optional[str] = None,
|
||||
page: Optional[int] = 1,
|
||||
query: str | None = None,
|
||||
view_option: str | None = None,
|
||||
order_by: str | None = None,
|
||||
direction: str | None = None,
|
||||
page: int | None = 1,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -614,7 +612,7 @@ class KnowledgeFileIdForm(BaseModel):
|
||||
file_id: str
|
||||
|
||||
|
||||
@router.post('/{id}/file/add', response_model=Optional[KnowledgeFilesResponse])
|
||||
@router.post('/{id}/file/add', response_model=KnowledgeFilesResponse | None)
|
||||
async def add_file_to_knowledge_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -695,7 +693,7 @@ async def add_file_to_knowledge_by_id(
|
||||
)
|
||||
|
||||
|
||||
@router.post('/{id}/file/update', response_model=Optional[KnowledgeFilesResponse])
|
||||
@router.post('/{id}/file/update', response_model=KnowledgeFilesResponse | None)
|
||||
async def update_file_from_knowledge_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -774,7 +772,7 @@ async def update_file_from_knowledge_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/{id}/file/remove', response_model=Optional[KnowledgeFilesResponse])
|
||||
@router.post('/{id}/file/remove', response_model=KnowledgeFilesResponse | None)
|
||||
async def remove_file_from_knowledge_by_id(
|
||||
id: str,
|
||||
form_data: KnowledgeFileIdForm,
|
||||
@@ -936,7 +934,7 @@ async def delete_knowledge_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/{id}/reset', response_model=Optional[KnowledgeResponse])
|
||||
@router.post('/{id}/reset', response_model=KnowledgeResponse | None)
|
||||
async def reset_knowledge_by_id(
|
||||
id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -978,7 +976,7 @@ async def reset_knowledge_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/{id}/files/batch/add', response_model=Optional[KnowledgeFilesResponse])
|
||||
@router.post('/{id}/files/batch/add', response_model=KnowledgeFilesResponse | None)
|
||||
async def add_files_to_knowledge_batch(
|
||||
request: Request,
|
||||
id: str,
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from pydantic import BaseModel
|
||||
import logging
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.memories import Memories, MemoryModel
|
||||
from open_webui.retrieval.vector.async_client import ASYNC_VECTOR_DB_CLIENT
|
||||
from open_webui.utils.auth import get_verified_user
|
||||
from open_webui.internal.db import get_async_session
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from open_webui.utils.access_control import has_permission
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.utils.auth import get_verified_user
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -56,10 +57,10 @@ class AddMemoryForm(BaseModel):
|
||||
|
||||
|
||||
class MemoryUpdateModel(BaseModel):
|
||||
content: Optional[str] = None
|
||||
content: str | None = None
|
||||
|
||||
|
||||
@router.post('/add', response_model=Optional[MemoryModel])
|
||||
@router.post('/add', response_model=MemoryModel | None)
|
||||
async def add_memory(
|
||||
request: Request,
|
||||
form_data: AddMemoryForm,
|
||||
@@ -107,7 +108,7 @@ async def add_memory(
|
||||
|
||||
class QueryMemoryForm(BaseModel):
|
||||
content: str
|
||||
k: Optional[int] = 1
|
||||
k: int | None = 1
|
||||
|
||||
|
||||
@router.post('/query')
|
||||
@@ -275,7 +276,7 @@ async def delete_memory_by_user_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/{memory_id}/update', response_model=Optional[MemoryModel])
|
||||
@router.post('/{memory_id}/update', response_model=MemoryModel | None)
|
||||
async def update_memory_by_id(
|
||||
memory_id: str,
|
||||
request: Request,
|
||||
|
||||
@@ -1,28 +1,14 @@
|
||||
from typing import Optional
|
||||
import io
|
||||
import base64
|
||||
import json
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import posixpath
|
||||
from typing import Optional
|
||||
from urllib.parse import unquote
|
||||
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.models import (
|
||||
ModelForm,
|
||||
ModelMeta,
|
||||
ModelModel,
|
||||
ModelParams,
|
||||
ModelResponse,
|
||||
ModelListResponse,
|
||||
ModelAccessListResponse,
|
||||
ModelAccessResponse,
|
||||
Models,
|
||||
)
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
|
||||
from pydantic import BaseModel
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
Depends,
|
||||
@@ -32,13 +18,26 @@ from fastapi import (
|
||||
status,
|
||||
)
|
||||
from fastapi.responses import RedirectResponse, StreamingResponse
|
||||
|
||||
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.access_control import has_permission, filter_allowed_access_grants
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import ENABLE_PROFILE_IMAGE_URL_FORWARDING
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.models import (
|
||||
ModelAccessListResponse,
|
||||
ModelAccessResponse,
|
||||
ModelForm,
|
||||
ModelListResponse,
|
||||
ModelMeta,
|
||||
ModelModel,
|
||||
ModelParams,
|
||||
ModelResponse,
|
||||
Models,
|
||||
)
|
||||
from open_webui.utils.access_control import filter_allowed_access_grants, has_permission
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -46,7 +45,7 @@ log = logging.getLogger(__name__)
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _safe_static_redirect_path(url: str) -> Optional[str]:
|
||||
def _safe_static_redirect_path(url: str) -> str | None:
|
||||
"""
|
||||
If url is a same-origin static asset path, return a normalized path safe for
|
||||
RedirectResponse Location. Otherwise None (caller should fall back to default).
|
||||
@@ -90,12 +89,12 @@ PAGE_ITEM_COUNT = 30
|
||||
|
||||
@router.get('/list', response_model=ModelAccessListResponse) # do NOT use "/" as path, conflicts with main.py
|
||||
async def get_models(
|
||||
query: Optional[str] = None,
|
||||
view_option: Optional[str] = None,
|
||||
tag: Optional[str] = None,
|
||||
order_by: Optional[str] = None,
|
||||
direction: Optional[str] = None,
|
||||
page: Optional[int] = 1,
|
||||
query: str | None = None,
|
||||
view_option: str | None = None,
|
||||
tag: str | None = None,
|
||||
order_by: str | None = None,
|
||||
direction: str | None = None,
|
||||
page: int | None = 1,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -192,7 +191,7 @@ async def get_model_tags(user=Depends(get_verified_user), db: AsyncSession = Dep
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/create', response_model=Optional[ModelModel])
|
||||
@router.post('/create', response_model=ModelModel | None)
|
||||
async def create_new_model(
|
||||
request: Request,
|
||||
form_data: ModelForm,
|
||||
@@ -409,7 +408,7 @@ class ModelIdForm(BaseModel):
|
||||
|
||||
|
||||
# Note: We're not using the typical url path param here, but instead using a query parameter to allow '/' in the id
|
||||
@router.get('/model', response_model=Optional[ModelAccessResponse])
|
||||
@router.get('/model', response_model=ModelAccessResponse | None)
|
||||
async def get_model_by_id(id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
||||
model = await Models.get_model_by_id(id, db=db)
|
||||
if model:
|
||||
@@ -537,7 +536,7 @@ async def get_model_profile_image(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/model/toggle', response_model=Optional[ModelResponse])
|
||||
@router.post('/model/toggle', response_model=ModelResponse | None)
|
||||
async def toggle_model_by_id(id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
||||
model = await Models.get_model_by_id(id, db=db)
|
||||
if model:
|
||||
@@ -578,7 +577,7 @@ async def toggle_model_by_id(id: str, user=Depends(get_verified_user), db: Async
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/model/update', response_model=Optional[ModelModel])
|
||||
@router.post('/model/update', response_model=ModelModel | None)
|
||||
async def update_model_by_id(
|
||||
request: Request,
|
||||
form_data: ModelForm,
|
||||
@@ -627,11 +626,11 @@ async def update_model_by_id(
|
||||
|
||||
class ModelAccessGrantsForm(BaseModel):
|
||||
id: str
|
||||
name: Optional[str] = None
|
||||
name: str | None = None
|
||||
access_grants: list[dict]
|
||||
|
||||
|
||||
@router.post('/model/access/update', response_model=Optional[ModelModel])
|
||||
@router.post('/model/access/update', response_model=ModelModel | None)
|
||||
async def update_model_access_by_id(
|
||||
request: Request,
|
||||
form_data: ModelAccessGrantsForm,
|
||||
|
||||
@@ -2,39 +2,33 @@ import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status, BackgroundTasks
|
||||
from pydantic import BaseModel
|
||||
|
||||
from open_webui.socket.main import sio
|
||||
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.users import Users, UserResponse
|
||||
from open_webui.models.notes import (
|
||||
NoteListResponse,
|
||||
Notes,
|
||||
NoteModel,
|
||||
NoteForm,
|
||||
NoteUserResponse,
|
||||
)
|
||||
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Request, status
|
||||
from open_webui.config import (
|
||||
BYPASS_ADMIN_ACCESS_CONTROL,
|
||||
ENABLE_ADMIN_CHAT_ACCESS,
|
||||
ENABLE_ADMIN_EXPORT,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
|
||||
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.notes import (
|
||||
NoteForm,
|
||||
NoteListResponse,
|
||||
NoteModel,
|
||||
Notes,
|
||||
NoteUserResponse,
|
||||
)
|
||||
from open_webui.models.users import UserResponse, Users
|
||||
from open_webui.socket.main import sio
|
||||
from open_webui.utils.access_control import (
|
||||
filter_allowed_access_grants,
|
||||
has_permission,
|
||||
has_public_read_access_grant,
|
||||
has_public_write_access_grant,
|
||||
filter_allowed_access_grants,
|
||||
)
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
# TODO: Implement a more intelligent load balancing mechanism for distributing requests among multiple backend instances.
|
||||
# Current implementation uses a simple round-robin approach (random.choice). Consider incorporating algorithms like weighted round-robin,
|
||||
# least connections, or least response time for better resource utilization and performance optimization.
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
@@ -10,70 +11,60 @@ import random
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from typing import Optional, Union
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import aiohttp
|
||||
from aiocache import cached
|
||||
|
||||
|
||||
from open_webui.utils.headers import include_user_info_headers
|
||||
from open_webui.models.chats import Chats
|
||||
from open_webui.models.users import UserModel
|
||||
|
||||
from open_webui.env import (
|
||||
ENABLE_FORWARD_USER_INFO_HEADERS,
|
||||
FORWARD_SESSION_INFO_HEADER_CHAT_ID,
|
||||
)
|
||||
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
Depends,
|
||||
FastAPI,
|
||||
File,
|
||||
HTTPException,
|
||||
Request,
|
||||
UploadFile,
|
||||
APIRouter,
|
||||
)
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel, ConfigDict, validator
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from open_webui.config import (
|
||||
UPLOAD_DIR,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import (
|
||||
AIOHTTP_CLIENT_SESSION_SSL,
|
||||
AIOHTTP_CLIENT_TIMEOUT,
|
||||
AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
|
||||
BYPASS_MODEL_ACCESS_CONTROL,
|
||||
ENABLE_FORWARD_USER_INFO_HEADERS,
|
||||
ENV,
|
||||
FORWARD_SESSION_INFO_HEADER_CHAT_ID,
|
||||
MODELS_CACHE_TTL,
|
||||
)
|
||||
from open_webui.internal.db import get_async_session
|
||||
|
||||
|
||||
from open_webui.models.models import Models
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.chats import Chats
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.models import Models
|
||||
from open_webui.models.users import UserModel
|
||||
from open_webui.utils.access_control import check_model_access
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.headers import include_user_info_headers
|
||||
from open_webui.utils.misc import (
|
||||
calculate_sha256,
|
||||
)
|
||||
from open_webui.utils.session_pool import (
|
||||
cleanup_response,
|
||||
get_session,
|
||||
stream_wrapper,
|
||||
)
|
||||
from open_webui.utils.payload import (
|
||||
apply_model_params_to_body_ollama,
|
||||
apply_model_params_to_body_openai,
|
||||
apply_system_prompt_to_body,
|
||||
)
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.config import (
|
||||
UPLOAD_DIR,
|
||||
from open_webui.utils.session_pool import (
|
||||
cleanup_response,
|
||||
get_session,
|
||||
stream_wrapper,
|
||||
)
|
||||
from open_webui.env import (
|
||||
ENV,
|
||||
MODELS_CACHE_TTL,
|
||||
AIOHTTP_CLIENT_SESSION_SSL,
|
||||
AIOHTTP_CLIENT_TIMEOUT,
|
||||
AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
|
||||
BYPASS_MODEL_ACCESS_CONTROL,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from pydantic import BaseModel, ConfigDict, validator
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -126,12 +117,12 @@ async def send_request(
|
||||
url: str,
|
||||
method: str = 'POST',
|
||||
*,
|
||||
payload: Optional[Union[str, bytes]] = None,
|
||||
key: Optional[str] = None,
|
||||
payload: Union[str, bytes | None] = None,
|
||||
key: str | None = None,
|
||||
user: UserModel = None,
|
||||
stream: bool = False,
|
||||
content_type: Optional[str] = None,
|
||||
metadata: Optional[dict] = None,
|
||||
content_type: str | None = None,
|
||||
metadata: dict | None = None,
|
||||
):
|
||||
r = None
|
||||
streaming = False
|
||||
@@ -225,7 +216,7 @@ async def get_status():
|
||||
|
||||
class ConnectionVerificationForm(BaseModel):
|
||||
url: str
|
||||
key: Optional[str] = None
|
||||
key: str | None = None
|
||||
|
||||
|
||||
@router.post('/verify')
|
||||
@@ -279,7 +270,7 @@ async def get_config(request: Request, user=Depends(get_admin_user)):
|
||||
|
||||
|
||||
class OllamaConfigForm(BaseModel):
|
||||
ENABLE_OLLAMA_API: Optional[bool] = None
|
||||
ENABLE_OLLAMA_API: bool | None = None
|
||||
OLLAMA_BASE_URLS: list[str]
|
||||
OLLAMA_API_CONFIGS: dict
|
||||
|
||||
@@ -437,7 +428,7 @@ async def get_filtered_models(models, user, db=None):
|
||||
|
||||
@router.get('/api/tags')
|
||||
@router.get('/api/tags/{url_idx}')
|
||||
async def get_ollama_tags(request: Request, url_idx: Optional[int] = None, user=Depends(get_verified_user)):
|
||||
async def get_ollama_tags(request: Request, url_idx: int | None = None, user=Depends(get_verified_user)):
|
||||
if not request.app.state.config.ENABLE_OLLAMA_API:
|
||||
raise HTTPException(status_code=503, detail=ERROR_MESSAGES.OLLAMA_API_DISABLED)
|
||||
|
||||
@@ -514,7 +505,7 @@ async def get_ollama_loaded_models(request: Request, user=Depends(get_admin_user
|
||||
|
||||
@router.get('/api/version')
|
||||
@router.get('/api/version/{url_idx}')
|
||||
async def get_ollama_versions(request: Request, url_idx: Optional[int] = None):
|
||||
async def get_ollama_versions(request: Request, url_idx: int | None = None):
|
||||
if request.app.state.config.ENABLE_OLLAMA_API:
|
||||
if url_idx is None:
|
||||
# returns lowest version
|
||||
@@ -560,7 +551,7 @@ async def get_ollama_versions(request: Request, url_idx: Optional[int] = None):
|
||||
|
||||
|
||||
class ModelNameForm(BaseModel):
|
||||
model: Optional[str] = None
|
||||
model: str | None = None
|
||||
model_config = ConfigDict(
|
||||
extra='allow',
|
||||
)
|
||||
@@ -654,8 +645,8 @@ async def pull_model(
|
||||
|
||||
class PushModelForm(BaseModel):
|
||||
model: str
|
||||
insecure: Optional[bool] = None
|
||||
stream: Optional[bool] = None
|
||||
insecure: bool | None = None
|
||||
stream: bool | None = None
|
||||
|
||||
|
||||
@router.delete('/api/push')
|
||||
@@ -663,7 +654,7 @@ class PushModelForm(BaseModel):
|
||||
async def push_model(
|
||||
request: Request,
|
||||
form_data: PushModelForm,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_admin_user),
|
||||
):
|
||||
if not request.app.state.config.ENABLE_OLLAMA_API:
|
||||
@@ -694,9 +685,9 @@ async def push_model(
|
||||
|
||||
|
||||
class CreateModelForm(BaseModel):
|
||||
model: Optional[str] = None
|
||||
stream: Optional[bool] = None
|
||||
path: Optional[str] = None
|
||||
model: str | None = None
|
||||
stream: bool | None = None
|
||||
path: str | None = None
|
||||
|
||||
model_config = ConfigDict(extra='allow')
|
||||
|
||||
@@ -734,7 +725,7 @@ class CopyModelForm(BaseModel):
|
||||
async def copy_model(
|
||||
request: Request,
|
||||
form_data: CopyModelForm,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_admin_user),
|
||||
):
|
||||
if not request.app.state.config.ENABLE_OLLAMA_API:
|
||||
@@ -769,7 +760,7 @@ async def copy_model(
|
||||
async def delete_model(
|
||||
request: Request,
|
||||
form_data: ModelNameForm,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_admin_user),
|
||||
):
|
||||
if not request.app.state.config.ENABLE_OLLAMA_API:
|
||||
@@ -843,9 +834,9 @@ async def show_model_info(request: Request, form_data: ModelNameForm, user=Depen
|
||||
class GenerateEmbedForm(BaseModel):
|
||||
model: str
|
||||
input: list[str] | str
|
||||
truncate: Optional[bool] = None
|
||||
options: Optional[dict] = None
|
||||
keep_alive: Optional[Union[int, str]] = None
|
||||
truncate: bool | None = None
|
||||
options: dict | None = None
|
||||
keep_alive: Union[int, str | None] = None
|
||||
|
||||
model_config = ConfigDict(
|
||||
extra='allow',
|
||||
@@ -857,7 +848,7 @@ class GenerateEmbedForm(BaseModel):
|
||||
async def embed(
|
||||
request: Request,
|
||||
form_data: GenerateEmbedForm,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
if not request.app.state.config.ENABLE_OLLAMA_API:
|
||||
@@ -907,8 +898,8 @@ async def embed(
|
||||
class GenerateEmbeddingsForm(BaseModel):
|
||||
model: str
|
||||
prompt: str
|
||||
options: Optional[dict] = None
|
||||
keep_alive: Optional[Union[int, str]] = None
|
||||
options: dict | None = None
|
||||
keep_alive: Union[int, str | None] = None
|
||||
|
||||
|
||||
@router.post('/api/embeddings')
|
||||
@@ -916,7 +907,7 @@ class GenerateEmbeddingsForm(BaseModel):
|
||||
async def embeddings(
|
||||
request: Request,
|
||||
form_data: GenerateEmbeddingsForm,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
if not request.app.state.config.ENABLE_OLLAMA_API:
|
||||
@@ -965,17 +956,17 @@ async def embeddings(
|
||||
|
||||
class GenerateCompletionForm(BaseModel):
|
||||
model: str
|
||||
prompt: Optional[str] = None
|
||||
suffix: Optional[str] = None
|
||||
images: Optional[list[str]] = None
|
||||
format: Optional[Union[dict, str]] = None
|
||||
options: Optional[dict] = None
|
||||
system: Optional[str] = None
|
||||
template: Optional[str] = None
|
||||
context: Optional[list[int]] = None
|
||||
stream: Optional[bool] = True
|
||||
raw: Optional[bool] = None
|
||||
keep_alive: Optional[Union[int, str]] = None
|
||||
prompt: str | None = None
|
||||
suffix: str | None = None
|
||||
images: list[str | None] = None
|
||||
format: Union[dict, str | None] = None
|
||||
options: dict | None = None
|
||||
system: str | None = None
|
||||
template: str | None = None
|
||||
context: list[int | None] = None
|
||||
stream: bool | None = True
|
||||
raw: bool | None = None
|
||||
keep_alive: Union[int, str | None] = None
|
||||
|
||||
|
||||
@router.post('/api/generate')
|
||||
@@ -983,7 +974,7 @@ class GenerateCompletionForm(BaseModel):
|
||||
async def generate_completion(
|
||||
request: Request,
|
||||
form_data: GenerateCompletionForm,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
if not request.app.state.config.ENABLE_OLLAMA_API:
|
||||
@@ -1027,9 +1018,9 @@ async def generate_completion(
|
||||
|
||||
class ChatMessage(BaseModel):
|
||||
role: str
|
||||
content: Optional[str] = None
|
||||
tool_calls: Optional[list[dict]] = None
|
||||
images: Optional[list[str]] = None
|
||||
content: str | None = None
|
||||
tool_calls: list[dict | None] = None
|
||||
images: list[str | None] = None
|
||||
|
||||
model_config = ConfigDict(extra='allow')
|
||||
|
||||
@@ -1046,18 +1037,18 @@ class ChatMessage(BaseModel):
|
||||
class GenerateChatCompletionForm(BaseModel):
|
||||
model: str
|
||||
messages: list[ChatMessage]
|
||||
format: Optional[Union[dict, str]] = None
|
||||
options: Optional[dict] = None
|
||||
template: Optional[str] = None
|
||||
stream: Optional[bool] = True
|
||||
keep_alive: Optional[Union[int, str]] = None
|
||||
tools: Optional[list[dict]] = None
|
||||
format: Union[dict, str | None] = None
|
||||
options: dict | None = None
|
||||
template: str | None = None
|
||||
stream: bool | None = True
|
||||
keep_alive: Union[int, str | None] = None
|
||||
tools: list[dict | None] = None
|
||||
model_config = ConfigDict(
|
||||
extra='allow',
|
||||
)
|
||||
|
||||
|
||||
async def get_ollama_url(request: Request, model: str, url_idx: Optional[int] = None):
|
||||
async def get_ollama_url(request: Request, model: str, url_idx: int | None = None):
|
||||
if url_idx is None:
|
||||
models = request.app.state.OLLAMA_MODELS
|
||||
if model not in models:
|
||||
@@ -1075,7 +1066,7 @@ async def get_ollama_url(request: Request, model: str, url_idx: Optional[int] =
|
||||
async def generate_chat_completion(
|
||||
request: Request,
|
||||
form_data: dict,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
bypass_system_prompt: bool = False,
|
||||
):
|
||||
@@ -1162,7 +1153,7 @@ class OpenAIChatMessageContent(BaseModel):
|
||||
|
||||
class OpenAIChatMessage(BaseModel):
|
||||
role: str
|
||||
content: Union[Optional[str], list[OpenAIChatMessageContent]]
|
||||
content: Union[str | None, list[OpenAIChatMessageContent]]
|
||||
|
||||
model_config = ConfigDict(extra='allow')
|
||||
|
||||
@@ -1186,7 +1177,7 @@ class OpenAICompletionForm(BaseModel):
|
||||
async def generate_openai_completion(
|
||||
request: Request,
|
||||
form_data: dict,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
# NOTE: We intentionally do NOT use Depends(get_async_session) here.
|
||||
@@ -1248,7 +1239,7 @@ async def generate_openai_completion(
|
||||
async def generate_openai_chat_completion(
|
||||
request: Request,
|
||||
form_data: dict,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
# NOTE: We intentionally do NOT use Depends(get_async_session) here.
|
||||
@@ -1313,7 +1304,7 @@ async def generate_openai_chat_completion(
|
||||
async def generate_anthropic_messages(
|
||||
request: Request,
|
||||
form_data: dict,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
"""
|
||||
@@ -1371,7 +1362,7 @@ class ResponsesForm(BaseModel):
|
||||
async def generate_responses(
|
||||
request: Request,
|
||||
form_data: ResponsesForm,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
):
|
||||
"""
|
||||
@@ -1422,7 +1413,7 @@ async def generate_responses(
|
||||
@router.get('/v1/models/{url_idx}')
|
||||
async def get_openai_models(
|
||||
request: Request,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -1565,7 +1556,7 @@ async def download_file_stream(ollama_url, file_url, file_path, file_name, chunk
|
||||
async def download_model(
|
||||
request: Request,
|
||||
form_data: UrlForm,
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_admin_user),
|
||||
):
|
||||
allowed_hosts = ['https://huggingface.co/', 'https://github.com/']
|
||||
@@ -1598,7 +1589,7 @@ async def download_model(
|
||||
async def upload_model(
|
||||
request: Request,
|
||||
file: UploadFile = File(...),
|
||||
url_idx: Optional[int] = None,
|
||||
url_idx: int | None = None,
|
||||
user=Depends(get_admin_user),
|
||||
):
|
||||
if url_idx is None:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
@@ -8,62 +10,52 @@ from urllib.parse import quote, urlparse
|
||||
|
||||
import aiohttp
|
||||
from aiocache import cached
|
||||
|
||||
|
||||
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
|
||||
|
||||
from fastapi import Depends, HTTPException, Request, APIRouter, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from fastapi.responses import (
|
||||
FileResponse,
|
||||
StreamingResponse,
|
||||
JSONResponse,
|
||||
PlainTextResponse,
|
||||
StreamingResponse,
|
||||
)
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from open_webui.internal.db import get_async_session
|
||||
|
||||
from open_webui.models.models import Models
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.utils.access_control import has_connection_access, check_model_access
|
||||
from open_webui.config import (
|
||||
CACHE_DIR,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import (
|
||||
MODELS_CACHE_TTL,
|
||||
AIOHTTP_CLIENT_SESSION_SSL,
|
||||
AIOHTTP_CLIENT_TIMEOUT,
|
||||
AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
|
||||
ENABLE_FORWARD_USER_INFO_HEADERS,
|
||||
FORWARD_SESSION_INFO_HEADER_CHAT_ID,
|
||||
BYPASS_MODEL_ACCESS_CONTROL,
|
||||
ENABLE_FORWARD_USER_INFO_HEADERS,
|
||||
ENABLE_OPENAI_API_PASSTHROUGH,
|
||||
FORWARD_SESSION_INFO_HEADER_CHAT_ID,
|
||||
MODELS_CACHE_TTL,
|
||||
)
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.models import Models
|
||||
from open_webui.models.users import UserModel
|
||||
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
|
||||
|
||||
from open_webui.utils.payload import (
|
||||
apply_model_params_to_body_openai,
|
||||
apply_system_prompt_to_body,
|
||||
)
|
||||
from open_webui.utils.access_control import check_model_access, has_connection_access
|
||||
from open_webui.utils.anthropic import get_anthropic_models, is_anthropic_url
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.headers import get_custom_headers, include_user_info_headers
|
||||
from open_webui.utils.misc import (
|
||||
convert_logit_bias_input_to_json,
|
||||
stream_chunks_handler,
|
||||
)
|
||||
from open_webui.utils.payload import (
|
||||
apply_model_params_to_body_openai,
|
||||
apply_system_prompt_to_body,
|
||||
)
|
||||
from open_webui.utils.session_pool import (
|
||||
cleanup_response,
|
||||
get_session,
|
||||
stream_wrapper,
|
||||
)
|
||||
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.headers import include_user_info_headers, get_custom_headers
|
||||
from open_webui.utils.anthropic import is_anthropic_url, get_anthropic_models
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -160,7 +152,7 @@ async def get_headers_and_cookies(
|
||||
url,
|
||||
key=None,
|
||||
config=None,
|
||||
metadata: Optional[dict] = None,
|
||||
metadata: dict | None = None,
|
||||
user: UserModel = None,
|
||||
):
|
||||
cookies = {}
|
||||
@@ -256,7 +248,7 @@ async def get_config(request: Request, user=Depends(get_admin_user)):
|
||||
|
||||
|
||||
class OpenAIConfigForm(BaseModel):
|
||||
ENABLE_OPENAI_API: Optional[bool] = None
|
||||
ENABLE_OPENAI_API: bool | None = None
|
||||
OPENAI_API_BASE_URLS: list[str]
|
||||
OPENAI_API_KEYS: list[str]
|
||||
OPENAI_API_CONFIGS: dict
|
||||
@@ -493,7 +485,6 @@ async def get_filtered_models(models, user, db=None):
|
||||
return filtered_models
|
||||
|
||||
|
||||
|
||||
@cached(
|
||||
ttl=MODELS_CACHE_TTL,
|
||||
key=lambda _, user: f'openai_all_models_{user.id}' if user else 'openai_all_models',
|
||||
@@ -571,14 +562,13 @@ async def get_all_models(request: Request, user: UserModel) -> dict[str, list]:
|
||||
models = get_merged_models(map(extract_data, responses))
|
||||
log.debug(f'models: {models}')
|
||||
|
||||
|
||||
request.app.state.OPENAI_MODELS = models
|
||||
return {'data': list(models.values())}
|
||||
|
||||
|
||||
@router.get('/models')
|
||||
@router.get('/models/{url_idx}')
|
||||
async def get_models(request: Request, url_idx: Optional[int] = None, user=Depends(get_verified_user)):
|
||||
async def get_models(request: Request, url_idx: int | None = None, user=Depends(get_verified_user)):
|
||||
if not request.app.state.config.ENABLE_OPENAI_API:
|
||||
raise HTTPException(status_code=503, detail='OpenAI API is disabled')
|
||||
|
||||
@@ -670,7 +660,7 @@ class ConnectionVerificationForm(BaseModel):
|
||||
url: str
|
||||
key: str
|
||||
|
||||
config: Optional[dict] = None
|
||||
config: dict | None = None
|
||||
|
||||
|
||||
@router.post('/verify')
|
||||
@@ -1361,20 +1351,20 @@ class ResponsesForm(BaseModel):
|
||||
model_config = ConfigDict(extra='allow')
|
||||
|
||||
model: str
|
||||
input: Optional[list | str] = None
|
||||
instructions: Optional[str] = None
|
||||
stream: Optional[bool] = None
|
||||
temperature: Optional[float] = None
|
||||
max_output_tokens: Optional[int] = None
|
||||
top_p: Optional[float] = None
|
||||
tools: Optional[list] = None
|
||||
tool_choice: Optional[str | dict] = None
|
||||
text: Optional[dict] = None
|
||||
truncation: Optional[str] = None
|
||||
metadata: Optional[dict] = None
|
||||
store: Optional[bool] = None
|
||||
reasoning: Optional[dict] = None
|
||||
previous_response_id: Optional[str] = None
|
||||
input: list | str | None = None
|
||||
instructions: str | None = None
|
||||
stream: bool | None = None
|
||||
temperature: float | None = None
|
||||
max_output_tokens: int | None = None
|
||||
top_p: float | None = None
|
||||
tools: list | None = None
|
||||
tool_choice: str | dict | None = None
|
||||
text: dict | None = None
|
||||
truncation: str | None = None
|
||||
metadata: dict | None = None
|
||||
store: bool | None = None
|
||||
reasoning: dict | None = None
|
||||
previous_response_id: str | None = None
|
||||
|
||||
|
||||
@router.post('/responses')
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
from typing import Optional
|
||||
|
||||
import aiohttp
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
Depends,
|
||||
FastAPI,
|
||||
File,
|
||||
@@ -7,24 +14,14 @@ from fastapi import (
|
||||
Request,
|
||||
UploadFile,
|
||||
status,
|
||||
APIRouter,
|
||||
)
|
||||
import aiohttp
|
||||
import os
|
||||
import logging
|
||||
import shutil
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import FileResponse
|
||||
from typing import Optional
|
||||
|
||||
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL
|
||||
from open_webui.config import CACHE_DIR
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
|
||||
|
||||
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL
|
||||
from open_webui.routers.openai import get_all_models_responses
|
||||
|
||||
from open_webui.utils.auth import get_admin_user
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
from typing import Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Request
|
||||
from __future__ import annotations
|
||||
|
||||
from open_webui.models.prompts import (
|
||||
PromptForm,
|
||||
PromptUserResponse,
|
||||
PromptAccessResponse,
|
||||
PromptAccessListResponse,
|
||||
PromptModel,
|
||||
Prompts,
|
||||
)
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.prompt_history import (
|
||||
@@ -16,13 +13,18 @@ from open_webui.models.prompt_history import (
|
||||
PromptHistoryModel,
|
||||
PromptHistoryResponse,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.models.prompts import (
|
||||
PromptAccessListResponse,
|
||||
PromptAccessResponse,
|
||||
PromptForm,
|
||||
PromptModel,
|
||||
Prompts,
|
||||
PromptUserResponse,
|
||||
)
|
||||
from open_webui.utils.access_control import filter_allowed_access_grants, has_permission
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.access_control import has_permission, filter_allowed_access_grants
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
||||
from open_webui.internal.db import get_async_session
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
|
||||
class PromptVersionUpdateForm(BaseModel):
|
||||
@@ -32,7 +34,7 @@ class PromptVersionUpdateForm(BaseModel):
|
||||
class PromptMetadataForm(BaseModel):
|
||||
name: str
|
||||
command: str
|
||||
tags: Optional[list[str]] = None
|
||||
tags: list[str | None] = None
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
@@ -66,12 +68,12 @@ async def get_prompt_tags(user=Depends(get_verified_user), db: AsyncSession = De
|
||||
|
||||
@router.get('/list', response_model=PromptAccessListResponse)
|
||||
async def get_prompt_list(
|
||||
query: Optional[str] = None,
|
||||
view_option: Optional[str] = None,
|
||||
tag: Optional[str] = None,
|
||||
order_by: Optional[str] = None,
|
||||
direction: Optional[str] = None,
|
||||
page: Optional[int] = 1,
|
||||
query: str | None = None,
|
||||
view_option: str | None = None,
|
||||
tag: str | None = None,
|
||||
order_by: str | None = None,
|
||||
direction: str | None = None,
|
||||
page: int | None = 1,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -136,7 +138,7 @@ async def get_prompt_list(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/create', response_model=Optional[PromptModel])
|
||||
@router.post('/create', response_model=PromptModel | None)
|
||||
async def create_new_prompt(
|
||||
request: Request,
|
||||
form_data: PromptForm,
|
||||
@@ -191,7 +193,7 @@ async def create_new_prompt(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/command/{command}', response_model=Optional[PromptAccessResponse])
|
||||
@router.get('/command/{command}', response_model=PromptAccessResponse | None)
|
||||
async def get_prompt_by_command(
|
||||
command: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -235,7 +237,7 @@ async def get_prompt_by_command(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/id/{prompt_id}', response_model=Optional[PromptAccessResponse])
|
||||
@router.get('/id/{prompt_id}', response_model=PromptAccessResponse | None)
|
||||
async def get_prompt_by_id(
|
||||
prompt_id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -279,7 +281,7 @@ async def get_prompt_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/id/{prompt_id}/update', response_model=Optional[PromptModel])
|
||||
@router.post('/id/{prompt_id}/update', response_model=PromptModel | None)
|
||||
async def update_prompt_by_id(
|
||||
request: Request,
|
||||
prompt_id: str,
|
||||
@@ -345,7 +347,7 @@ async def update_prompt_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/id/{prompt_id}/update/meta', response_model=Optional[PromptModel])
|
||||
@router.post('/id/{prompt_id}/update/meta', response_model=PromptModel | None)
|
||||
async def update_prompt_metadata(
|
||||
prompt_id: str,
|
||||
form_data: PromptMetadataForm,
|
||||
@@ -398,7 +400,7 @@ async def update_prompt_metadata(
|
||||
)
|
||||
|
||||
|
||||
@router.post('/id/{prompt_id}/update/version', response_model=Optional[PromptModel])
|
||||
@router.post('/id/{prompt_id}/update/version', response_model=PromptModel | None)
|
||||
async def set_prompt_version(
|
||||
prompt_id: str,
|
||||
form_data: PromptVersionUpdateForm,
|
||||
@@ -447,7 +449,7 @@ class PromptAccessGrantsForm(BaseModel):
|
||||
access_grants: list[dict]
|
||||
|
||||
|
||||
@router.post('/id/{prompt_id}/access/update', response_model=Optional[PromptModel])
|
||||
@router.post('/id/{prompt_id}/access/update', response_model=PromptModel | None)
|
||||
async def update_prompt_access_by_id(
|
||||
request: Request,
|
||||
prompt_id: str,
|
||||
@@ -496,7 +498,7 @@ async def update_prompt_access_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/id/{prompt_id}/toggle', response_model=Optional[PromptModel])
|
||||
@router.post('/id/{prompt_id}/toggle', response_model=PromptModel | None)
|
||||
async def toggle_prompt_active(
|
||||
prompt_id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
|
||||
@@ -1,130 +1,122 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import mimetypes
|
||||
import os
|
||||
import shutil
|
||||
import asyncio
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Iterator, List, Optional, Sequence, Union
|
||||
from typing import Iterator, Optional, Sequence, Union
|
||||
|
||||
import tiktoken
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
Depends,
|
||||
FastAPI,
|
||||
Query,
|
||||
File,
|
||||
Form,
|
||||
HTTPException,
|
||||
UploadFile,
|
||||
Query,
|
||||
Request,
|
||||
UploadFile,
|
||||
status,
|
||||
APIRouter,
|
||||
)
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.concurrency import run_in_threadpool
|
||||
from pydantic import BaseModel
|
||||
import tiktoken
|
||||
|
||||
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from langchain_core.documents import Document
|
||||
from langchain_text_splitters import (
|
||||
MarkdownHeaderTextSplitter,
|
||||
RecursiveCharacterTextSplitter,
|
||||
TokenTextSplitter,
|
||||
MarkdownHeaderTextSplitter,
|
||||
)
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from open_webui.models.files import FileModel, FileUpdateForm, Files
|
||||
from open_webui.utils.access_control.files import has_access_to_file
|
||||
from open_webui.models.knowledge import Knowledges
|
||||
from open_webui.storage.provider import Storage
|
||||
from open_webui.internal.db import get_async_db, get_async_session
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
|
||||
from open_webui.retrieval.vector.factory import VECTOR_DB_CLIENT
|
||||
from open_webui.retrieval.vector.async_client import ASYNC_VECTOR_DB_CLIENT
|
||||
|
||||
# Document loaders
|
||||
|
||||
from open_webui.retrieval.loaders.youtube import YoutubeLoader
|
||||
|
||||
# Web search engines
|
||||
from open_webui.retrieval.web.main import SearchResult
|
||||
from open_webui.retrieval.web.utils import get_web_loader
|
||||
from open_webui.retrieval.web.ollama import search_ollama_cloud
|
||||
from open_webui.retrieval.web.perplexity_search import search_perplexity_search
|
||||
from open_webui.retrieval.web.brave import search_brave
|
||||
from open_webui.retrieval.web.brave_llm_context import search_brave_llm_context
|
||||
from open_webui.retrieval.web.kagi import search_kagi
|
||||
from open_webui.retrieval.web.mojeek import search_mojeek
|
||||
from open_webui.retrieval.web.bocha import search_bocha
|
||||
from open_webui.retrieval.web.duckduckgo import search_duckduckgo
|
||||
from open_webui.retrieval.web.google_pse import search_google_pse
|
||||
from open_webui.retrieval.web.jina_search import search_jina
|
||||
from open_webui.retrieval.web.searchapi import search_searchapi
|
||||
from open_webui.retrieval.web.serpapi import search_serpapi
|
||||
from open_webui.retrieval.web.searxng import search_searxng
|
||||
from open_webui.retrieval.web.yacy import search_yacy
|
||||
from open_webui.retrieval.web.serper import search_serper
|
||||
from open_webui.retrieval.web.serply import search_serply
|
||||
from open_webui.retrieval.web.serpstack import search_serpstack
|
||||
from open_webui.retrieval.web.tavily import search_tavily
|
||||
from open_webui.retrieval.web.bing import search_bing
|
||||
from open_webui.retrieval.web.azure import search_azure
|
||||
from open_webui.retrieval.web.exa import search_exa
|
||||
from open_webui.retrieval.web.perplexity import search_perplexity
|
||||
from open_webui.retrieval.web.sougou import search_sougou
|
||||
from open_webui.retrieval.web.firecrawl import search_firecrawl
|
||||
from open_webui.retrieval.web.external import search_external
|
||||
from open_webui.retrieval.web.yandex import search_yandex
|
||||
from open_webui.retrieval.web.ydc import search_youcom
|
||||
|
||||
from open_webui.retrieval.utils import (
|
||||
build_loader_from_config,
|
||||
filter_accessible_collections,
|
||||
get_content_from_url,
|
||||
get_embedding_function,
|
||||
get_reranking_function,
|
||||
get_model_path,
|
||||
query_collection,
|
||||
query_collection_with_hybrid_search,
|
||||
query_doc,
|
||||
query_doc_with_hybrid_search,
|
||||
)
|
||||
from open_webui.retrieval.vector.utils import filter_metadata
|
||||
from open_webui.utils.misc import (
|
||||
calculate_sha256_string,
|
||||
sanitize_text_for_db,
|
||||
)
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.access_control import has_permission
|
||||
|
||||
from open_webui.config import (
|
||||
DEFAULT_LOCALE,
|
||||
ENV,
|
||||
RAG_EMBEDDING_CONTENT_PREFIX,
|
||||
RAG_EMBEDDING_MODEL_AUTO_UPDATE,
|
||||
RAG_EMBEDDING_MODEL_TRUST_REMOTE_CODE,
|
||||
RAG_EMBEDDING_QUERY_PREFIX,
|
||||
RAG_RERANKING_MODEL_AUTO_UPDATE,
|
||||
RAG_RERANKING_MODEL_TRUST_REMOTE_CODE,
|
||||
UPLOAD_DIR,
|
||||
DEFAULT_LOCALE,
|
||||
RAG_EMBEDDING_CONTENT_PREFIX,
|
||||
RAG_EMBEDDING_QUERY_PREFIX,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import (
|
||||
DEVICE_TYPE,
|
||||
DOCKER,
|
||||
RAG_EMBEDDING_TIMEOUT,
|
||||
SENTENCE_TRANSFORMERS_BACKEND,
|
||||
SENTENCE_TRANSFORMERS_MODEL_KWARGS,
|
||||
SENTENCE_TRANSFORMERS_CROSS_ENCODER_BACKEND,
|
||||
SENTENCE_TRANSFORMERS_CROSS_ENCODER_MODEL_KWARGS,
|
||||
SENTENCE_TRANSFORMERS_CROSS_ENCODER_SIGMOID_ACTIVATION_FUNCTION,
|
||||
SENTENCE_TRANSFORMERS_MODEL_KWARGS,
|
||||
)
|
||||
from open_webui.internal.db import get_async_db, get_async_session
|
||||
from open_webui.models.files import FileModel, Files, FileUpdateForm
|
||||
from open_webui.models.knowledge import Knowledges
|
||||
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
# Document loaders
|
||||
from open_webui.retrieval.loaders.youtube import YoutubeLoader
|
||||
from open_webui.retrieval.utils import (
|
||||
build_loader_from_config,
|
||||
filter_accessible_collections,
|
||||
get_content_from_url,
|
||||
get_embedding_function,
|
||||
get_model_path,
|
||||
get_reranking_function,
|
||||
query_collection,
|
||||
query_collection_with_hybrid_search,
|
||||
query_doc,
|
||||
query_doc_with_hybrid_search,
|
||||
)
|
||||
from open_webui.retrieval.vector.async_client import ASYNC_VECTOR_DB_CLIENT
|
||||
from open_webui.retrieval.vector.factory import VECTOR_DB_CLIENT
|
||||
from open_webui.retrieval.vector.utils import filter_metadata
|
||||
from open_webui.retrieval.web.azure import search_azure
|
||||
from open_webui.retrieval.web.bing import search_bing
|
||||
from open_webui.retrieval.web.bocha import search_bocha
|
||||
from open_webui.retrieval.web.brave import search_brave
|
||||
from open_webui.retrieval.web.brave_llm_context import search_brave_llm_context
|
||||
from open_webui.retrieval.web.duckduckgo import search_duckduckgo
|
||||
from open_webui.retrieval.web.exa import search_exa
|
||||
from open_webui.retrieval.web.external import search_external
|
||||
from open_webui.retrieval.web.firecrawl import search_firecrawl
|
||||
from open_webui.retrieval.web.google_pse import search_google_pse
|
||||
from open_webui.retrieval.web.jina_search import search_jina
|
||||
from open_webui.retrieval.web.kagi import search_kagi
|
||||
|
||||
# Web search engines
|
||||
from open_webui.retrieval.web.main import SearchResult
|
||||
from open_webui.retrieval.web.mojeek import search_mojeek
|
||||
from open_webui.retrieval.web.ollama import search_ollama_cloud
|
||||
from open_webui.retrieval.web.perplexity import search_perplexity
|
||||
from open_webui.retrieval.web.perplexity_search import search_perplexity_search
|
||||
from open_webui.retrieval.web.searchapi import search_searchapi
|
||||
from open_webui.retrieval.web.searxng import search_searxng
|
||||
from open_webui.retrieval.web.serpapi import search_serpapi
|
||||
from open_webui.retrieval.web.serper import search_serper
|
||||
from open_webui.retrieval.web.serply import search_serply
|
||||
from open_webui.retrieval.web.serpstack import search_serpstack
|
||||
from open_webui.retrieval.web.sougou import search_sougou
|
||||
from open_webui.retrieval.web.tavily import search_tavily
|
||||
from open_webui.retrieval.web.utils import get_web_loader
|
||||
from open_webui.retrieval.web.yacy import search_yacy
|
||||
from open_webui.retrieval.web.yandex import search_yandex
|
||||
from open_webui.retrieval.web.ydc import search_youcom
|
||||
from open_webui.storage.provider import Storage
|
||||
from open_webui.utils.access_control import has_permission
|
||||
from open_webui.utils.access_control.files import has_access_to_file
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.misc import (
|
||||
calculate_sha256_string,
|
||||
sanitize_text_for_db,
|
||||
)
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -162,7 +154,7 @@ def get_ef(
|
||||
|
||||
def get_rf(
|
||||
engine: str = '',
|
||||
reranking_model: Optional[str] = None,
|
||||
reranking_model: str | None = None,
|
||||
external_reranker_url: str = '',
|
||||
external_reranker_api_key: str = '',
|
||||
external_reranker_timeout: str = '',
|
||||
@@ -249,7 +241,7 @@ router = APIRouter()
|
||||
|
||||
|
||||
class CollectionNameForm(BaseModel):
|
||||
collection_name: Optional[str] = None
|
||||
collection_name: str | None = None
|
||||
|
||||
|
||||
class ProcessUrlForm(CollectionNameForm):
|
||||
@@ -257,7 +249,7 @@ class ProcessUrlForm(CollectionNameForm):
|
||||
|
||||
|
||||
class SearchForm(BaseModel):
|
||||
queries: List[str]
|
||||
queries: list[str]
|
||||
|
||||
|
||||
@router.get('/embedding')
|
||||
@@ -302,14 +294,14 @@ class AzureOpenAIConfigForm(BaseModel):
|
||||
|
||||
|
||||
class EmbeddingModelUpdateForm(BaseModel):
|
||||
openai_config: Optional[OpenAIConfigForm] = None
|
||||
ollama_config: Optional[OllamaConfigForm] = None
|
||||
azure_openai_config: Optional[AzureOpenAIConfigForm] = None
|
||||
openai_config: OpenAIConfigForm | None = None
|
||||
ollama_config: OllamaConfigForm | None = None
|
||||
azure_openai_config: AzureOpenAIConfigForm | None = None
|
||||
RAG_EMBEDDING_ENGINE: str
|
||||
RAG_EMBEDDING_MODEL: str
|
||||
RAG_EMBEDDING_BATCH_SIZE: Optional[int] = 1
|
||||
ENABLE_ASYNC_EMBEDDING: Optional[bool] = True
|
||||
RAG_EMBEDDING_CONCURRENT_REQUESTS: Optional[int] = 0
|
||||
RAG_EMBEDDING_BATCH_SIZE: int | None = 1
|
||||
ENABLE_ASYNC_EMBEDDING: bool | None = True
|
||||
RAG_EMBEDDING_CONCURRENT_REQUESTS: int | None = 0
|
||||
|
||||
|
||||
def unload_embedding_model(request: Request):
|
||||
@@ -566,153 +558,153 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
|
||||
|
||||
|
||||
class WebConfig(BaseModel):
|
||||
ENABLE_WEB_SEARCH: Optional[bool] = None
|
||||
WEB_SEARCH_ENGINE: Optional[str] = None
|
||||
WEB_SEARCH_TRUST_ENV: Optional[bool] = None
|
||||
WEB_SEARCH_RESULT_COUNT: Optional[int] = None
|
||||
WEB_SEARCH_CONCURRENT_REQUESTS: Optional[int] = None
|
||||
WEB_SEARCH_DOMAIN_FILTER_LIST: Optional[List[str]] = []
|
||||
WEB_FETCH_MAX_CONTENT_LENGTH: Optional[int] = None
|
||||
WEB_LOADER_CONCURRENT_REQUESTS: Optional[int] = None
|
||||
BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL: Optional[bool] = None
|
||||
BYPASS_WEB_SEARCH_WEB_LOADER: Optional[bool] = None
|
||||
OLLAMA_CLOUD_WEB_SEARCH_API_KEY: Optional[str] = None
|
||||
SEARXNG_QUERY_URL: Optional[str] = None
|
||||
SEARXNG_LANGUAGE: Optional[str] = None
|
||||
YACY_QUERY_URL: Optional[str] = None
|
||||
YACY_USERNAME: Optional[str] = None
|
||||
YACY_PASSWORD: Optional[str] = None
|
||||
GOOGLE_PSE_API_KEY: Optional[str] = None
|
||||
GOOGLE_PSE_ENGINE_ID: Optional[str] = None
|
||||
BRAVE_SEARCH_API_KEY: Optional[str] = None
|
||||
BRAVE_SEARCH_CONTEXT_TOKENS: Optional[int] = None
|
||||
KAGI_SEARCH_API_KEY: Optional[str] = None
|
||||
MOJEEK_SEARCH_API_KEY: Optional[str] = None
|
||||
BOCHA_SEARCH_API_KEY: Optional[str] = None
|
||||
SERPSTACK_API_KEY: Optional[str] = None
|
||||
SERPSTACK_HTTPS: Optional[bool] = None
|
||||
SERPER_API_KEY: Optional[str] = None
|
||||
SERPLY_API_KEY: Optional[str] = None
|
||||
DDGS_BACKEND: Optional[str] = None
|
||||
TAVILY_API_KEY: Optional[str] = None
|
||||
SEARCHAPI_API_KEY: Optional[str] = None
|
||||
SEARCHAPI_ENGINE: Optional[str] = None
|
||||
SERPAPI_API_KEY: Optional[str] = None
|
||||
SERPAPI_ENGINE: Optional[str] = None
|
||||
JINA_API_KEY: Optional[str] = None
|
||||
JINA_API_BASE_URL: Optional[str] = None
|
||||
BING_SEARCH_V7_ENDPOINT: Optional[str] = None
|
||||
BING_SEARCH_V7_SUBSCRIPTION_KEY: Optional[str] = None
|
||||
EXA_API_KEY: Optional[str] = None
|
||||
PERPLEXITY_API_KEY: Optional[str] = None
|
||||
PERPLEXITY_MODEL: Optional[str] = None
|
||||
PERPLEXITY_SEARCH_CONTEXT_USAGE: Optional[str] = None
|
||||
PERPLEXITY_SEARCH_API_URL: Optional[str] = None
|
||||
SOUGOU_API_SID: Optional[str] = None
|
||||
SOUGOU_API_SK: Optional[str] = None
|
||||
WEB_LOADER_ENGINE: Optional[str] = None
|
||||
WEB_LOADER_TIMEOUT: Optional[str] = None
|
||||
ENABLE_WEB_LOADER_SSL_VERIFICATION: Optional[bool] = None
|
||||
PLAYWRIGHT_WS_URL: Optional[str] = None
|
||||
PLAYWRIGHT_TIMEOUT: Optional[int] = None
|
||||
FIRECRAWL_API_KEY: Optional[str] = None
|
||||
FIRECRAWL_API_BASE_URL: Optional[str] = None
|
||||
FIRECRAWL_TIMEOUT: Optional[str] = None
|
||||
TAVILY_EXTRACT_DEPTH: Optional[str] = None
|
||||
EXTERNAL_WEB_SEARCH_URL: Optional[str] = None
|
||||
EXTERNAL_WEB_SEARCH_API_KEY: Optional[str] = None
|
||||
EXTERNAL_WEB_LOADER_URL: Optional[str] = None
|
||||
EXTERNAL_WEB_LOADER_API_KEY: Optional[str] = None
|
||||
YOUTUBE_LOADER_LANGUAGE: Optional[List[str]] = None
|
||||
YOUTUBE_LOADER_PROXY_URL: Optional[str] = None
|
||||
YOUTUBE_LOADER_TRANSLATION: Optional[str] = None
|
||||
YANDEX_WEB_SEARCH_URL: Optional[str] = None
|
||||
YANDEX_WEB_SEARCH_API_KEY: Optional[str] = None
|
||||
YANDEX_WEB_SEARCH_CONFIG: Optional[str] = None
|
||||
YOUCOM_API_KEY: Optional[str] = None
|
||||
ENABLE_WEB_SEARCH: bool | None = None
|
||||
WEB_SEARCH_ENGINE: str | None = None
|
||||
WEB_SEARCH_TRUST_ENV: bool | None = None
|
||||
WEB_SEARCH_RESULT_COUNT: int | None = None
|
||||
WEB_SEARCH_CONCURRENT_REQUESTS: int | None = None
|
||||
WEB_SEARCH_DOMAIN_FILTER_LIST: list[str | None] = []
|
||||
WEB_FETCH_MAX_CONTENT_LENGTH: int | None = None
|
||||
WEB_LOADER_CONCURRENT_REQUESTS: int | None = None
|
||||
BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL: bool | None = None
|
||||
BYPASS_WEB_SEARCH_WEB_LOADER: bool | None = None
|
||||
OLLAMA_CLOUD_WEB_SEARCH_API_KEY: str | None = None
|
||||
SEARXNG_QUERY_URL: str | None = None
|
||||
SEARXNG_LANGUAGE: str | None = None
|
||||
YACY_QUERY_URL: str | None = None
|
||||
YACY_USERNAME: str | None = None
|
||||
YACY_PASSWORD: str | None = None
|
||||
GOOGLE_PSE_API_KEY: str | None = None
|
||||
GOOGLE_PSE_ENGINE_ID: str | None = None
|
||||
BRAVE_SEARCH_API_KEY: str | None = None
|
||||
BRAVE_SEARCH_CONTEXT_TOKENS: int | None = None
|
||||
KAGI_SEARCH_API_KEY: str | None = None
|
||||
MOJEEK_SEARCH_API_KEY: str | None = None
|
||||
BOCHA_SEARCH_API_KEY: str | None = None
|
||||
SERPSTACK_API_KEY: str | None = None
|
||||
SERPSTACK_HTTPS: bool | None = None
|
||||
SERPER_API_KEY: str | None = None
|
||||
SERPLY_API_KEY: str | None = None
|
||||
DDGS_BACKEND: str | None = None
|
||||
TAVILY_API_KEY: str | None = None
|
||||
SEARCHAPI_API_KEY: str | None = None
|
||||
SEARCHAPI_ENGINE: str | None = None
|
||||
SERPAPI_API_KEY: str | None = None
|
||||
SERPAPI_ENGINE: str | None = None
|
||||
JINA_API_KEY: str | None = None
|
||||
JINA_API_BASE_URL: str | None = None
|
||||
BING_SEARCH_V7_ENDPOINT: str | None = None
|
||||
BING_SEARCH_V7_SUBSCRIPTION_KEY: str | None = None
|
||||
EXA_API_KEY: str | None = None
|
||||
PERPLEXITY_API_KEY: str | None = None
|
||||
PERPLEXITY_MODEL: str | None = None
|
||||
PERPLEXITY_SEARCH_CONTEXT_USAGE: str | None = None
|
||||
PERPLEXITY_SEARCH_API_URL: str | None = None
|
||||
SOUGOU_API_SID: str | None = None
|
||||
SOUGOU_API_SK: str | None = None
|
||||
WEB_LOADER_ENGINE: str | None = None
|
||||
WEB_LOADER_TIMEOUT: str | None = None
|
||||
ENABLE_WEB_LOADER_SSL_VERIFICATION: bool | None = None
|
||||
PLAYWRIGHT_WS_URL: str | None = None
|
||||
PLAYWRIGHT_TIMEOUT: int | None = None
|
||||
FIRECRAWL_API_KEY: str | None = None
|
||||
FIRECRAWL_API_BASE_URL: str | None = None
|
||||
FIRECRAWL_TIMEOUT: str | None = None
|
||||
TAVILY_EXTRACT_DEPTH: str | None = None
|
||||
EXTERNAL_WEB_SEARCH_URL: str | None = None
|
||||
EXTERNAL_WEB_SEARCH_API_KEY: str | None = None
|
||||
EXTERNAL_WEB_LOADER_URL: str | None = None
|
||||
EXTERNAL_WEB_LOADER_API_KEY: str | None = None
|
||||
YOUTUBE_LOADER_LANGUAGE: list[str | None] = None
|
||||
YOUTUBE_LOADER_PROXY_URL: str | None = None
|
||||
YOUTUBE_LOADER_TRANSLATION: str | None = None
|
||||
YANDEX_WEB_SEARCH_URL: str | None = None
|
||||
YANDEX_WEB_SEARCH_API_KEY: str | None = None
|
||||
YANDEX_WEB_SEARCH_CONFIG: str | None = None
|
||||
YOUCOM_API_KEY: str | None = None
|
||||
|
||||
|
||||
class ConfigForm(BaseModel):
|
||||
# RAG settings
|
||||
RAG_TEMPLATE: Optional[str] = None
|
||||
TOP_K: Optional[int] = None
|
||||
BYPASS_EMBEDDING_AND_RETRIEVAL: Optional[bool] = None
|
||||
RAG_FULL_CONTEXT: Optional[bool] = None
|
||||
RAG_TEMPLATE: str | None = None
|
||||
TOP_K: int | None = None
|
||||
BYPASS_EMBEDDING_AND_RETRIEVAL: bool | None = None
|
||||
RAG_FULL_CONTEXT: bool | None = None
|
||||
|
||||
# Hybrid search settings
|
||||
ENABLE_RAG_HYBRID_SEARCH: Optional[bool] = None
|
||||
ENABLE_RAG_HYBRID_SEARCH_ENRICHED_TEXTS: Optional[bool] = None
|
||||
TOP_K_RERANKER: Optional[int] = None
|
||||
RELEVANCE_THRESHOLD: Optional[float] = None
|
||||
HYBRID_BM25_WEIGHT: Optional[float] = None
|
||||
ENABLE_RAG_HYBRID_SEARCH: bool | None = None
|
||||
ENABLE_RAG_HYBRID_SEARCH_ENRICHED_TEXTS: bool | None = None
|
||||
TOP_K_RERANKER: int | None = None
|
||||
RELEVANCE_THRESHOLD: float | None = None
|
||||
HYBRID_BM25_WEIGHT: float | None = None
|
||||
|
||||
# Content extraction settings
|
||||
CONTENT_EXTRACTION_ENGINE: Optional[str] = None
|
||||
PDF_EXTRACT_IMAGES: Optional[bool] = None
|
||||
PDF_LOADER_MODE: Optional[str] = None
|
||||
CONTENT_EXTRACTION_ENGINE: str | None = None
|
||||
PDF_EXTRACT_IMAGES: bool | None = None
|
||||
PDF_LOADER_MODE: str | None = None
|
||||
|
||||
DATALAB_MARKER_API_KEY: Optional[str] = None
|
||||
DATALAB_MARKER_API_BASE_URL: Optional[str] = None
|
||||
DATALAB_MARKER_ADDITIONAL_CONFIG: Optional[str] = None
|
||||
DATALAB_MARKER_SKIP_CACHE: Optional[bool] = None
|
||||
DATALAB_MARKER_FORCE_OCR: Optional[bool] = None
|
||||
DATALAB_MARKER_PAGINATE: Optional[bool] = None
|
||||
DATALAB_MARKER_STRIP_EXISTING_OCR: Optional[bool] = None
|
||||
DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION: Optional[bool] = None
|
||||
DATALAB_MARKER_FORMAT_LINES: Optional[bool] = None
|
||||
DATALAB_MARKER_USE_LLM: Optional[bool] = None
|
||||
DATALAB_MARKER_OUTPUT_FORMAT: Optional[str] = None
|
||||
DATALAB_MARKER_API_KEY: str | None = None
|
||||
DATALAB_MARKER_API_BASE_URL: str | None = None
|
||||
DATALAB_MARKER_ADDITIONAL_CONFIG: str | None = None
|
||||
DATALAB_MARKER_SKIP_CACHE: bool | None = None
|
||||
DATALAB_MARKER_FORCE_OCR: bool | None = None
|
||||
DATALAB_MARKER_PAGINATE: bool | None = None
|
||||
DATALAB_MARKER_STRIP_EXISTING_OCR: bool | None = None
|
||||
DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION: bool | None = None
|
||||
DATALAB_MARKER_FORMAT_LINES: bool | None = None
|
||||
DATALAB_MARKER_USE_LLM: bool | None = None
|
||||
DATALAB_MARKER_OUTPUT_FORMAT: str | None = None
|
||||
|
||||
EXTERNAL_DOCUMENT_LOADER_URL: Optional[str] = None
|
||||
EXTERNAL_DOCUMENT_LOADER_API_KEY: Optional[str] = None
|
||||
EXTERNAL_DOCUMENT_LOADER_URL: str | None = None
|
||||
EXTERNAL_DOCUMENT_LOADER_API_KEY: str | None = None
|
||||
|
||||
TIKA_SERVER_URL: Optional[str] = None
|
||||
DOCLING_SERVER_URL: Optional[str] = None
|
||||
DOCLING_API_KEY: Optional[str] = None
|
||||
DOCLING_PARAMS: Optional[dict] = None
|
||||
DOCUMENT_INTELLIGENCE_ENDPOINT: Optional[str] = None
|
||||
DOCUMENT_INTELLIGENCE_KEY: Optional[str] = None
|
||||
DOCUMENT_INTELLIGENCE_MODEL: Optional[str] = None
|
||||
MISTRAL_OCR_API_BASE_URL: Optional[str] = None
|
||||
MISTRAL_OCR_API_KEY: Optional[str] = None
|
||||
PADDLEOCR_VL_BASE_URL: Optional[str] = None
|
||||
PADDLEOCR_VL_TOKEN: Optional[str] = None
|
||||
TIKA_SERVER_URL: str | None = None
|
||||
DOCLING_SERVER_URL: str | None = None
|
||||
DOCLING_API_KEY: str | None = None
|
||||
DOCLING_PARAMS: dict | None = None
|
||||
DOCUMENT_INTELLIGENCE_ENDPOINT: str | None = None
|
||||
DOCUMENT_INTELLIGENCE_KEY: str | None = None
|
||||
DOCUMENT_INTELLIGENCE_MODEL: str | None = None
|
||||
MISTRAL_OCR_API_BASE_URL: str | None = None
|
||||
MISTRAL_OCR_API_KEY: str | None = None
|
||||
PADDLEOCR_VL_BASE_URL: str | None = None
|
||||
PADDLEOCR_VL_TOKEN: str | None = None
|
||||
|
||||
# MinerU settings
|
||||
MINERU_API_MODE: Optional[str] = None
|
||||
MINERU_API_URL: Optional[str] = None
|
||||
MINERU_API_KEY: Optional[str] = None
|
||||
MINERU_API_TIMEOUT: Optional[str] = None
|
||||
MINERU_PARAMS: Optional[dict] = None
|
||||
MINERU_API_MODE: str | None = None
|
||||
MINERU_API_URL: str | None = None
|
||||
MINERU_API_KEY: str | None = None
|
||||
MINERU_API_TIMEOUT: str | None = None
|
||||
MINERU_PARAMS: dict | None = None
|
||||
|
||||
# Reranking settings
|
||||
RAG_RERANKING_MODEL: Optional[str] = None
|
||||
RAG_RERANKING_ENGINE: Optional[str] = None
|
||||
RAG_RERANKING_BATCH_SIZE: Optional[int] = None
|
||||
RAG_EXTERNAL_RERANKER_URL: Optional[str] = None
|
||||
RAG_EXTERNAL_RERANKER_API_KEY: Optional[str] = None
|
||||
RAG_EXTERNAL_RERANKER_TIMEOUT: Optional[str] = None
|
||||
RAG_RERANKING_MODEL: str | None = None
|
||||
RAG_RERANKING_ENGINE: str | None = None
|
||||
RAG_RERANKING_BATCH_SIZE: int | None = None
|
||||
RAG_EXTERNAL_RERANKER_URL: str | None = None
|
||||
RAG_EXTERNAL_RERANKER_API_KEY: str | None = None
|
||||
RAG_EXTERNAL_RERANKER_TIMEOUT: str | None = None
|
||||
|
||||
# Chunking settings
|
||||
TEXT_SPLITTER: Optional[str] = None
|
||||
ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER: Optional[bool] = None
|
||||
CHUNK_SIZE: Optional[int] = None
|
||||
CHUNK_MIN_SIZE_TARGET: Optional[int] = None
|
||||
CHUNK_OVERLAP: Optional[int] = None
|
||||
TEXT_SPLITTER: str | None = None
|
||||
ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER: bool | None = None
|
||||
CHUNK_SIZE: int | None = None
|
||||
CHUNK_MIN_SIZE_TARGET: int | None = None
|
||||
CHUNK_OVERLAP: int | None = None
|
||||
|
||||
# File upload settings
|
||||
FILE_MAX_SIZE: Optional[Union[int, str]] = None
|
||||
FILE_MAX_COUNT: Optional[Union[int, str]] = None
|
||||
FILE_IMAGE_COMPRESSION_WIDTH: Optional[Union[int, str]] = None
|
||||
FILE_IMAGE_COMPRESSION_HEIGHT: Optional[Union[int, str]] = None
|
||||
ALLOWED_FILE_EXTENSIONS: Optional[List[str]] = None
|
||||
FILE_MAX_SIZE: Union[int, str | None] = None
|
||||
FILE_MAX_COUNT: Union[int, str | None] = None
|
||||
FILE_IMAGE_COMPRESSION_WIDTH: Union[int, str | None] = None
|
||||
FILE_IMAGE_COMPRESSION_HEIGHT: Union[int, str | None] = None
|
||||
ALLOWED_FILE_EXTENSIONS: list[str | None] = None
|
||||
|
||||
# Integration settings
|
||||
ENABLE_GOOGLE_DRIVE_INTEGRATION: Optional[bool] = None
|
||||
ENABLE_ONEDRIVE_INTEGRATION: Optional[bool] = None
|
||||
ENABLE_GOOGLE_DRIVE_INTEGRATION: bool | None = None
|
||||
ENABLE_ONEDRIVE_INTEGRATION: bool | None = None
|
||||
|
||||
# Web search settings
|
||||
web: Optional[WebConfig] = None
|
||||
web: WebConfig | None = None
|
||||
|
||||
|
||||
@router.post('/config/update')
|
||||
@@ -1341,7 +1333,7 @@ def save_docs_to_vector_db(
|
||||
request: Request,
|
||||
docs,
|
||||
collection_name,
|
||||
metadata: Optional[dict] = None,
|
||||
metadata: dict | None = None,
|
||||
overwrite: bool = False,
|
||||
split: bool = True,
|
||||
add: bool = False,
|
||||
@@ -1539,8 +1531,8 @@ def save_docs_to_vector_db(
|
||||
|
||||
class ProcessFileForm(BaseModel):
|
||||
file_id: str
|
||||
content: Optional[str] = None
|
||||
collection_name: Optional[str] = None
|
||||
content: str | None = None
|
||||
collection_name: str | None = None
|
||||
|
||||
|
||||
@router.post('/process/file')
|
||||
@@ -1767,7 +1759,7 @@ async def process_file(
|
||||
class ProcessTextForm(BaseModel):
|
||||
name: str
|
||||
content: str
|
||||
collection_name: Optional[str] = None
|
||||
collection_name: str | None = None
|
||||
|
||||
|
||||
@router.post('/process/text')
|
||||
@@ -2357,10 +2349,10 @@ async def _validate_collection_access(collection_names: list[str], user, access_
|
||||
class QueryDocForm(BaseModel):
|
||||
collection_name: str
|
||||
query: str
|
||||
k: Optional[int] = None
|
||||
k_reranker: Optional[int] = None
|
||||
r: Optional[float] = None
|
||||
hybrid: Optional[bool] = None
|
||||
k: int | None = None
|
||||
k_reranker: int | None = None
|
||||
r: float | None = None
|
||||
hybrid: bool | None = None
|
||||
|
||||
|
||||
@router.post('/query/doc')
|
||||
@@ -2423,12 +2415,12 @@ async def query_doc_handler(
|
||||
class QueryCollectionsForm(BaseModel):
|
||||
collection_names: list[str]
|
||||
query: str
|
||||
k: Optional[int] = None
|
||||
k_reranker: Optional[int] = None
|
||||
r: Optional[float] = None
|
||||
hybrid: Optional[bool] = None
|
||||
hybrid_bm25_weight: Optional[float] = None
|
||||
enable_enriched_texts: Optional[bool] = None
|
||||
k: int | None = None
|
||||
k_reranker: int | None = None
|
||||
r: float | None = None
|
||||
hybrid: bool | None = None
|
||||
hybrid_bm25_weight: float | None = None
|
||||
enable_enriched_texts: bool | None = None
|
||||
|
||||
|
||||
@router.post('/query/collection')
|
||||
@@ -2581,24 +2573,24 @@ async def reset_upload_dir(user=Depends(get_admin_user)) -> bool:
|
||||
if ENV == 'dev':
|
||||
|
||||
@router.get('/ef/{text}')
|
||||
async def get_embeddings(request: Request, text: Optional[str] = 'Hello World!'):
|
||||
async def get_embeddings(request: Request, text: str | None = 'Hello World!'):
|
||||
return {'result': await request.app.state.EMBEDDING_FUNCTION(text, prefix=RAG_EMBEDDING_QUERY_PREFIX)}
|
||||
|
||||
|
||||
class BatchProcessFilesForm(BaseModel):
|
||||
files: List[FileModel]
|
||||
files: list[FileModel]
|
||||
collection_name: str
|
||||
|
||||
|
||||
class BatchProcessFilesResult(BaseModel):
|
||||
file_id: str
|
||||
status: str
|
||||
error: Optional[str] = None
|
||||
error: str | None = None
|
||||
|
||||
|
||||
class BatchProcessFilesResponse(BaseModel):
|
||||
results: List[BatchProcessFilesResult]
|
||||
errors: List[BatchProcessFilesResult]
|
||||
results: list[BatchProcessFilesResult]
|
||||
errors: list[BatchProcessFilesResult]
|
||||
|
||||
|
||||
@router.post('/process/files/batch')
|
||||
@@ -2622,12 +2614,12 @@ async def process_files_batch(
|
||||
if collection_name:
|
||||
await _validate_collection_access([collection_name], user, access_type='write')
|
||||
|
||||
file_results: List[BatchProcessFilesResult] = []
|
||||
file_errors: List[BatchProcessFilesResult] = []
|
||||
file_updates: List[FileUpdateForm] = []
|
||||
file_results: list[BatchProcessFilesResult] = []
|
||||
file_errors: list[BatchProcessFilesResult] = []
|
||||
file_updates: list[FileUpdateForm] = []
|
||||
|
||||
# Prepare all documents first
|
||||
all_docs: List[Document] = []
|
||||
all_docs: list[Document] = []
|
||||
|
||||
for file in form_data.files:
|
||||
try:
|
||||
@@ -2653,7 +2645,7 @@ async def process_files_batch(
|
||||
continue
|
||||
|
||||
text_content = file.data.get('content', '')
|
||||
docs: List[Document] = [
|
||||
docs: list[Document] = [
|
||||
Document(
|
||||
page_content=text_content.replace('<br/>', '\n'),
|
||||
metadata={
|
||||
|
||||
@@ -7,31 +7,27 @@ NOTE: This is an experimental implementation and may not fully comply with SCIM
|
||||
|
||||
import hmac
|
||||
import logging
|
||||
import uuid
|
||||
import time
|
||||
from typing import Optional, List, Dict, Any
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Query, Header, status
|
||||
from fastapi import APIRouter, Depends, Header, HTTPException, Query, Request, status
|
||||
from fastapi.responses import JSONResponse
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
|
||||
from open_webui.models.users import Users, UserModel
|
||||
from open_webui.models.groups import Groups, GroupModel
|
||||
from open_webui.config import OAUTH_PROVIDERS
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import SCIM_AUTH_PROVIDER
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.groups import GroupModel, Groups
|
||||
from open_webui.models.users import UserModel, Users
|
||||
from open_webui.utils.auth import (
|
||||
decode_token,
|
||||
get_admin_user,
|
||||
get_current_user,
|
||||
decode_token,
|
||||
get_verified_user,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
|
||||
from open_webui.config import OAUTH_PROVIDERS
|
||||
from open_webui.env import SCIM_AUTH_PROVIDER
|
||||
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from open_webui.internal.db import get_async_session
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from open_webui.models.groups import Groups
|
||||
from pydantic import BaseModel
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.internal.db import get_async_session
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.skills import (
|
||||
SkillAccessListResponse,
|
||||
SkillAccessResponse,
|
||||
SkillForm,
|
||||
SkillModel,
|
||||
SkillResponse,
|
||||
SkillUserResponse,
|
||||
SkillAccessResponse,
|
||||
SkillAccessListResponse,
|
||||
Skills,
|
||||
SkillUserResponse,
|
||||
)
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.utils.access_control import filter_allowed_access_grants, has_permission
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.access_control import has_permission, filter_allowed_access_grants
|
||||
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -1,40 +1,36 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Response, status, Request
|
||||
from fastapi.responses import JSONResponse, RedirectResponse
|
||||
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
import logging
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
from open_webui.utils.chat import generate_chat_completion
|
||||
from open_webui.utils.task import (
|
||||
title_generation_template,
|
||||
follow_up_generation_template,
|
||||
query_generation_template,
|
||||
image_prompt_generation_template,
|
||||
autocomplete_generation_template,
|
||||
tags_generation_template,
|
||||
emoji_generation_template,
|
||||
moa_response_generation_template,
|
||||
)
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.constants import ERROR_MESSAGES, TASKS
|
||||
|
||||
from open_webui.routers.pipelines import process_pipeline_inlet_filter
|
||||
|
||||
from open_webui.utils.task import get_task_model_id
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
|
||||
from fastapi.responses import JSONResponse, RedirectResponse
|
||||
from open_webui.config import (
|
||||
DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_FOLLOW_UP_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_TAGS_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_EMOJI_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_FOLLOW_UP_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_MOA_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_TAGS_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE,
|
||||
DEFAULT_VOICE_MODE_PROMPT_TEMPLATE,
|
||||
)
|
||||
from open_webui.constants import ERROR_MESSAGES, TASKS
|
||||
from open_webui.routers.pipelines import process_pipeline_inlet_filter
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.chat import generate_chat_completion
|
||||
from open_webui.utils.task import (
|
||||
autocomplete_generation_template,
|
||||
emoji_generation_template,
|
||||
follow_up_generation_template,
|
||||
get_task_model_id,
|
||||
image_prompt_generation_template,
|
||||
moa_response_generation_template,
|
||||
query_generation_template,
|
||||
tags_generation_template,
|
||||
title_generation_template,
|
||||
)
|
||||
from pydantic import BaseModel
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -12,14 +12,13 @@ from urllib.parse import unquote
|
||||
import aiohttp
|
||||
from fastapi import APIRouter, Depends, Request, Response, WebSocket
|
||||
from fastapi.responses import JSONResponse, StreamingResponse
|
||||
from starlette.background import BackgroundTask
|
||||
|
||||
from open_webui.utils.auth import get_verified_user
|
||||
from open_webui.utils.access_control import has_connection_access
|
||||
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL
|
||||
from open_webui.config import TERMINAL_PROXY_HEADERS
|
||||
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.users import Users
|
||||
from open_webui.utils.access_control import has_connection_access
|
||||
from open_webui.utils.auth import get_verified_user
|
||||
from starlette.background import BackgroundTask
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -199,6 +198,7 @@ async def _resolve_authenticated_connection(ws: WebSocket, server_id: str):
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from open_webui.utils.auth import decode_token
|
||||
|
||||
# First-message authentication
|
||||
|
||||
@@ -1,44 +1,43 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
import time
|
||||
import re
|
||||
|
||||
import aiohttp
|
||||
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL, AIOHTTP_CLIENT_TIMEOUT
|
||||
from open_webui.models.groups import Groups
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL, CACHE_DIR
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import AIOHTTP_CLIENT_SESSION_SSL, AIOHTTP_CLIENT_TIMEOUT
|
||||
from open_webui.internal.db import get_async_session
|
||||
|
||||
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.oauth_sessions import OAuthSessions
|
||||
from open_webui.models.tools import (
|
||||
ToolAccessResponse,
|
||||
ToolForm,
|
||||
ToolModel,
|
||||
ToolResponse,
|
||||
ToolUserResponse,
|
||||
ToolAccessResponse,
|
||||
Tools,
|
||||
ToolUserResponse,
|
||||
)
|
||||
from open_webui.models.access_grants import AccessGrants
|
||||
from open_webui.utils.access_control import (
|
||||
filter_allowed_access_grants,
|
||||
has_access,
|
||||
has_permission,
|
||||
)
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.plugin import (
|
||||
get_tool_module_from_cache,
|
||||
load_tool_module_by_id,
|
||||
replace_imports,
|
||||
get_tool_module_from_cache,
|
||||
resolve_valves_schema_options,
|
||||
)
|
||||
from open_webui.utils.tools import get_tool_specs
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.access_control import (
|
||||
has_permission,
|
||||
has_access,
|
||||
filter_allowed_access_grants,
|
||||
)
|
||||
from open_webui.utils.tools import get_tool_servers
|
||||
|
||||
from open_webui.config import CACHE_DIR, BYPASS_ADMIN_ACCESS_CONTROL
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.utils.tools import get_tool_servers, get_tool_specs
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -248,7 +247,7 @@ def github_url_to_raw_url(url: str) -> str:
|
||||
return url
|
||||
|
||||
|
||||
@router.post('/load/url', response_model=Optional[dict])
|
||||
@router.post('/load/url', response_model=dict | None)
|
||||
async def load_tool_from_url(request: Request, form_data: LoadUrlForm, user=Depends(get_admin_user)):
|
||||
# NOTE: This is NOT a SSRF vulnerability:
|
||||
# This endpoint is admin-only (see get_admin_user), meant for *trusted* internal use,
|
||||
@@ -323,7 +322,7 @@ async def export_tools(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/create', response_model=Optional[ToolResponse])
|
||||
@router.post('/create', response_model=ToolResponse | None)
|
||||
async def create_new_tools(
|
||||
request: Request,
|
||||
form_data: ToolForm,
|
||||
@@ -401,7 +400,7 @@ async def create_new_tools(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/id/{id}', response_model=Optional[ToolAccessResponse])
|
||||
@router.get('/id/{id}', response_model=ToolAccessResponse | None)
|
||||
async def get_tools_by_id(id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
||||
tools = await Tools.get_tool_by_id(id, db=db)
|
||||
|
||||
@@ -448,7 +447,7 @@ async def get_tools_by_id(id: str, user=Depends(get_verified_user), db: AsyncSes
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/id/{id}/update', response_model=Optional[ToolModel])
|
||||
@router.post('/id/{id}/update', response_model=ToolModel | None)
|
||||
async def update_tools_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -541,7 +540,7 @@ class ToolAccessGrantsForm(BaseModel):
|
||||
access_grants: list[dict]
|
||||
|
||||
|
||||
@router.post('/id/{id}/access/update', response_model=Optional[ToolModel])
|
||||
@router.post('/id/{id}/access/update', response_model=ToolModel | None)
|
||||
async def update_tool_access_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -634,7 +633,7 @@ async def delete_tools_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/id/{id}/valves', response_model=Optional[dict])
|
||||
@router.get('/id/{id}/valves', response_model=dict | None)
|
||||
async def get_tools_valves_by_id(
|
||||
id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -676,7 +675,7 @@ async def get_tools_valves_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/id/{id}/valves/spec', response_model=Optional[dict])
|
||||
@router.get('/id/{id}/valves/spec', response_model=dict | None)
|
||||
async def get_tools_valves_spec_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -726,7 +725,7 @@ async def get_tools_valves_spec_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/id/{id}/valves/update', response_model=Optional[dict])
|
||||
@router.post('/id/{id}/valves/update', response_model=dict | None)
|
||||
async def update_tools_valves_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -789,7 +788,7 @@ async def update_tools_valves_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/id/{id}/valves/user', response_model=Optional[dict])
|
||||
@router.get('/id/{id}/valves/user', response_model=dict | None)
|
||||
async def get_tools_user_valves_by_id(
|
||||
id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -826,7 +825,7 @@ async def get_tools_user_valves_by_id(
|
||||
)
|
||||
|
||||
|
||||
@router.get('/id/{id}/valves/user/spec', response_model=Optional[dict])
|
||||
@router.get('/id/{id}/valves/user/spec', response_model=dict | None)
|
||||
async def get_tools_user_valves_spec_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
@@ -871,7 +870,7 @@ async def get_tools_user_valves_spec_by_id(
|
||||
return None
|
||||
|
||||
|
||||
@router.post('/id/{id}/valves/user/update', response_model=Optional[dict])
|
||||
@router.post('/id/{id}/valves/user/update', response_model=dict | None)
|
||||
async def update_tools_user_valves_by_id(
|
||||
request: Request,
|
||||
id: str,
|
||||
|
||||
@@ -1,46 +1,40 @@
|
||||
import logging
|
||||
from typing import Optional
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import io
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from fastapi.responses import Response, StreamingResponse, FileResponse
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
from open_webui.models.auths import Auths
|
||||
from open_webui.models.oauth_sessions import OAuthSessions
|
||||
|
||||
from open_webui.models.groups import Groups
|
||||
|
||||
from open_webui.models.users import (
|
||||
UserModel,
|
||||
UserGroupIdsModel,
|
||||
UserGroupIdsListResponse,
|
||||
UserInfoResponse,
|
||||
UserInfoListResponse,
|
||||
UserRoleUpdateForm,
|
||||
UserStatus,
|
||||
Users,
|
||||
UserSettings,
|
||||
UserUpdateForm,
|
||||
)
|
||||
|
||||
from fastapi.responses import FileResponse, Response, StreamingResponse
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.env import ENABLE_PROFILE_IMAGE_URL_FORWARDING, PROFILE_IMAGE_ALLOWED_MIME_TYPES, STATIC_DIR
|
||||
from open_webui.internal.db import get_async_session
|
||||
|
||||
|
||||
from open_webui.models.auths import Auths
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.oauth_sessions import OAuthSessions
|
||||
from open_webui.models.users import (
|
||||
UserGroupIdsListResponse,
|
||||
UserGroupIdsModel,
|
||||
UserInfoListResponse,
|
||||
UserInfoResponse,
|
||||
UserModel,
|
||||
UserRoleUpdateForm,
|
||||
Users,
|
||||
UserSettings,
|
||||
UserStatus,
|
||||
UserUpdateForm,
|
||||
)
|
||||
from open_webui.socket.main import disconnect_user_sessions
|
||||
from open_webui.utils.access_control import get_permissions, has_permission
|
||||
from open_webui.utils.auth import (
|
||||
get_admin_user,
|
||||
get_password_hash,
|
||||
get_verified_user,
|
||||
validate_password,
|
||||
)
|
||||
from open_webui.utils.access_control import get_permissions, has_permission
|
||||
from open_webui.socket.main import disconnect_user_sessions
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -59,10 +53,10 @@ PAGE_ITEM_COUNT = 30
|
||||
|
||||
@router.get('/', response_model=UserGroupIdsListResponse)
|
||||
async def get_users(
|
||||
query: Optional[str] = None,
|
||||
order_by: Optional[str] = None,
|
||||
direction: Optional[str] = None,
|
||||
page: Optional[int] = 1,
|
||||
query: str | None = None,
|
||||
order_by: str | None = None,
|
||||
direction: str | None = None,
|
||||
page: int | None = 1,
|
||||
user=Depends(get_admin_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -114,10 +108,10 @@ async def get_all_users(
|
||||
|
||||
@router.get('/search', response_model=UserInfoListResponse)
|
||||
async def search_users(
|
||||
query: Optional[str] = None,
|
||||
order_by: Optional[str] = None,
|
||||
direction: Optional[str] = None,
|
||||
page: Optional[int] = 1,
|
||||
query: str | None = None,
|
||||
order_by: str | None = None,
|
||||
direction: str | None = None,
|
||||
page: int | None = 1,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
@@ -275,7 +269,7 @@ async def update_default_user_permissions(request: Request, form_data: UserPermi
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/user/settings', response_model=Optional[UserSettings])
|
||||
@router.get('/user/settings', response_model=UserSettings | None)
|
||||
async def get_user_settings_by_session_user(
|
||||
user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -372,7 +366,7 @@ async def update_user_status_by_session_user(
|
||||
############################
|
||||
|
||||
|
||||
@router.get('/user/info', response_model=Optional[dict])
|
||||
@router.get('/user/info', response_model=dict | None)
|
||||
async def get_user_info_by_session_user(user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
||||
# user already fetched by get_verified_user — no need to refetch
|
||||
return user.info
|
||||
@@ -383,7 +377,7 @@ async def get_user_info_by_session_user(user=Depends(get_verified_user), db: Asy
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/user/info/update', response_model=Optional[dict])
|
||||
@router.post('/user/info/update', response_model=dict | None)
|
||||
async def update_user_info_by_session_user(
|
||||
form_data: dict, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)
|
||||
):
|
||||
@@ -408,8 +402,8 @@ async def update_user_info_by_session_user(
|
||||
|
||||
class UserActiveResponse(UserStatus):
|
||||
name: str
|
||||
profile_image_url: Optional[str] = None
|
||||
groups: Optional[list] = []
|
||||
profile_image_url: str | None = None
|
||||
groups: list | None = []
|
||||
|
||||
is_active: bool
|
||||
model_config = ConfigDict(extra='allow')
|
||||
@@ -536,7 +530,7 @@ async def get_user_active_status_by_id(
|
||||
############################
|
||||
|
||||
|
||||
@router.post('/{user_id}/update', response_model=Optional[UserModel])
|
||||
@router.post('/{user_id}/update', response_model=UserModel | None)
|
||||
async def update_user_by_id(
|
||||
user_id: str,
|
||||
form_data: UserUpdateForm,
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import black
|
||||
import logging
|
||||
import markdown
|
||||
from __future__ import annotations
|
||||
|
||||
from open_webui.models.chats import ChatTitleMessagesForm
|
||||
import logging
|
||||
|
||||
import black
|
||||
import markdown
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
|
||||
from open_webui.config import DATA_DIR, ENABLE_ADMIN_EXPORT
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
|
||||
from open_webui.utils.misc import get_gravatar_url
|
||||
from open_webui.utils.pdf_generator import PDFGenerator
|
||||
from open_webui.models.chats import ChatTitleMessagesForm
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.code_interpreter import execute_code_jupyter
|
||||
from open_webui.utils.misc import get_gravatar_url
|
||||
from open_webui.utils.pdf_generator import PDFGenerator
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
Reference in new issue
Block a user