mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-11 20:05:19 +00:00
Compare commits
8 commits
345c76f8e6
...
7c9d4aacbe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c9d4aacbe | ||
|
|
ae47101dc6 | ||
|
|
d85ab787cf | ||
|
|
551bc64a8d | ||
|
|
2a9f13a451 | ||
|
|
76e70128f6 | ||
|
|
15cb2e289d | ||
|
|
ba25c26b11 |
3 changed files with 88 additions and 2 deletions
|
|
@ -2967,6 +2967,29 @@ WEB_SEARCH_RESULT_COUNT = PersistentConfig(
|
|||
)
|
||||
|
||||
|
||||
# Web Search Rate Limiting Config
|
||||
|
||||
WEB_SEARCH_RATE_LIMIT_ENABLED = PersistentConfig(
|
||||
"WEB_SEARCH_RATE_LIMIT_ENABLED",
|
||||
"rag.web.search.rate_limit.enabled",
|
||||
os.getenv("WEB_SEARCH_RATE_LIMIT_ENABLED", "False").lower(),
|
||||
)
|
||||
|
||||
# The maximum number of requests that can be made to the web search engine per N seconds, where N=WEB_SEARCH_RATE_LIMIT_MIN_SECONDS
|
||||
WEB_SEARCH_RATE_LIMIT_MAX_REQUESTS = PersistentConfig(
|
||||
"WEB_SEARCH_RATE_LIMIT_MAX_REQUESTS",
|
||||
"rag.web.search.rate_limit.max_requests",
|
||||
int(os.getenv("WEB_SEARCH_RATE_LIMIT_MAX_REQUESTS", "1")),
|
||||
)
|
||||
|
||||
#
|
||||
WEB_SEARCH_RATE_LIMIT_MIN_SECONDS = PersistentConfig(
|
||||
"WEB_SEARCH_RATE_LIMIT_MIN_SECONDS",
|
||||
"rag.web.search.rate_limit.min_seconds",
|
||||
int(os.getenv("WEB_SEARCH_RATE_MIN_SECONDS", "1")),
|
||||
)
|
||||
|
||||
|
||||
# You can provide a list of your own websites to filter after performing a web search.
|
||||
# This ensures the highest level of safety and reliability of the information sources.
|
||||
WEB_SEARCH_DOMAIN_FILTER_LIST = PersistentConfig(
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ import mimetypes
|
|||
import os
|
||||
import shutil
|
||||
import asyncio
|
||||
import time
|
||||
|
||||
import re
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from multiprocessing import Value
|
||||
from pathlib import Path
|
||||
from typing import Iterator, List, Optional, Sequence, Union
|
||||
|
||||
|
|
@ -24,6 +26,9 @@ from fastapi import (
|
|||
)
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.concurrency import run_in_threadpool
|
||||
|
||||
from functools import wraps
|
||||
from collections import deque
|
||||
from pydantic import BaseModel
|
||||
import tiktoken
|
||||
|
||||
|
|
@ -1766,7 +1771,62 @@ async def process_web(
|
|||
detail=ERROR_MESSAGES.DEFAULT(e),
|
||||
)
|
||||
|
||||
from open_webui.config import (
|
||||
WEB_SEARCH_RATE_LIMIT_MAX_REQUESTS,
|
||||
WEB_SEARCH_RATE_LIMIT_MIN_SECONDS,
|
||||
WEB_SEARCH_RATE_LIMIT_ENABLED,
|
||||
)
|
||||
|
||||
# Rate Limit (Specifically for search: This references environment variables named for search)
|
||||
|
||||
|
||||
web_search_lock = asyncio.Lock()
|
||||
# Track timestamps of previous calls
|
||||
web_search_timestamps = deque()
|
||||
|
||||
|
||||
def search_rate_limit(max_calls: int, period: float):
|
||||
"""
|
||||
Async-friendly decorator for limiting function calls to `max_calls` per `period` seconds.
|
||||
Works in FastAPI async endpoints without blocking the event loop.
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
async def wrapper(*args, **kwargs):
|
||||
async with web_search_lock:
|
||||
now = asyncio.get_event_loop().time()
|
||||
|
||||
# Remove timestamps older than the period
|
||||
while web_search_timestamps and now - web_search_timestamps[0] > period:
|
||||
web_search_timestamps.popleft()
|
||||
|
||||
if len(web_search_timestamps) >= max_calls:
|
||||
# Need to wait until oldest call is outside the period
|
||||
wait_time = period - (now - web_search_timestamps[0])
|
||||
await asyncio.sleep(wait_time)
|
||||
now = asyncio.get_event_loop().time()
|
||||
while (
|
||||
web_search_timestamps
|
||||
and now - web_search_timestamps[0] > period
|
||||
):
|
||||
web_search_timestamps.popleft()
|
||||
|
||||
# Record this call
|
||||
web_search_timestamps.append(now)
|
||||
|
||||
# Call the actual function
|
||||
return await func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
@search_rate_limit(
|
||||
int(WEB_SEARCH_RATE_LIMIT_MAX_REQUESTS.value),
|
||||
int(WEB_SEARCH_RATE_LIMIT_MIN_SECONDS.value),
|
||||
)
|
||||
def search_web(
|
||||
request: Request, engine: str, query: str, user=None
|
||||
) -> list[SearchResult]:
|
||||
|
|
@ -1792,6 +1852,8 @@ def search_web(
|
|||
query (str): The query to search for
|
||||
"""
|
||||
|
||||
logging.info(f"search_web: {engine} query: {query}")
|
||||
|
||||
# TODO: add playwright to search the web
|
||||
if engine == "ollama_cloud":
|
||||
return search_ollama_cloud(
|
||||
|
|
@ -2087,6 +2149,7 @@ async def process_web_search(
|
|||
|
||||
for result in search_results:
|
||||
if result:
|
||||
result = await result
|
||||
for item in result:
|
||||
if item and item.link:
|
||||
result_items.append(item)
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody class="">
|
||||
{#each users as user, userIdx}
|
||||
{#each users as user, userIdx (user.id)}
|
||||
<tr class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs">
|
||||
<td class="px-3 py-1 min-w-[7rem] w-28">
|
||||
<button
|
||||
|
|
|
|||
Loading…
Reference in a new issue