This commit is contained in:
Timothy Jaeryang Baek
2026-03-08 18:26:36 -05:00
parent 352391fa76
commit 1364df0913
2 changed files with 54 additions and 1 deletions

View File

@@ -4,6 +4,7 @@ from typing import Optional
from sqlalchemy.orm import Session
from open_webui.internal.db import Base, JSONField, get_db, get_db_context
from open_webui.utils.misc import sanitize_metadata
from pydantic import BaseModel, ConfigDict, model_validator
from sqlalchemy import BigInteger, Column, String, Text, JSON
@@ -127,9 +128,16 @@ class FilesTable:
self, user_id: str, form_data: FileForm, db: Optional[Session] = None
) -> Optional[FileModel]:
with get_db_context(db) as db:
file_data = form_data.model_dump()
# Sanitize meta to remove non-JSON-serializable objects
# (e.g. callable tool functions, MCP client instances from middleware)
if file_data.get("meta"):
file_data["meta"] = sanitize_metadata(file_data["meta"])
file = FileModel(
**{
**form_data.model_dump(),
**file_data,
"user_id": user_id,
"created_at": int(time.time()),
"updated_at": int(time.time()),

View File

@@ -566,6 +566,51 @@ def sanitize_data_for_db(obj):
return obj
def sanitize_metadata(metadata: dict) -> dict:
"""
Return a JSON-safe copy of a metadata dict for database storage.
The middleware metadata accumulates non-serializable Python objects
(e.g. callable tool functions, MCP client instances) that cause
PostgreSQL JSON inserts to fail. This helper strips those out while
preserving the primitive data needed for file-to-chat linking.
"""
if not isinstance(metadata, dict):
return metadata
def _sanitize(obj):
if isinstance(obj, (str, int, float, bool, type(None))):
return obj
if isinstance(obj, dict):
return {
k: _sanitize(v)
for k, v in obj.items()
if not callable(v) and _is_serializable(v)
}
if isinstance(obj, list):
return [_sanitize(v) for v in obj if not callable(v) and _is_serializable(v)]
if callable(obj):
return None
# Last resort: try to see if it's serializable
try:
json.dumps(obj)
return obj
except (TypeError, ValueError):
return None
def _is_serializable(obj):
"""Quick check whether a value can survive JSON serialization."""
if isinstance(obj, (str, int, float, bool, type(None), dict, list)):
return True
try:
json.dumps(obj)
return True
except (TypeError, ValueError):
return False
return _sanitize(metadata)
def extract_folders_after_data_docs(path):
# Convert the path to a Path object if it's not already
path = Path(path)