Skip to content

Commit 947dcd3

Browse files
authored
Merge pull request open-webui#22385 from open-webui/dev
0.8.9
2 parents 79f0437 + 710b527 commit 947dcd3

147 files changed

Lines changed: 7586 additions & 1698 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/pull_request_template.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,11 @@ This is to ensure large feature PRs are discussed with the community first, befo
8888
🚨 DO NOT DELETE THE TEXT BELOW 🚨
8989
Keep the "Contributor License Agreement" confirmation text intact.
9090
Deleting it will trigger the CLA-Bot to INVALIDATE your PR.
91+
92+
Your PR will NOT be reviewed or merged until you check the box below confirming that you have read and agree to the terms of the CLA.
9193
-->
9294

93-
By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms.
95+
- [ ] By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms.
9496

9597
> [!NOTE]
9698
> Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.

.github/workflows/deploy-to-hf-spaces.yml

Lines changed: 0 additions & 64 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 79 additions & 0 deletions
Large diffs are not rendered by default.

backend/open_webui/config.py

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,12 @@ def __getattr__(self, key):
339339
os.environ.get("ENABLE_OAUTH_SIGNUP", "False").lower() == "true",
340340
)
341341

342+
OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE = PersistentConfig(
343+
"OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE",
344+
"oauth.refresh_token_include_scope",
345+
os.environ.get("OAUTH_REFRESH_TOKEN_INCLUDE_SCOPE", "False").lower() == "true",
346+
)
347+
342348

343349
OAUTH_MERGE_ACCOUNTS_BY_EMAIL = PersistentConfig(
344350
"OAUTH_MERGE_ACCOUNTS_BY_EMAIL",
@@ -1960,7 +1966,7 @@ class BannerModel(BaseModel):
19601966
- Only suggest follow-ups that make sense given the chat content and do not repeat what was already covered.
19611967
- If the conversation is very short or not specific, suggest more general (but relevant) follow-ups the user might ask.
19621968
- Use the conversation's primary language; default to English if multilingual.
1963-
- Response must be a JSON array of strings, no extra text or formatting.
1969+
- Response must be a JSON object with a "follow_ups" key containing an array of strings, no extra text or formatting.
19641970
### Output:
19651971
JSON format: { "follow_ups": ["Question 1?", "Question 2?", "Question 3?"] }
19661972
### Chat History:
@@ -2290,20 +2296,36 @@ class BannerModel(BaseModel):
22902296
]
22912297

22922298
DEFAULT_CODE_INTERPRETER_PROMPT = """
2293-
#### Tools Available
2294-
2295-
1. **Code Interpreter**: `<code_interpreter type="code" lang="python"></code_interpreter>`
2296-
- You have access to a Python shell that runs directly in the user's browser, enabling fast execution of code for analysis, calculations, or problem-solving. Use it in this response.
2297-
- The Python code you write can incorporate a wide array of libraries, handle data manipulation or visualization, perform API calls for web-related tasks, or tackle virtually any computational challenge. Use this flexibility to **think outside the box, craft elegant solutions, and harness Python's full potential**.
2298-
- To use it, **you must enclose your code within `<code_interpreter type="code" lang="python">` XML tags** and stop right away. If you don't, the code won't execute.
2299-
- When writing code in the code_interpreter XML tag, Do NOT use the triple backticks code block for markdown formatting, example: ```py # python code ``` will cause an error because it is markdown formatting, it is not python code.
2300-
- When coding, **always aim to print meaningful outputs** (e.g., results, tables, summaries, or visuals) to better interpret and verify the findings. Avoid relying on implicit outputs; prioritize explicit and clear print statements so the results are effectively communicated to the user.
2301-
- After obtaining the printed output, **always provide a concise analysis, interpretation, or next steps to help the user understand the findings or refine the outcome further.**
2302-
- If the results are unclear, unexpected, or require validation, refine the code and execute it again as needed. Always aim to deliver meaningful insights from the results, iterating if necessary.
2303-
- **If a link to an image, audio, or any file is provided in markdown format in the output, ALWAYS regurgitate word for word, explicitly display it as part of the response to ensure the user can access it easily, do NOT change the link.**
2304-
- All responses should be communicated in the chat's primary language, ensuring seamless understanding. If the chat is multilingual, default to English for clarity.
2305-
2306-
Ensure that the tools are effectively utilized to achieve the highest-quality analysis for the user."""
2299+
#### Code Interpreter
2300+
2301+
You have access to a Python code interpreter via: `<code_interpreter type="code" lang="python"></code_interpreter>`
2302+
2303+
- The Python shell runs directly in the user's browser for fast execution of analysis, calculations, or problem-solving. Use it in this response.
2304+
- You can use a wide array of libraries for data manipulation, visualization, API calls, or any computational task. Think outside the box and harness Python's full potential.
2305+
- **You must enclose your code within `<code_interpreter type="code" lang="python">` XML tags** and stop right away. If you don't, the code won't execute.
2306+
- Do NOT use triple backticks (```py ... ```) inside the XML tags — that is markdown formatting, not executable Python code.
2307+
- **Always print meaningful outputs** (results, tables, summaries, visuals). Avoid implicit outputs; use explicit print statements.
2308+
- After obtaining output, **provide a concise analysis, interpretation, or next steps** to help the user understand the findings.
2309+
- If results are unclear or unexpected, refine the code and re-execute. Iterate until you deliver meaningful insights.
2310+
- **If a link to an image, audio, or any file appears in the output, display it exactly as-is** in your response so the user can access it. Do not modify the link.
2311+
- Respond in the chat's primary language. Default to English if multilingual.
2312+
2313+
Ensure the code interpreter is effectively utilized to achieve the highest-quality analysis for the user."""
2314+
2315+
# Appended to the code interpreter prompt only when engine is pyodide (not jupyter)
2316+
CODE_INTERPRETER_PYODIDE_PROMPT = """
2317+
2318+
##### Pyodide Environment
2319+
2320+
- This Python environment runs via Pyodide in the browser. **Do not install packages** — `pip install`, `subprocess`, and `micropip.install()` are not available.
2321+
- If a required library is unavailable, use an alternative approach with available modules. Do not attempt to install anything.
2322+
2323+
##### Persistent File System
2324+
2325+
- User-uploaded files are available at `/mnt/uploads/`. When the user asks you to work with their files, read from this directory.
2326+
- You can also write output files to `/mnt/uploads/` so the user can access and download them from the file browser.
2327+
- The file system persists across code executions within the same session.
2328+
- Use `import os; os.listdir('/mnt/uploads')` to discover available files."""
23072329

23082330

23092331
####################################

backend/open_webui/env.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,16 @@ def parse_section(section):
788788
except ValueError:
789789
WEBSOCKET_SERVER_PING_INTERVAL = 25
790790

791+
WEBSOCKET_EVENT_CALLER_TIMEOUT = os.environ.get("WEBSOCKET_EVENT_CALLER_TIMEOUT", "")
792+
793+
if WEBSOCKET_EVENT_CALLER_TIMEOUT == "":
794+
WEBSOCKET_EVENT_CALLER_TIMEOUT = None
795+
else:
796+
try:
797+
WEBSOCKET_EVENT_CALLER_TIMEOUT = int(WEBSOCKET_EVENT_CALLER_TIMEOUT)
798+
except ValueError:
799+
WEBSOCKET_EVENT_CALLER_TIMEOUT = 300
800+
791801

792802
REQUESTS_VERIFY = os.environ.get("REQUESTS_VERIFY", "True").lower() == "true"
793803

backend/open_webui/internal/db.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,30 @@ def create_sqlcipher_connection():
102102
conn.execute(f"PRAGMA key = '{database_password}'")
103103
return conn
104104

105-
engine = create_engine(
106-
"sqlite://", # Dummy URL since we're using creator
107-
creator=create_sqlcipher_connection,
108-
echo=False,
109-
)
105+
# The dummy "sqlite://" URL would cause SQLAlchemy to auto-select
106+
# SingletonThreadPool, which non-deterministically closes in-use
107+
# connections when thread count exceeds pool_size, leading to segfaults
108+
# in the native sqlcipher3 C library. Use NullPool by default for safety,
109+
# or QueuePool if DATABASE_POOL_SIZE is explicitly configured.
110+
if isinstance(DATABASE_POOL_SIZE, int) and DATABASE_POOL_SIZE > 0:
111+
engine = create_engine(
112+
"sqlite://",
113+
creator=create_sqlcipher_connection,
114+
pool_size=DATABASE_POOL_SIZE,
115+
max_overflow=DATABASE_POOL_MAX_OVERFLOW,
116+
pool_timeout=DATABASE_POOL_TIMEOUT,
117+
pool_recycle=DATABASE_POOL_RECYCLE,
118+
pool_pre_ping=True,
119+
poolclass=QueuePool,
120+
echo=False,
121+
)
122+
else:
123+
engine = create_engine(
124+
"sqlite://",
125+
creator=create_sqlcipher_connection,
126+
poolclass=NullPool,
127+
echo=False,
128+
)
110129

111130
log.info("Connected to encrypted SQLite database using SQLCipher")
112131

backend/open_webui/main.py

Lines changed: 45 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,46 +1398,52 @@ async def dispatch(self, request: Request, call_next):
13981398
app.add_middleware(SecurityHeadersMiddleware)
13991399

14001400

1401-
class APIKeyRestrictionMiddleware(BaseHTTPMiddleware):
1402-
async def dispatch(self, request: Request, call_next):
1403-
auth_header = request.headers.get("Authorization")
1404-
token = None
1405-
1406-
if auth_header:
1407-
parts = auth_header.split(" ", 1)
1408-
if len(parts) == 2:
1409-
token = parts[1]
1410-
1411-
# Only apply restrictions if an sk- API key is used
1412-
if token and token.startswith("sk-"):
1413-
# Check if restrictions are enabled
1414-
if request.app.state.config.ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS:
1415-
allowed_paths = [
1416-
path.strip()
1417-
for path in str(
1418-
request.app.state.config.API_KEYS_ALLOWED_ENDPOINTS
1419-
).split(",")
1420-
if path.strip()
1421-
]
1422-
1423-
request_path = request.url.path
1424-
1425-
# Match exact path or prefix path
1426-
is_allowed = any(
1427-
request_path == allowed or request_path.startswith(allowed + "/")
1428-
for allowed in allowed_paths
1429-
)
1430-
1431-
if not is_allowed:
1432-
return JSONResponse(
1433-
status_code=status.HTTP_403_FORBIDDEN,
1434-
content={
1435-
"detail": "API key not allowed to access this endpoint."
1436-
},
1401+
class APIKeyRestrictionMiddleware:
1402+
def __init__(self, app):
1403+
self.app = app
1404+
1405+
async def __call__(self, scope, receive, send):
1406+
if scope["type"] == "http":
1407+
request = Request(scope)
1408+
auth_header = request.headers.get("Authorization")
1409+
token = None
1410+
1411+
if auth_header:
1412+
parts = auth_header.split(" ", 1)
1413+
if len(parts) == 2:
1414+
token = parts[1]
1415+
1416+
# Only apply restrictions if an sk- API key is used
1417+
if token and token.startswith("sk-"):
1418+
# Check if restrictions are enabled
1419+
if app.state.config.ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS:
1420+
allowed_paths = [
1421+
path.strip()
1422+
for path in str(
1423+
app.state.config.API_KEYS_ALLOWED_ENDPOINTS
1424+
).split(",")
1425+
if path.strip()
1426+
]
1427+
1428+
request_path = request.url.path
1429+
1430+
# Match exact path or prefix path
1431+
is_allowed = any(
1432+
request_path == allowed
1433+
or request_path.startswith(allowed + "/")
1434+
for allowed in allowed_paths
14371435
)
14381436

1439-
response = await call_next(request)
1440-
return response
1437+
if not is_allowed:
1438+
await JSONResponse(
1439+
status_code=status.HTTP_403_FORBIDDEN,
1440+
content={
1441+
"detail": "API key not allowed to access this endpoint."
1442+
},
1443+
)(scope, receive, send)
1444+
return
1445+
1446+
await self.app(scope, receive, send)
14411447

14421448

14431449
app.add_middleware(APIKeyRestrictionMiddleware)
@@ -2187,6 +2193,7 @@ async def get_app_config(request: Request):
21872193
"user_count": user_count,
21882194
"code": {
21892195
"engine": app.state.config.CODE_EXECUTION_ENGINE,
2196+
"interpreter_engine": app.state.config.CODE_INTERPRETER_ENGINE,
21902197
},
21912198
"audio": {
21922199
"tts": {

0 commit comments

Comments
 (0)