diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 648988e2c3..365544e80c 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -276,9 +276,9 @@ def _resolve_ollama_base_url(url: str) -> str: if not default.result() and fallback.result(): url = url.replace(':11434', ':12434') - log.info(f'Ollama port 11434 unreachable on {host}, falling back to 12434') + log.info('Ollama port 11434 unreachable on %s, falling back to 12434', host) elif not default.result(): - log.info(f'Ollama ports 11434 and 12434 both unreachable on {host}') + log.info('Ollama ports 11434 and 12434 both unreachable on %s', host) return url @@ -811,7 +811,7 @@ if VECTOR_DB == 'oracle23ai': 'Oracle23ai requires setting ORACLE_WALLET_DIR and ORACLE_WALLET_PASSWORD when using wallet authentication.' ) -log.info(f'VECTOR_DB: {VECTOR_DB}') +log.info('VECTOR_DB: %s', VECTOR_DB) # S3 Vector S3_VECTOR_BUCKET_NAME = os.getenv('S3_VECTOR_BUCKET_NAME', None) @@ -994,7 +994,7 @@ PDF_EXTRACT_IMAGES = os.getenv('PDF_EXTRACT_IMAGES', 'False').lower() == 'true' PDF_LOADER_MODE = os.getenv('PDF_LOADER_MODE', 'page') RAG_EMBEDDING_MODEL = os.getenv('RAG_EMBEDDING_MODEL', 'sentence-transformers/all-MiniLM-L6-v2') -log.info(f'Embedding model set: {RAG_EMBEDDING_MODEL}') +log.info('Embedding model set: %s', RAG_EMBEDDING_MODEL) RAG_TOKENIZER_MODEL = os.getenv('RAG_TOKENIZER_MODEL', '') @@ -1022,7 +1022,7 @@ RAG_RERANKING_ENGINE = os.getenv('RAG_RERANKING_ENGINE', '') RAG_RERANKING_MODEL = os.getenv('RAG_RERANKING_MODEL', '') if RAG_RERANKING_MODEL != '': - log.info(f'Reranking model set: {RAG_RERANKING_MODEL}') + log.info('Reranking model set: %s', RAG_RERANKING_MODEL) RAG_RERANKING_MODEL_AUTO_UPDATE = ( diff --git a/backend/open_webui/env.py b/backend/open_webui/env.py index f269aca78c..84f2f339a8 100644 --- a/backend/open_webui/env.py +++ b/backend/open_webui/env.py @@ -227,7 +227,7 @@ if FROM_INIT_PY: # Check if the data directory exists in the package directory if DATA_DIR.exists() and DATA_DIR != NEW_DATA_DIR: - log.info(f'Moving {DATA_DIR} to {NEW_DATA_DIR}') + log.info('Moving %s to %s', DATA_DIR, NEW_DATA_DIR) for item in DATA_DIR.iterdir(): dest = NEW_DATA_DIR / item.name if item.is_dir(): diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index 79193446e0..781a6f4a71 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -425,13 +425,13 @@ async def lifespan(app: FastAPI): log.info('Initializing tool servers...') try: await set_tool_servers(mock_request) - log.info(f'Initialized {len(app.state.TOOL_SERVERS)} tool server(s)') + log.info('Initialized %s tool server(s)', len(app.state.TOOL_SERVERS)) except Exception as e: log.warning(f'Failed to initialize tool servers at startup: {e}') try: await set_terminal_servers(mock_request) - log.info(f'Initialized {len(app.state.TERMINAL_SERVERS)} terminal server(s)') + log.info('Initialized %s terminal server(s)', len(app.state.TERMINAL_SERVERS)) except Exception as e: log.warning(f'Failed to initialize terminal servers at startup: {e}') @@ -2594,7 +2594,7 @@ async def register_client(request, client_id: str) -> bool: **apply_connection_oauth_options(connection, oauth_client_info.model_dump(mode='json')) ) oauth_client_manager.add_client(client_id, oauth_client_info) - log.info(f'Re-registered OAuth client {client_id} for tool server') + log.info('Re-registered OAuth client %s for tool server', client_id) return True diff --git a/backend/open_webui/models/chats.py b/backend/open_webui/models/chats.py index d29b623b44..25890a1b5c 100644 --- a/backend/open_webui/models/chats.py +++ b/backend/open_webui/models/chats.py @@ -1935,7 +1935,7 @@ class ChatTable: result = await session.execute(stmt) all_chats = result.scalars().all() - log.info(f'The number of chats: {len(all_chats)}') + log.info('The number of chats: %s', len(all_chats)) # Validate and return chats return [ChatModel.model_validate(chat) for chat in all_chats] @@ -2100,7 +2100,7 @@ class ChatTable: bind = await session.connection() dialect_name = bind.dialect.name - log.info(f'DB dialect name: {dialect_name}') + log.info('DB dialect name: %s', dialect_name) if dialect_name == 'sqlite': stmt = stmt.filter( text(f"EXISTS (SELECT 1 FROM json_each(Chat.meta, '$.tags') WHERE json_each.value = :tag_id)") @@ -2227,7 +2227,7 @@ class ChatTable: result = await session.execute(stmt.where(Chat.meta['internal'].as_boolean().is_not(True))) count = result.scalar() - log.info(f"Count of chats for folder '{folder_id}': {count}") + log.info("Count of chats for folder '%s': %s", folder_id, count) return count async def count_chats_by_folder_ids_and_user_id( @@ -2241,7 +2241,7 @@ class ChatTable: result = await session.execute(stmt.where(Chat.meta['internal'].as_boolean().is_not(True))) count = result.scalar() - log.info(f"Count of chats for folders '{folder_ids}': {count}") + log.info("Count of chats for folders '%s': %s", folder_ids, count) return count async def delete_tag_by_id_and_user_id_and_tag_name( diff --git a/backend/open_webui/retrieval/loaders/datalab_marker.py b/backend/open_webui/retrieval/loaders/datalab_marker.py index 02a50f10a4..8d852ce302 100644 --- a/backend/open_webui/retrieval/loaders/datalab_marker.py +++ b/backend/open_webui/retrieval/loaders/datalab_marker.py @@ -72,7 +72,7 @@ class DatalabMarkerLoader: response = requests.get(url, headers=headers) response.raise_for_status() result = response.json() - log.info(f'Marker API status check for request {request_id}: {result}') + log.info('Marker API status check for request %s: %s', request_id, result) return result except requests.HTTPError as e: log.error(f'Error checking Marker request status: {e}') @@ -104,7 +104,10 @@ class DatalabMarkerLoader: form_data['additional_config'] = self.additional_config log.info( - f"Datalab Marker POST request parameters: {{'filename': '{filename}', 'mime_type': '{mime_type}', **{form_data}}}" + "Datalab Marker POST request parameters: {'filename': '%s', 'mime_type': '%s', **%s}", + filename, + mime_type, + form_data, ) try: @@ -168,7 +171,7 @@ class DatalabMarkerLoader: 'total_cost', ) } - log.info(f'Marker processing completed successfully: {json.dumps(summary, indent=2)}') + log.info('Marker processing completed successfully: %s', json.dumps(summary, indent=2)) break if status_val == 'failed' or success_val is False: @@ -235,7 +238,7 @@ class DatalabMarkerLoader: try: with open(output_path, 'w', encoding='utf-8') as f: f.write(full_text) - log.info(f'Saved Marker output to: {output_path}') + log.info('Saved Marker output to: %s', output_path) except Exception as e: log.warning(f'Failed to write marker output to disk: {e}') diff --git a/backend/open_webui/retrieval/loaders/mineru.py b/backend/open_webui/retrieval/loaders/mineru.py index 907bc5dfc3..4459515dc2 100644 --- a/backend/open_webui/retrieval/loaders/mineru.py +++ b/backend/open_webui/retrieval/loaders/mineru.py @@ -74,7 +74,7 @@ class MinerULoader: Load document using Local API (synchronous). Posts file to /file_parse endpoint and gets immediate response. """ - log.info(f'Using MinerU Local API at {self.api_url}') + log.info('Using MinerU Local API at %s', self.api_url) filename = os.path.basename(self.file_path) @@ -97,7 +97,7 @@ class MinerULoader: with open(self.file_path, 'rb') as f: files = {'files': (filename, f, 'application/octet-stream')} - log.info(f'Sending file to MinerU Local API: {filename}') + log.info('Sending file to MinerU Local API: %s', filename) log.debug('Local API parameters: %s', form_data) response = requests.post( @@ -163,7 +163,7 @@ class MinerULoader: detail='MinerU returned empty markdown content', ) - log.info(f'Successfully parsed document with MinerU Local API: {filename}') + log.info('Successfully parsed document with MinerU Local API: %s', filename) # Create metadata metadata = { @@ -180,7 +180,7 @@ class MinerULoader: Load document using Cloud API (asynchronous). Uses batch upload endpoint to avoid need for public file URLs. """ - log.info(f'Using MinerU Cloud API at {self.api_url}') + log.info('Using MinerU Cloud API at %s', self.api_url) filename = os.path.basename(self.file_path) @@ -196,7 +196,7 @@ class MinerULoader: # Step 4: Download and extract markdown from ZIP markdown_content = self._download_and_extract_zip(result['full_zip_url'], filename) - log.info(f'Successfully parsed document with MinerU Cloud API: {filename}') + log.info('Successfully parsed document with MinerU Cloud API: %s', filename) # Create metadata metadata = { @@ -232,7 +232,7 @@ class MinerULoader: if self.page_ranges: request_body['files'][0]['page_ranges'] = self.page_ranges - log.info(f'Requesting upload URL for: {filename}') + log.info('Requesting upload URL for: %s', filename) log.debug('Cloud API request body: %s', request_body) try: @@ -284,7 +284,7 @@ class MinerULoader: ) upload_url = file_urls[0] - log.info(f'Received upload URL for batch: {batch_id}') + log.info('Received upload URL for batch: %s', batch_id) return batch_id, upload_url @@ -334,7 +334,7 @@ class MinerULoader: max_iterations = 300 # 10 minutes max (2 seconds per iteration) poll_interval = 2 # seconds - log.info(f'Polling batch status: {batch_id}') + log.info('Polling batch status: %s', batch_id) for iteration in range(max_iterations): try: @@ -393,7 +393,7 @@ class MinerULoader: state = file_result.get('state') if state == 'done': - log.info(f'Processing complete for {filename}') + log.info('Processing complete for %s', filename) return file_result elif state == 'failed': error_msg = file_result.get('err_msg', 'Unknown error') @@ -404,7 +404,7 @@ class MinerULoader: elif state in ['waiting-file', 'pending', 'running', 'converting']: # Still processing if iteration % 10 == 0: # Log every 20 seconds - log.info(f'Processing status: {state} (iteration {iteration + 1}/{max_iterations})') + log.info('Processing status: %s (iteration %s/%s)', state, iteration + 1, max_iterations) time.sleep(poll_interval) else: log.warning(f'Unknown state: {state}') @@ -421,7 +421,7 @@ class MinerULoader: Download ZIP file from CDN and extract markdown content. Returns the markdown content as a string. """ - log.info(f'Downloading results from: {zip_url}') + log.info('Downloading results from: %s', zip_url) try: response = requests.get(zip_url, timeout=60) @@ -452,7 +452,7 @@ class MinerULoader: read_errors = [] for member in md_members: - log.info(f'Found markdown file in ZIP: {member.filename}') + log.info('Found markdown file in ZIP: %s', member.filename) try: with zip_ref.open(member, 'r') as f: if self.max_markdown_bytes is None: @@ -515,5 +515,5 @@ class MinerULoader: detail='Extracted markdown content is empty', ) - log.info(f'Successfully extracted markdown content ({len(markdown_content)} characters)') + log.info('Successfully extracted markdown content (%s characters)', len(markdown_content)) return markdown_content diff --git a/backend/open_webui/retrieval/loaders/mistral.py b/backend/open_webui/retrieval/loaders/mistral.py index 186704557e..05b9d0fda7 100644 --- a/backend/open_webui/retrieval/loaders/mistral.py +++ b/backend/open_webui/retrieval/loaders/mistral.py @@ -261,7 +261,7 @@ class MistralLoader: file_id = response_data.get('id') if not file_id: raise ValueError('File ID not found in upload response.') - log.info(f'File uploaded successfully. File ID: {file_id}') + log.info('File uploaded successfully. File ID: %s', file_id) return file_id except Exception as e: log.error(f'Failed to upload file: {e}') @@ -305,12 +305,12 @@ class MistralLoader: if not file_id: raise ValueError('File ID not found in upload response.') - log.info(f'File uploaded successfully. File ID: {file_id}') + log.info('File uploaded successfully. File ID: %s', file_id) return file_id def _get_signed_url(self, file_id: str) -> str: """Retrieves a temporary signed URL for the uploaded file (sync version).""" - log.info(f'Getting signed URL for file ID: {file_id}') + log.info('Getting signed URL for file ID: %s', file_id) url = f'{self.base_url}/files/{file_id}/url' params = {'expiry': 1} signed_url_headers = {**self.headers, 'Accept': 'application/json'} @@ -421,7 +421,7 @@ class MistralLoader: ocr_response = await self._handle_response_async(response) processing_time = time.time() - start_time - log.info(f'OCR processing completed in {processing_time:.2f}s') + log.info('OCR processing completed in %.2fs', processing_time) return ocr_response @@ -434,13 +434,13 @@ class MistralLoader: def _delete_file(self, file_id: str) -> None: """Deletes the file from Mistral storage (sync version).""" - log.info(f'Deleting uploaded file ID: {file_id}') + log.info('Deleting uploaded file ID: %s', file_id) url = f'{self.base_url}/files/{file_id}' try: response = requests.delete(url, headers=self.headers, timeout=self.cleanup_timeout) delete_response = self._handle_response(response) - log.info(f'File deleted successfully: {delete_response}') + log.info('File deleted successfully: %s', delete_response) except Exception as e: # Log error but don't necessarily halt execution if deletion fails log.error(f'Failed to delete file ID {file_id}: {e}') @@ -551,7 +551,7 @@ class MistralLoader: ) if skipped_pages > 0: - log.info(f'Processed {len(documents)} pages, skipped {skipped_pages} empty/invalid pages') + log.info('Processed %s pages, skipped %s empty/invalid pages', len(documents), skipped_pages) if not documents: # Case where pages existed but none had valid markdown/index @@ -584,7 +584,7 @@ class MistralLoader: if self.use_base64: documents = self._process_results(self._process_ocr(self._get_file_data_url())) total_time = time.time() - start_time - log.info(f'Sync OCR workflow completed in {total_time:.2f}s, produced {len(documents)} documents') + log.info('Sync OCR workflow completed in %.2fs, produced %s documents', total_time, len(documents)) return documents # 1. Upload file @@ -600,7 +600,7 @@ class MistralLoader: documents = self._process_results(ocr_response) total_time = time.time() - start_time - log.info(f'Sync OCR workflow completed in {total_time:.2f}s, produced {len(documents)} documents') + log.info('Sync OCR workflow completed in %.2fs, produced %s documents', total_time, len(documents)) return documents @@ -642,7 +642,7 @@ class MistralLoader: ocr_response = await self._process_ocr_async(session, self._get_file_data_url()) documents = self._process_results(ocr_response) total_time = time.time() - start_time - log.info(f'Async OCR workflow completed in {total_time:.2f}s, produced {len(documents)} documents') + log.info('Async OCR workflow completed in %.2fs, produced %s documents', total_time, len(documents)) return documents # 1. Upload file with streaming @@ -658,7 +658,7 @@ class MistralLoader: documents = self._process_results(ocr_response) total_time = time.time() - start_time - log.info(f'Async OCR workflow completed in {total_time:.2f}s, produced {len(documents)} documents') + log.info('Async OCR workflow completed in %.2fs, produced %s documents', total_time, len(documents)) return documents @@ -701,7 +701,7 @@ class MistralLoader: if not loaders: return [] - log.info(f'Starting concurrent processing of {len(loaders)} files with max {max_concurrent} concurrent') + log.info('Starting concurrent processing of %s files with max %s concurrent', len(loaders), max_concurrent) start_time = time.time() # Use semaphore to control concurrency @@ -741,9 +741,11 @@ class MistralLoader: failure_count = len(results) - success_count log.info( - f'Batch processing completed in {total_time:.2f}s: ' - f'{success_count} files succeeded, {failure_count} files failed, ' - f'produced {total_docs} total documents' + 'Batch processing completed in %.2fs: %s files succeeded, %s files failed, produced %s total documents', + total_time, + success_count, + failure_count, + total_docs, ) return processed_results diff --git a/backend/open_webui/retrieval/loaders/paddleocr_vl.py b/backend/open_webui/retrieval/loaders/paddleocr_vl.py index 50172ddb47..5f1107dd9e 100644 --- a/backend/open_webui/retrieval/loaders/paddleocr_vl.py +++ b/backend/open_webui/retrieval/loaders/paddleocr_vl.py @@ -35,7 +35,7 @@ class PaddleOCRVLLoader: self.file_name = os.path.basename(file_path) def load(self) -> List[Document]: - log.info(f'Processing with PaddleOCR-vl: {self.file_path}') + log.info('Processing with PaddleOCR-vl: %s', self.file_path) try: with open(self.file_path, 'rb') as file: @@ -96,7 +96,7 @@ class PaddleOCRVLLoader: ) if skipped_pages > 0: - log.info(f'PaddleOCR-vl: Processed {len(documents)} pages, skipped {skipped_pages} empty pages.') + log.info('PaddleOCR-vl: Processed %s pages, skipped %s empty pages.', len(documents), skipped_pages) if not documents: log.warning('No valid text content found by PaddleOCR-vl.') diff --git a/backend/open_webui/retrieval/loaders/youtube.py b/backend/open_webui/retrieval/loaders/youtube.py index bd70a3db7c..61659d7bc1 100644 --- a/backend/open_webui/retrieval/loaders/youtube.py +++ b/backend/open_webui/retrieval/loaders/youtube.py @@ -138,7 +138,7 @@ class YoutubeLoader: log.debug("No transcript found for language '%s'", lang) continue except Exception as e: - log.info(f"Error finding transcript for language '{lang}'") + log.info("Error finding transcript for language '%s'", lang) raise e # If we get here, all languages failed diff --git a/backend/open_webui/retrieval/models/external.py b/backend/open_webui/retrieval/models/external.py index 6d2849eb88..a66a2c50bf 100644 --- a/backend/open_webui/retrieval/models/external.py +++ b/backend/open_webui/retrieval/models/external.py @@ -35,8 +35,8 @@ class ExternalReranker(BaseReranker): } try: - log.info(f'ExternalReranker:predict:model {self.model}') - log.info(f'ExternalReranker:predict:query {query}') + log.info('ExternalReranker:predict:model %s', self.model) + log.info('ExternalReranker:predict:query %s', query) headers = { 'Content-Type': 'application/json', diff --git a/backend/open_webui/retrieval/utils.py b/backend/open_webui/retrieval/utils.py index cee76b3e4f..98e15d38e8 100644 --- a/backend/open_webui/retrieval/utils.py +++ b/backend/open_webui/retrieval/utils.py @@ -318,7 +318,7 @@ def query_doc(collection_name: str, query_embedding: list[float], k: int, user: ) if result: - log.info(f'query_doc:result {result.ids} {result.metadatas}') + log.info('query_doc:result %s %s', result.ids, result.metadatas) return result except Exception as e: @@ -332,7 +332,7 @@ def get_doc(collection_name: str, user: UserModel = None): result = VECTOR_DB_CLIENT.get(collection_name=collection_name) if result: - log.info(f'query_doc:result {result.ids} {result.metadatas}') + log.info('query_doc:result %s %s', result.ids, result.metadatas) return result except Exception as e: @@ -585,7 +585,7 @@ async def query_doc_with_hybrid_search( 'metadatas': [metadatas], } - log.info('query_doc_with_hybrid_search:result ' + f'{result["metadatas"]} {result["distances"]}') + log.info('query_doc_with_hybrid_search:result %s %s', result['metadatas'], result['distances']) return result except Exception as e: log.exception(f'Error querying doc {collection_name} with hybrid search: {e}') @@ -812,7 +812,7 @@ async def query_collection_with_hybrid_search( collection_results = dict(await asyncio.gather(*(_fetch_collection(name) for name in collection_names))) - log.info(f'Starting hybrid search for {len(queries)} queries in {len(collection_names)} collections...') + log.info('Starting hybrid search for %s queries in %s collections...', len(queries), len(collection_names)) async def process_query(collection_name, query): try: diff --git a/backend/open_webui/retrieval/vector/dbs/milvus.py b/backend/open_webui/retrieval/vector/dbs/milvus.py index 07f45bccce..0aa392f683 100644 --- a/backend/open_webui/retrieval/vector/dbs/milvus.py +++ b/backend/open_webui/retrieval/vector/dbs/milvus.py @@ -125,7 +125,7 @@ class MilvusClient(VectorDBBase): index_type = MILVUS_INDEX_TYPE.upper() metric_type = MILVUS_METRIC_TYPE.upper() - log.info(f'Using Milvus index type: {index_type}, metric type: {metric_type}') + log.info('Using Milvus index type: %s, metric type: %s', index_type, metric_type) index_creation_params = {} if index_type == 'HNSW': @@ -133,18 +133,18 @@ class MilvusClient(VectorDBBase): 'M': MILVUS_HNSW_M, 'efConstruction': MILVUS_HNSW_EFCONSTRUCTION, } - log.info(f'HNSW params: {index_creation_params}') + log.info('HNSW params: %s', index_creation_params) elif index_type == 'IVF_FLAT': index_creation_params = {'nlist': MILVUS_IVF_FLAT_NLIST} - log.info(f'IVF_FLAT params: {index_creation_params}') + log.info('IVF_FLAT params: %s', index_creation_params) elif index_type == 'DISKANN': index_creation_params = { 'max_degree': MILVUS_DISKANN_MAX_DEGREE, 'search_list_size': MILVUS_DISKANN_SEARCH_LIST_SIZE, } - log.info(f'DISKANN params: {index_creation_params}') + log.info('DISKANN params: %s', index_creation_params) elif index_type in ['FLAT', 'AUTOINDEX']: - log.info(f'Using {index_type} index with no specific build-time params.') + log.info('Using %s index with no specific build-time params.', index_type) else: log.warning( f"Unsupported MILVUS_INDEX_TYPE: '{index_type}'. " @@ -167,7 +167,11 @@ class MilvusClient(VectorDBBase): index_params=index_params, ) log.info( - f"Successfully created collection '{self.collection_prefix}_{collection_name}' with index type '{index_type}' and metric '{metric_type}'." + "Successfully created collection '%s_%s' with index type '%s' and metric '%s'.", + self.collection_prefix, + collection_name, + index_type, + metric_type, ) def has_collection(self, collection_name: str) -> bool: @@ -220,7 +224,11 @@ class MilvusClient(VectorDBBase): try: log.info( - f"Querying collection {self.collection_prefix}_{collection_name} with filter: '{filter_string}', limit: {limit}" + "Querying collection %s_%s with filter: '%s', limit: %s", + self.collection_prefix, + collection_name, + filter_string, + limit, ) iterator = self.client.query_iterator( @@ -265,7 +273,7 @@ class MilvusClient(VectorDBBase): # Insert the items into the collection, if the collection does not exist, it will be created. collection_name = collection_name.replace('-', '_') if not self.client.has_collection(collection_name=f'{self.collection_prefix}_{collection_name}'): - log.info(f'Collection {self.collection_prefix}_{collection_name} does not exist. Creating now.') + log.info('Collection %s_%s does not exist. Creating now.', self.collection_prefix, collection_name) if not items: log.error( f'Cannot create collection {self.collection_prefix}_{collection_name} without items to determine dimension.' @@ -273,7 +281,7 @@ class MilvusClient(VectorDBBase): raise ValueError('Cannot create Milvus collection without items to determine vector dimension.') self._create_collection(collection_name=collection_name, dimension=len(items[0]['vector'])) - log.info(f'Inserting {len(items)} items into collection {self.collection_prefix}_{collection_name}.') + log.info('Inserting %s items into collection %s_%s.', len(items), self.collection_prefix, collection_name) data = [] for item in items: text = item['text'] or '' @@ -301,7 +309,9 @@ class MilvusClient(VectorDBBase): # Update the items in the collection, if the items are not present, insert them. If the collection does not exist, it will be created. collection_name = collection_name.replace('-', '_') if not self.client.has_collection(collection_name=f'{self.collection_prefix}_{collection_name}'): - log.info(f'Collection {self.collection_prefix}_{collection_name} does not exist for upsert. Creating now.') + log.info( + 'Collection %s_%s does not exist for upsert. Creating now.', self.collection_prefix, collection_name + ) if not items: log.error( f'Cannot create collection {self.collection_prefix}_{collection_name} for upsert without items to determine dimension.' @@ -311,7 +321,7 @@ class MilvusClient(VectorDBBase): ) self._create_collection(collection_name=collection_name, dimension=len(items[0]['vector'])) - log.info(f'Upserting {len(items)} items into collection {self.collection_prefix}_{collection_name}.') + log.info('Upserting %s items into collection %s_%s.', len(items), self.collection_prefix, collection_name) data = [] for item in items: text = item['text'] or '' @@ -348,7 +358,7 @@ class MilvusClient(VectorDBBase): return None if ids: - log.info(f'Deleting items by IDs from {self.collection_prefix}_{collection_name}. IDs: {ids}') + log.info('Deleting items by IDs from %s_%s. IDs: %s', self.collection_prefix, collection_name, ids) return self.client.delete( collection_name=f'{self.collection_prefix}_{collection_name}', ids=ids, @@ -356,7 +366,10 @@ class MilvusClient(VectorDBBase): elif filter: filter_string = ' && '.join([f'metadata["{key}"] == {JSONCodec.dumps(value)}' for key, value in filter.items()]) log.info( - f'Deleting items by filter from {self.collection_prefix}_{collection_name}. Filter: {filter_string}' + 'Deleting items by filter from %s_%s. Filter: %s', + self.collection_prefix, + collection_name, + filter_string, ) return self.client.delete( collection_name=f'{self.collection_prefix}_{collection_name}', @@ -378,7 +391,7 @@ class MilvusClient(VectorDBBase): try: self.client.drop_collection(collection_name=collection_name_full) deleted_collections.append(collection_name_full) - log.info(f'Deleted collection: {collection_name_full}') + log.info('Deleted collection: %s', collection_name_full) except Exception as e: log.error(f'Error deleting collection {collection_name_full}: {e}') - log.info(f'Milvus reset complete. Deleted collections: {deleted_collections}') + log.info('Milvus reset complete. Deleted collections: %s', deleted_collections) diff --git a/backend/open_webui/retrieval/vector/dbs/milvus_multitenancy.py b/backend/open_webui/retrieval/vector/dbs/milvus_multitenancy.py index 599cb8712c..4e3aad49ce 100644 --- a/backend/open_webui/retrieval/vector/dbs/milvus_multitenancy.py +++ b/backend/open_webui/retrieval/vector/dbs/milvus_multitenancy.py @@ -146,7 +146,7 @@ class MilvusClient(VectorDBBase): # The index only accelerates resource_id filters; never fail # collection creation over it. log.warning(f'Could not create {RESOURCE_ID_FIELD} index on {mt_collection_name}: {e}') - log.info(f'Created shared collection: {mt_collection_name}') + log.info('Created shared collection: %s', mt_collection_name) def _ensure_collection(self, mt_collection_name: str, dimension: int): if not self.client.has_collection(mt_collection_name): diff --git a/backend/open_webui/retrieval/vector/dbs/opengauss.py b/backend/open_webui/retrieval/vector/dbs/opengauss.py index 142eb8546c..900713a7a1 100644 --- a/backend/open_webui/retrieval/vector/dbs/opengauss.py +++ b/backend/open_webui/retrieval/vector/dbs/opengauss.py @@ -181,7 +181,7 @@ class OpenGaussClient(VectorDBBase): new_items.append(new_chunk) self.session.bulk_save_objects(new_items) self.session.commit() - log.info(f"Inserting {len(new_items)} items into collection '{collection_name}'.") + log.info("Inserting %s items into collection '%s'.", len(new_items), collection_name) except Exception as e: self.session.rollback() log.exception(f'Failed to insert data: {e}') @@ -207,7 +207,7 @@ class OpenGaussClient(VectorDBBase): ) self.session.add(new_chunk) self.session.commit() - log.info(f"Inserting/updating {len(items)} items in collection '{collection_name}'.") + log.info("Inserting/updating %s items in collection '%s'.", len(items), collection_name) except Exception as e: self.session.rollback() log.exception(f'Failed to insert or update data.: {e}') @@ -352,7 +352,7 @@ class OpenGaussClient(VectorDBBase): query = query.filter(DocumentChunk.vmetadata[key].astext == str(value)) deleted = query.delete(synchronize_session=False) self.session.commit() - log.info(f"Deleted {deleted} items from collection '{collection_name}'") + log.info("Deleted %s items from collection '%s'", deleted, collection_name) except Exception as e: self.session.rollback() log.exception(f'Failed to delete data: {e}') @@ -362,7 +362,7 @@ class OpenGaussClient(VectorDBBase): try: deleted = self.session.query(DocumentChunk).delete() self.session.commit() - log.info(f'Reset completed. Deleted {deleted} items') + log.info('Reset completed. Deleted %s items', deleted) except Exception as e: self.session.rollback() log.exception(f'Reset failed: {e}') @@ -386,4 +386,4 @@ class OpenGaussClient(VectorDBBase): def delete_collection(self, collection_name: str) -> None: self.delete(collection_name) - log.info(f"Collection '{collection_name}' has been deleted") + log.info("Collection '%s' has been deleted", collection_name) diff --git a/backend/open_webui/retrieval/vector/dbs/oracle23ai.py b/backend/open_webui/retrieval/vector/dbs/oracle23ai.py index bc3a5fbc8e..e72b7e326a 100644 --- a/backend/open_webui/retrieval/vector/dbs/oracle23ai.py +++ b/backend/open_webui/retrieval/vector/dbs/oracle23ai.py @@ -94,10 +94,10 @@ class Oracle23aiClient(VectorDBBase): self._create_dbcs_pool() dsn = ORACLE_DB_DSN - log.info(f'Creating Connection Pool [{ORACLE_DB_USER}:**@{dsn}]') + log.info('Creating Connection Pool [%s:**@%s]', ORACLE_DB_USER, dsn) with self.get_connection() as connection: - log.info(f'Connection version: {connection.version}') + log.info('Connection version: %s', connection.version) self._initialize_database(connection) log.info('Oracle Vector Search initialization complete.') @@ -159,7 +159,7 @@ class Oracle23aiClient(VectorDBBase): if attempt < max_retries - 1: wait_time = 2**attempt - log.info(f'Retrying in {wait_time} seconds...') + log.info('Retrying in %s seconds...', wait_time) time.sleep(wait_time) else: raise @@ -184,7 +184,7 @@ class Oracle23aiClient(VectorDBBase): thread = threading.Thread(target=_monitor, daemon=True) thread.start() - log.info(f'Started DB health monitor every {interval_seconds} seconds.') + log.info('Started DB health monitor every %s seconds.', interval_seconds) def _reconnect_pool(self): """ @@ -412,7 +412,7 @@ class Oracle23aiClient(VectorDBBase): ... ] >>> client.insert("my_collection", items) """ - log.info(f"Inserting {len(items)} items into collection '{collection_name}'.") + log.info("Inserting %s items into collection '%s'.", len(items), collection_name) with self.get_connection() as connection: try: @@ -437,7 +437,7 @@ class Oracle23aiClient(VectorDBBase): ) connection.commit() - log.info(f"Successfully inserted {len(items)} items into collection '{collection_name}'.") + log.info("Successfully inserted %s items into collection '%s'.", len(items), collection_name) except Exception as e: connection.rollback() @@ -466,7 +466,7 @@ class Oracle23aiClient(VectorDBBase): ... ] >>> client.upsert("my_collection", items) """ - log.info(f"Upserting {len(items)} items into collection '{collection_name}'.") + log.info("Upserting %s items into collection '%s'.", len(items), collection_name) with self.get_connection() as connection: try: @@ -505,7 +505,7 @@ class Oracle23aiClient(VectorDBBase): ) connection.commit() - log.info(f"Successfully upserted {len(items)} items into collection '{collection_name}'.") + log.info("Successfully upserted %s items into collection '%s'.", len(items), collection_name) except Exception as e: connection.rollback() @@ -541,7 +541,7 @@ class Oracle23aiClient(VectorDBBase): ... for i, (id, dist) in enumerate(zip(results.ids[0], results.distances[0])): ... log.info(f"Match {i+1}: id={id}, distance={dist}") """ - log.info(f"Searching items from collection '{collection_name}' with limit {limit}.") + log.info("Searching items from collection '%s' with limit %s.", collection_name, limit) try: if not vectors: @@ -587,7 +587,7 @@ class Oracle23aiClient(VectorDBBase): metadatas[qid].append(self._json_to_metadata(metadata_str)) distances[qid].append(float(row[3])) - log.info(f'Search completed. Found {sum(len(ids[i]) for i in range(num_queries))} total results.') + log.info('Search completed. Found %s total results.', sum(len(ids[i]) for i in range(num_queries))) return SearchResult(ids=ids, distances=distances, documents=documents, metadatas=metadatas) @@ -616,7 +616,7 @@ class Oracle23aiClient(VectorDBBase): >>> if results: ... print(f"Found {len(results.ids[0])} matching documents") """ - log.info(f"Querying items from collection '{collection_name}' with filters.") + log.info("Querying items from collection '%s' with filters.", collection_name) try: limit = limit or 100 @@ -656,7 +656,7 @@ class Oracle23aiClient(VectorDBBase): ] ] - log.info(f'Query completed. Found {len(results)} results.') + log.info('Query completed. Found %s results.', len(results)) return GetResult(ids=ids, documents=documents, metadatas=metadatas) @@ -747,7 +747,7 @@ class Oracle23aiClient(VectorDBBase): >>> # Or delete by metadata filter >>> client.delete("my_collection", filter={"source": "deprecated_source"}) """ - log.info(f"Deleting items from collection '{collection_name}'.") + log.info("Deleting items from collection '%s'.", collection_name) try: query = 'DELETE FROM document_chunk WHERE collection_name = :collection_name' @@ -772,7 +772,7 @@ class Oracle23aiClient(VectorDBBase): deleted = cursor.rowcount connection.commit() - log.info(f"Deleted {deleted} items from collection '{collection_name}'.") + log.info("Deleted %s items from collection '%s'.", deleted, collection_name) except Exception as e: log.exception(f'Error during delete: {e}') @@ -800,7 +800,7 @@ class Oracle23aiClient(VectorDBBase): deleted = cursor.rowcount connection.commit() - log.info(f"Reset complete. Deleted {deleted} items from 'document_chunk' table.") + log.info("Reset complete. Deleted %s items from 'document_chunk' table.", deleted) except Exception as e: log.exception(f'Error during reset: {e}') @@ -875,7 +875,7 @@ class Oracle23aiClient(VectorDBBase): >>> client = Oracle23aiClient() >>> client.delete_collection("obsolete_collection") """ - log.info(f"Deleting collection '{collection_name}'.") + log.info("Deleting collection '%s'.", collection_name) try: with self.get_connection() as connection: @@ -891,7 +891,7 @@ class Oracle23aiClient(VectorDBBase): deleted = cursor.rowcount connection.commit() - log.info(f"Collection '{collection_name}' deleted. Removed {deleted} items.") + log.info("Collection '%s' deleted. Removed %s items.", collection_name, deleted) except Exception as e: log.exception(f"Error deleting collection '{collection_name}': {e}") diff --git a/backend/open_webui/retrieval/vector/dbs/pgvector.py b/backend/open_webui/retrieval/vector/dbs/pgvector.py index 2cd086f18e..91158bcc03 100644 --- a/backend/open_webui/retrieval/vector/dbs/pgvector.py +++ b/backend/open_webui/retrieval/vector/dbs/pgvector.py @@ -326,7 +326,7 @@ class PgvectorClient(VectorDBBase): }, ) self.session.commit() - log.info(f"Encrypted & inserted {len(items)} into '{collection_name}'") + log.info("Encrypted & inserted %s into '%s'", len(items), collection_name) else: new_items = [] @@ -342,7 +342,7 @@ class PgvectorClient(VectorDBBase): new_items.append(new_chunk) self.session.bulk_save_objects(new_items) self.session.commit() - log.info(f"Inserted {len(new_items)} items into collection '{collection_name}'.") + log.info("Inserted %s items into collection '%s'.", len(new_items), collection_name) except Exception as e: self.session.rollback() log.exception(f'Error during insert: {e}') @@ -381,7 +381,7 @@ class PgvectorClient(VectorDBBase): }, ) self.session.commit() - log.info(f"Encrypted & upserted {len(items)} into '{collection_name}'") + log.info("Encrypted & upserted %s into '%s'", len(items), collection_name) else: for item in items: vector = self.adjust_vector_length(item['vector']) @@ -401,7 +401,7 @@ class PgvectorClient(VectorDBBase): ) self.session.add(new_chunk) self.session.commit() - log.info(f"Upserted {len(items)} items into collection '{collection_name}'.") + log.info("Upserted %s items into collection '%s'.", len(items), collection_name) except Exception as e: self.session.rollback() log.exception(f'Error during upsert: {e}') @@ -712,7 +712,7 @@ class PgvectorClient(VectorDBBase): query = query.filter(DocumentChunk.vmetadata[key].astext == str(value)) deleted = query.delete(synchronize_session=False) self.session.commit() - log.info(f"Deleted {deleted} items from collection '{collection_name}'.") + log.info("Deleted %s items from collection '%s'.", deleted, collection_name) except Exception as e: self.session.rollback() log.exception(f'Error during delete: {e}') @@ -722,7 +722,7 @@ class PgvectorClient(VectorDBBase): try: deleted = self.session.query(DocumentChunk).delete() self.session.commit() - log.info(f"Reset complete. Deleted {deleted} items from 'document_chunk' table.") + log.info("Reset complete. Deleted %s items from 'document_chunk' table.", deleted) except Exception as e: self.session.rollback() log.exception(f'Error during reset: {e}') @@ -746,4 +746,4 @@ class PgvectorClient(VectorDBBase): def delete_collection(self, collection_name: str) -> None: self.delete(collection_name) - log.info(f"Collection '{collection_name}' deleted.") + log.info("Collection '%s' deleted.", collection_name) diff --git a/backend/open_webui/retrieval/vector/dbs/pinecone.py b/backend/open_webui/retrieval/vector/dbs/pinecone.py index 44f55e5582..4d13977f3f 100644 --- a/backend/open_webui/retrieval/vector/dbs/pinecone.py +++ b/backend/open_webui/retrieval/vector/dbs/pinecone.py @@ -106,16 +106,16 @@ class PineconeClient(VectorDBBase): try: # Check if index exists if self.index_name not in self.client.list_indexes().names(): - log.info(f"Creating Pinecone index '{self.index_name}'...") + log.info("Creating Pinecone index '%s'...", self.index_name) self.client.create_index( name=self.index_name, dimension=self.dimension, metric=self.metric, spec=ServerlessSpec(cloud=self.cloud, region=self.environment), ) - log.info(f"Successfully created Pinecone index '{self.index_name}'") + log.info("Successfully created Pinecone index '%s'", self.index_name) else: - log.info(f"Using existing Pinecone index '{self.index_name}'") + log.info("Using existing Pinecone index '%s'", self.index_name) # Connect to the index self.index = self.client.Index( @@ -245,7 +245,7 @@ class PineconeClient(VectorDBBase): collection_name_with_prefix = self._get_collection_name_with_prefix(collection_name) try: self.index.delete(filter={'collection_name': collection_name_with_prefix}) - log.info(f"Collection '{collection_name_with_prefix}' deleted (all vectors removed).") + log.info("Collection '%s' deleted (all vectors removed).", collection_name_with_prefix) except Exception as e: log.warning(f"Failed to delete collection '{collection_name_with_prefix}': {e}") raise @@ -276,7 +276,7 @@ class PineconeClient(VectorDBBase): elapsed = time.time() - start_time log.debug('Insert of %s vectors took %.2f seconds', len(points), elapsed) log.info( - f"Successfully inserted {len(points)} vectors in parallel batches into '{collection_name_with_prefix}'" + "Successfully inserted %s vectors in parallel batches into '%s'", len(points), collection_name_with_prefix ) def upsert(self, collection_name: str, items: List[VectorItem]) -> None: @@ -305,7 +305,7 @@ class PineconeClient(VectorDBBase): elapsed = time.time() - start_time log.debug('Upsert of %s vectors took %.2f seconds', len(points), elapsed) log.info( - f"Successfully upserted {len(points)} vectors in parallel batches into '{collection_name_with_prefix}'" + "Successfully upserted %s vectors in parallel batches into '%s'", len(points), collection_name_with_prefix ) async def insert_async(self, collection_name: str, items: List[VectorItem]) -> None: @@ -326,7 +326,9 @@ class PineconeClient(VectorDBBase): if isinstance(result, Exception): log.error(f'Error in async insert batch: {result}') raise result - log.info(f"Successfully async inserted {len(points)} vectors in batches into '{collection_name_with_prefix}'") + log.info( + "Successfully async inserted %s vectors in batches into '%s'", len(points), collection_name_with_prefix + ) async def upsert_async(self, collection_name: str, items: List[VectorItem]) -> None: """Async version of upsert using asyncio and run_in_executor for improved performance.""" @@ -346,7 +348,9 @@ class PineconeClient(VectorDBBase): if isinstance(result, Exception): log.error(f'Error in async upsert batch: {result}') raise result - log.info(f"Successfully async upserted {len(points)} vectors in batches into '{collection_name_with_prefix}'") + log.info( + "Successfully async upserted %s vectors in batches into '%s'", len(points), collection_name_with_prefix + ) def search( self, @@ -477,7 +481,7 @@ class PineconeClient(VectorDBBase): log.debug( "Deleted batch of %s vectors by ID from '%s'", len(batch_ids), collection_name_with_prefix ) - log.info(f"Successfully deleted {len(ids)} vectors by ID from '{collection_name_with_prefix}'") + log.info("Successfully deleted %s vectors by ID from '%s'", len(ids), collection_name_with_prefix) elif filter: # Combine user filter with collection_name @@ -486,7 +490,7 @@ class PineconeClient(VectorDBBase): pinecone_filter.update(filter) # Delete by metadata filter self.index.delete(filter=pinecone_filter) - log.info(f"Successfully deleted vectors by filter from '{collection_name_with_prefix}'") + log.info("Successfully deleted vectors by filter from '%s'", collection_name_with_prefix) else: log.warning('No ids or filter provided for delete operation') diff --git a/backend/open_webui/retrieval/vector/dbs/qdrant.py b/backend/open_webui/retrieval/vector/dbs/qdrant.py index 7156a16c53..8462badf1f 100644 --- a/backend/open_webui/retrieval/vector/dbs/qdrant.py +++ b/backend/open_webui/retrieval/vector/dbs/qdrant.py @@ -119,7 +119,7 @@ class QdrantClient(VectorDBBase): on_disk=self.QDRANT_ON_DISK, ), ) - log.info(f'collection {collection_name_with_prefix} successfully created!') + log.info('collection %s successfully created!', collection_name_with_prefix) def _create_collection_if_not_exists(self, collection_name, dimension): if not self.has_collection(collection_name=collection_name): diff --git a/backend/open_webui/retrieval/vector/dbs/qdrant_multitenancy.py b/backend/open_webui/retrieval/vector/dbs/qdrant_multitenancy.py index 837faacea0..20fd8210d6 100644 --- a/backend/open_webui/retrieval/vector/dbs/qdrant_multitenancy.py +++ b/backend/open_webui/retrieval/vector/dbs/qdrant_multitenancy.py @@ -148,7 +148,7 @@ class QdrantClient(VectorDBBase): m=0, ), ) - log.info(f'Multi-tenant collection {mt_collection_name} created with dimension {dimension}!') + log.info('Multi-tenant collection %s created with dimension %s!', mt_collection_name, dimension) self.client.create_payload_index( collection_name=mt_collection_name, diff --git a/backend/open_webui/retrieval/vector/dbs/s3vector.py b/backend/open_webui/retrieval/vector/dbs/s3vector.py index f8888ac177..37effd3456 100644 --- a/backend/open_webui/retrieval/vector/dbs/s3vector.py +++ b/backend/open_webui/retrieval/vector/dbs/s3vector.py @@ -36,7 +36,7 @@ class S3VectorClient(VectorDBBase): if self.bucket_name and self.region: try: self.client = boto3.client('s3vectors', region_name=self.region) - log.info(f"S3Vector client initialized for bucket '{self.bucket_name}' in region '{self.region}'") + log.info("S3Vector client initialized for bucket '%s' in region '%s'", self.bucket_name, self.region) except Exception as e: log.error(f'Failed to initialize S3Vector client: {e}') self.client = None @@ -70,7 +70,9 @@ class S3VectorClient(VectorDBBase): ] }, ) - log.info(f'Created S3 index: {index_name} (dim={dimension}, type={data_type}, metric={distance_metric})') + log.info( + 'Created S3 index: %s (dim=%s, type=%s, metric=%s)', index_name, dimension, data_type, distance_metric + ) except Exception as e: log.error(f"Error creating S3 index '{index_name}': {e}") raise @@ -137,9 +139,9 @@ class S3VectorClient(VectorDBBase): return try: - log.info(f"Deleting collection '{collection_name}'") + log.info("Deleting collection '%s'", collection_name) self.client.delete_index(vectorBucketName=self.bucket_name, indexName=collection_name) - log.info(f"Successfully deleted collection '{collection_name}'") + log.info("Successfully deleted collection '%s'", collection_name) except Exception as e: log.error(f"Error deleting collection '{collection_name}': {e}") raise @@ -156,7 +158,7 @@ class S3VectorClient(VectorDBBase): try: if not self.has_collection(collection_name): - log.info(f"Index '{collection_name}' does not exist. Creating index.") + log.info("Index '%s' does not exist. Creating index.", collection_name) self._create_index( index_name=collection_name, dimension=dimension, @@ -202,9 +204,11 @@ class S3VectorClient(VectorDBBase): indexName=collection_name, vectors=batch, ) - log.info(f"Inserted batch {i // batch_size + 1}: {len(batch)} vectors into index '{collection_name}'.") + log.info( + "Inserted batch %s: %s vectors into index '%s'.", i // batch_size + 1, len(batch), collection_name + ) - log.info(f"Completed insertion of {len(vectors)} vectors into index '{collection_name}'.") + log.info("Completed insertion of %s vectors into index '%s'.", len(vectors), collection_name) except Exception as e: log.error(f'Error inserting vectors: {e}') raise @@ -218,11 +222,11 @@ class S3VectorClient(VectorDBBase): return dimension = len(items[0]['vector']) - log.info(f'Upsert dimension: {dimension}') + log.info('Upsert dimension: %s', dimension) try: if not self.has_collection(collection_name): - log.info(f"Index '{collection_name}' does not exist. Creating index for upsert.") + log.info("Index '%s' does not exist. Creating index for upsert.", collection_name) self._create_index( index_name=collection_name, dimension=dimension, @@ -264,10 +268,14 @@ class S3VectorClient(VectorDBBase): batch = vectors[i : i + batch_size] if i == 0: # Log sample info for first batch only log.info( - f'Upserting batch 1: {len(batch)} vectors. First vector sample: key={batch[0]["key"]}, data_type={type(batch[0]["data"]["float32"])}, data_len={len(batch[0]["data"]["float32"])}' + 'Upserting batch 1: %s vectors. First vector sample: key=%s, data_type=%s, data_len=%s', + len(batch), + batch[0]['key'], + type(batch[0]['data']['float32']), + len(batch[0]['data']['float32']), ) else: - log.info(f'Upserting batch {i // batch_size + 1}: {len(batch)} vectors.') + log.info('Upserting batch %s: %s vectors.', i // batch_size + 1, len(batch)) self.client.put_vectors( vectorBucketName=self.bucket_name, @@ -275,7 +283,7 @@ class S3VectorClient(VectorDBBase): vectors=batch, ) - log.info(f"Completed upsert of {len(vectors)} vectors into index '{collection_name}'.") + log.info("Completed upsert of %s vectors into index '%s'.", len(vectors), collection_name) except Exception as e: log.error(f'Error upserting vectors: {e}') raise @@ -300,7 +308,7 @@ class S3VectorClient(VectorDBBase): return None try: - log.info(f"Searching collection '{collection_name}' with {len(vectors)} query vectors, limit={limit}") + log.info("Searching collection '%s' with %s query vectors, limit=%s", collection_name, len(vectors), limit) # Initialize result lists all_ids = [] @@ -362,7 +370,7 @@ class S3VectorClient(VectorDBBase): all_metadatas.append(query_metadatas) all_distances.append(query_distances) - log.info(f'Search completed. Found results for {len(all_ids)} queries') + log.info('Search completed. Found results for %s queries', len(all_ids)) # Return SearchResult format return SearchResult( @@ -402,7 +410,7 @@ class S3VectorClient(VectorDBBase): return self.get(collection_name) try: - log.info(f"Querying collection '{collection_name}' with filter: {filter}") + log.info("Querying collection '%s' with filter: %s", collection_name, filter) # For S3 Vector, we need to use list_vectors and then filter results # Since S3 Vector may not support complex server-side filtering, @@ -437,7 +445,7 @@ class S3VectorClient(VectorDBBase): if limit and len(filtered_ids) >= limit: break - log.info(f'Filter applied: {len(filtered_ids)} vectors match out of {len(all_ids)} total') + log.info('Filter applied: %s vectors match out of %s total', len(filtered_ids), len(all_ids)) # Return GetResult format if filtered_ids: @@ -472,7 +480,7 @@ class S3VectorClient(VectorDBBase): return GetResult(ids=[[]], documents=[[]], metadatas=[[]]) try: - log.info(f"Retrieving all vectors from collection '{collection_name}'") + log.info("Retrieving all vectors from collection '%s'", collection_name) # Initialize result lists all_ids = [] @@ -534,7 +542,7 @@ class S3VectorClient(VectorDBBase): if not next_token: break - log.info(f"Retrieved {len(all_ids)} vectors from collection '{collection_name}'") + log.info("Retrieved %s vectors from collection '%s'", len(all_ids), collection_name) # Return in GetResult format # The Open WebUI GetResult expects lists of lists, so we wrap each list @@ -576,17 +584,17 @@ class S3VectorClient(VectorDBBase): try: if ids: # Delete by specific vector IDs/keys - log.info(f"Deleting {len(ids)} vectors by IDs from collection '{collection_name}'") + log.info("Deleting %s vectors by IDs from collection '%s'", len(ids), collection_name) self.client.delete_vectors( vectorBucketName=self.bucket_name, indexName=collection_name, keys=ids, ) - log.info(f"Deleted {len(ids)} vectors from index '{collection_name}'") + log.info("Deleted %s vectors from index '%s'", len(ids), collection_name) elif filter: # Handle filter-based deletion - log.info(f"Deleting vectors by filter from collection '{collection_name}': {filter}") + log.info("Deleting vectors by filter from collection '%s': %s", collection_name, filter) # If this is a knowledge collection and we have a file_id filter, # also clean up the corresponding file-specific collection @@ -595,7 +603,8 @@ class S3VectorClient(VectorDBBase): file_collection_name = f'file-{file_id}' if self.has_collection(file_collection_name): log.info( - f"Found related file-specific collection '{file_collection_name}', deleting it to prevent duplicates" + "Found related file-specific collection '%s', deleting it to prevent duplicates", + file_collection_name, ) self.delete_collection(file_collection_name) @@ -604,7 +613,7 @@ class S3VectorClient(VectorDBBase): query_result = self.query(collection_name, filter) if query_result and query_result.ids and query_result.ids[0]: matching_ids = query_result.ids[0] - log.info(f'Found {len(matching_ids)} vectors matching filter, deleting them') + log.info('Found %s vectors matching filter, deleting them', len(matching_ids)) # Delete the matching vectors by ID self.client.delete_vectors( @@ -612,7 +621,7 @@ class S3VectorClient(VectorDBBase): indexName=collection_name, keys=matching_ids, ) - log.info(f"Deleted {len(matching_ids)} vectors from index '{collection_name}' using filter") + log.info("Deleted %s vectors from index '%s' using filter", len(matching_ids), collection_name) else: log.warning('No vectors found matching the filter criteria') else: @@ -645,11 +654,11 @@ class S3VectorClient(VectorDBBase): try: self.client.delete_index(vectorBucketName=self.bucket_name, indexName=index_name) deleted_count += 1 - log.info(f'Deleted index: {index_name}') + log.info('Deleted index: %s', index_name) except Exception as e: log.error(f"Error deleting index '{index_name}': {e}") - log.info(f'Reset completed: deleted {deleted_count} indexes') + log.info('Reset completed: deleted %s indexes', deleted_count) except Exception as e: log.error(f'Error during reset: {e}') diff --git a/backend/open_webui/retrieval/vector/dbs/valkey.py b/backend/open_webui/retrieval/vector/dbs/valkey.py index c734a7168b..bf202a22c0 100644 --- a/backend/open_webui/retrieval/vector/dbs/valkey.py +++ b/backend/open_webui/retrieval/vector/dbs/valkey.py @@ -279,7 +279,7 @@ class ValkeyClient(VectorDBBase): f'{self._format_version(MIN_VALKEY_VERSION)}. valkey-search 1.2.0 requires Valkey core ' '9.0.1 or later. Upgrade your server or use valkey-bundle:9.1.0-rc2+.' ) - log.info(f'Valkey core version: {self._format_version(version) if version else "unknown"}') + log.info('Valkey core version: %s', self._format_version(version) if version else 'unknown') def _check_search_module(self) -> None: try: @@ -331,7 +331,7 @@ class ValkeyClient(VectorDBBase): 'TEXT field type and filter-only FT.SEARCH support required by this backend. ' 'Upgrade to valkey-bundle:9.1.0-rc2+ or load valkey-search 1.2.0+ as a module.' ) - log.info(f'valkey-search version: {self._format_version(search_version) if search_version else "unknown"}') + log.info('valkey-search version: %s', self._format_version(search_version) if search_version else 'unknown') def _index_name(self, collection_name: str) -> str: return f'idx:{self.collection_prefix}:{collection_name}' @@ -385,8 +385,11 @@ class ValkeyClient(VectorDBBase): try: g['glide_ft'].create(self.client, index_name, schema, options) log.info( - f'Created Valkey index {index_name} with dimension={dimension}, ' - f'type={self.index_type}, metric={self.distance_metric}' + 'Created Valkey index %s with dimension=%s, type=%s, metric=%s', + index_name, + dimension, + self.index_type, + self.distance_metric, ) except g['RequestError'] as e: if 'already exists' in str(e).lower(): @@ -456,7 +459,7 @@ class ValkeyClient(VectorDBBase): index_name = self._index_name(collection_name) try: self._g['glide_ft'].dropindex(self.client, index_name) - log.info(f'Dropped index {index_name}') + log.info('Dropped index %s', index_name) except self._g['RequestError'] as e: log.debug('Could not drop index %s: %s', index_name, e) @@ -656,7 +659,7 @@ class ValkeyClient(VectorDBBase): collections.append(name[len(idx_prefix) :]) try: glide_ft.dropindex(self.client, idx) - log.info(f'Dropped index: {name}') + log.info('Dropped index: %s', name) except Exception as e: log.error(f'Error dropping index {name}: {e}') except Exception as e: @@ -664,7 +667,7 @@ class ValkeyClient(VectorDBBase): for collection in collections: self._delete_keys_by_prefix(self._key_prefix(collection)) - log.info(f'Valkey vector store reset complete (prefix: {self.collection_prefix})') + log.info('Valkey vector store reset complete (prefix: %s)', self.collection_prefix) def _delete_keys_by_prefix(self, prefix: str) -> None: cursor = '0' diff --git a/backend/open_webui/retrieval/web/exa.py b/backend/open_webui/retrieval/web/exa.py index 860917854e..068cc380b9 100644 --- a/backend/open_webui/retrieval/web/exa.py +++ b/backend/open_webui/retrieval/web/exa.py @@ -31,7 +31,7 @@ def search_exa( count (int): Number of results to return filter_list (Optional[list[str]]): List of domains to filter results by """ - log.info(f'Searching with Exa for query: {query}') + log.info('Searching with Exa for query: %s', query) headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'} @@ -58,7 +58,7 @@ def search_exa( ) ) - log.info(f'Found {len(results)} results') + log.info('Found %s results', len(results)) return [ SearchResult( link=result.url, diff --git a/backend/open_webui/retrieval/web/external.py b/backend/open_webui/retrieval/web/external.py index f9be905ed6..983f9d4d34 100644 --- a/backend/open_webui/retrieval/web/external.py +++ b/backend/open_webui/retrieval/web/external.py @@ -53,7 +53,7 @@ def search_external( ) for result in results[:count] ] - log.info(f'External search results: {results}') + log.info('External search results: %s', results) return results except Exception as e: log.error(f'Error in External search: {e}') diff --git a/backend/open_webui/retrieval/web/firecrawl.py b/backend/open_webui/retrieval/web/firecrawl.py index 04e03aff55..0febae8403 100644 --- a/backend/open_webui/retrieval/web/firecrawl.py +++ b/backend/open_webui/retrieval/web/firecrawl.py @@ -225,7 +225,7 @@ def search_firecrawl( ) ) - log.info(f'FireCrawl search results: {search_results}') + log.info('FireCrawl search results: %s', search_results) return search_results except Exception as e: log.error(f'Error in FireCrawl search: {e}') diff --git a/backend/open_webui/retrieval/web/ollama.py b/backend/open_webui/retrieval/web/ollama.py index 7ed19b91b1..8535edf172 100644 --- a/backend/open_webui/retrieval/web/ollama.py +++ b/backend/open_webui/retrieval/web/ollama.py @@ -23,7 +23,7 @@ def search_ollama_cloud( count (int): Number of results to return filter_list (Optional[list[str]]): List of domains to filter results by """ - log.info(f'Searching with Ollama for query: {query}') + log.info('Searching with Ollama for query: %s', query) headers = {'Authorization': f'Bearer {api_key}', 'Content-Type': 'application/json'} payload = {'query': query, 'max_results': count} @@ -34,7 +34,7 @@ def search_ollama_cloud( data = response.json() results = data.get('results', []) - log.info(f'Found {len(results)} results') + log.info('Found %s results', len(results)) if filter_list: results = get_filtered_results(results, filter_list) diff --git a/backend/open_webui/retrieval/web/searchapi.py b/backend/open_webui/retrieval/web/searchapi.py index 855269ef02..bd4c292c18 100644 --- a/backend/open_webui/retrieval/web/searchapi.py +++ b/backend/open_webui/retrieval/web/searchapi.py @@ -31,7 +31,7 @@ def search_searchapi( response = requests.request('GET', url) json_response = response.json() - log.info(f'results from searchapi search: {json_response}') + log.info('results from searchapi search: %s', json_response) results = sorted(json_response.get('organic_results', []), key=lambda x: x.get('position', 0)) if filter_list: diff --git a/backend/open_webui/retrieval/web/serpapi.py b/backend/open_webui/retrieval/web/serpapi.py index 602f60d7a7..2e5b958937 100644 --- a/backend/open_webui/retrieval/web/serpapi.py +++ b/backend/open_webui/retrieval/web/serpapi.py @@ -31,7 +31,7 @@ def search_serpapi( response = requests.request('GET', url) json_response = response.json() - log.info(f'results from serpapi search: {json_response}') + log.info('results from serpapi search: %s', json_response) results = sorted(json_response.get('organic_results', []), key=lambda x: x.get('position', 0)) if filter_list: diff --git a/backend/open_webui/retrieval/web/serply.py b/backend/open_webui/retrieval/web/serply.py index c4909c14e0..1daac461df 100644 --- a/backend/open_webui/retrieval/web/serply.py +++ b/backend/open_webui/retrieval/web/serply.py @@ -51,7 +51,7 @@ def search_serply( response.raise_for_status() json_response = response.json() - log.info(f'results from serply search: {json_response}') + log.info('results from serply search: %s', json_response) results = sorted(json_response.get('results', []), key=lambda x: x.get('realPosition', 0)) if filter_list: diff --git a/backend/open_webui/retrieval/web/yandex.py b/backend/open_webui/retrieval/web/yandex.py index f12c8ab447..0e6306987f 100644 --- a/backend/open_webui/retrieval/web/yandex.py +++ b/backend/open_webui/retrieval/web/yandex.py @@ -112,7 +112,7 @@ def search_yandex( for result in results[:count] ] - log.info(f'Yandex search results: {results}') + log.info('Yandex search results: %s', results) return results except Exception as e: diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index bcfc88bec6..9fa5d89304 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -157,7 +157,7 @@ def convert_audio_to_mp3(file_path): output_path = os.path.splitext(file_path)[0] + '.mp3' audio = AudioSegment.from_file(file_path) audio.export(output_path, format='mp3') - log.info(f'Converted {file_path} to {output_path}') + log.info('Converted %s to %s', file_path, output_path) return output_path except Exception as e: log.error(f'Error converting audio file: {e}') @@ -208,7 +208,7 @@ def transcode_audio_to_mp3(audio_data: bytes, content_type_header: str, output_p audio_segment = AudioSegment.from_file(io.BytesIO(audio_data)) audio_segment.export(str(output_path), format='mp3') - log.info(f'Transcoded {mime_type} audio to MP3: {output_path}') + log.info('Transcoded %s audio to MP3: %s', mime_type, output_path) return True @@ -631,7 +631,7 @@ async def _transcribe_whisper(request, file_path, languages, file_dir, id): language=languages[0], multilingual=WHISPER_MULTILINGUAL, ) - log.info("Detected language '%s' with probability %f" % (info.language, info.language_probability)) + log.info("Detected language '%s' with probability %f", info.language, info.language_probability) return ''.join([segment.text for segment in list(segments)]) transcript = await asyncio.to_thread(_run) @@ -952,7 +952,9 @@ async def _transcribe_mistral(request, file_path, filename, metadata, file_dir, try: model = await Config.get('audio.stt.model') or 'voxtral-mini-latest' log.info( - f'Mistral STT - model: {model}, method: {"chat_completions" if use_chat_completions else "transcriptions"}' + 'Mistral STT - model: %s, method: %s', + model, + 'chat_completions' if use_chat_completions else 'transcriptions', ) session = await get_session() @@ -1075,7 +1077,7 @@ async def _transcribe_mistral(request, file_path, filename, metadata, file_dir, async def transcribe(request: Request, file_path: str, metadata: Optional[dict] = None, user=None): - log.info(f'transcribe: {file_path} {metadata}') + log.info('transcribe: %s %s', file_path, metadata) if BYPASS_PYDUB_PREPROCESSING: log.info('Bypassing pydub preprocessing (BYPASS_PYDUB_PREPROCESSING=true)') @@ -1202,7 +1204,7 @@ async def transcription( status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED, ) - log.info(f'file.content_type: {file.content_type}') + log.info('file.content_type: %s', file.content_type) stt_supported_content_types = await Config.get('audio.stt.supported_content_types', []) if not strict_match_mime_type(stt_supported_content_types, file.content_type): diff --git a/backend/open_webui/routers/auths.py b/backend/open_webui/routers/auths.py index d9f6b022ba..240c761931 100644 --- a/backend/open_webui/routers/auths.py +++ b/backend/open_webui/routers/auths.py @@ -548,8 +548,8 @@ async def ldap_auth( ] if ENABLE_LDAP_GROUP_MANAGEMENT: search_attributes.append(f'{LDAP_ATTRIBUTE_FOR_GROUPS}') - log.info(f'LDAP Group Management enabled. Adding {LDAP_ATTRIBUTE_FOR_GROUPS} to search attributes') - log.info(f'LDAP search attributes: {search_attributes}') + log.info('LDAP Group Management enabled. Adding %s to search attributes', LDAP_ATTRIBUTE_FOR_GROUPS) + log.info('LDAP search attributes: %s', search_attributes) search_success = await asyncio.to_thread( connection_app.search, @@ -586,30 +586,30 @@ async def ldap_auth( user_groups = [] if ENABLE_LDAP_GROUP_MANAGEMENT and LDAP_ATTRIBUTE_FOR_GROUPS in entry: group_dns = entry[LDAP_ATTRIBUTE_FOR_GROUPS] - log.info(f'LDAP raw group DNs for user {username_list}: {group_dns}') + log.info('LDAP raw group DNs for user %s: %s', username_list, group_dns) if group_dns: - log.info(f'LDAP group_dns original: {group_dns}') - log.info(f'LDAP group_dns type: {type(group_dns)}') - log.info(f'LDAP group_dns length: {len(group_dns)}') + log.info('LDAP group_dns original: %s', group_dns) + log.info('LDAP group_dns type: %s', type(group_dns)) + log.info('LDAP group_dns length: %s', len(group_dns)) if hasattr(group_dns, 'value'): group_dns = group_dns.value - log.info(f'Extracted .value property: {group_dns}') + log.info('Extracted .value property: %s', group_dns) elif hasattr(group_dns, '__iter__') and not isinstance(group_dns, (str, bytes)): group_dns = list(group_dns) - log.info(f'Converted to list: {group_dns}') + log.info('Converted to list: %s', group_dns) if isinstance(group_dns, list): group_dns = [str(item) for item in group_dns] else: group_dns = [str(group_dns)] - log.info(f'LDAP group_dns after processing - type: {type(group_dns)}, length: {len(group_dns)}') + log.info('LDAP group_dns after processing - type: %s, length: %s', type(group_dns), len(group_dns)) for group_idx, group_dn in enumerate(group_dns): group_dn = str(group_dn) - log.info(f'Processing group DN #{group_idx + 1}: {group_dn}') + log.info('Processing group DN #%s: %s', group_idx + 1, group_dn) try: group_cn = extract_group_cn_from_dn(group_dn) @@ -621,9 +621,9 @@ async def ldap_auth( except Exception as e: log.warning(f'Failed to extract group name from DN {group_dn}: {e}') - log.info(f'LDAP groups for user {username_list}: {user_groups} (total: {len(user_groups)})') + log.info('LDAP groups for user %s: %s (total: %s)', username_list, user_groups, len(user_groups)) else: - log.info(f'No groups found for user {username_list}') + log.info('No groups found for user %s', username_list) elif ENABLE_LDAP_GROUP_MANAGEMENT: log.warning( f'LDAP Group Management enabled but {LDAP_ATTRIBUTE_FOR_GROUPS} attribute not found in user entry' @@ -691,7 +691,7 @@ async def ldap_auth( if ENABLE_LDAP_GROUP_CREATION: await Groups.create_groups_by_group_names(user.id, user_groups, db=db) await Groups.sync_groups_by_group_names(user.id, user_groups, db=db) - log.info(f'Successfully synced groups for user {user.id}: {user_groups}') + log.info('Successfully synced groups for user %s: %s', user.id, user_groups) except Exception as e: log.error(f'Failed to sync groups for user {user.id}: {e}') @@ -1156,7 +1156,7 @@ async def get_admin_details( admin_email = await Config.get('auth.admin.email') admin_name = None - log.info(f'Admin details - Email: {admin_email}, Name: {admin_name}') + log.info('Admin details - Email: %s, Name: %s', admin_email, admin_name) if admin_email: admin = await Users.get_user_by_email(admin_email, db=db) diff --git a/backend/open_webui/routers/configs.py b/backend/open_webui/routers/configs.py index 9fc6ccd343..f591908a6d 100644 --- a/backend/open_webui/routers/configs.py +++ b/backend/open_webui/routers/configs.py @@ -575,7 +575,7 @@ async def verify_tool_servers_config(request: Request, form_data: ToolServerConn 'oauth_server_metadata': oauth_server_metadata.model_dump(mode='json'), } except Exception as e: - log.info(f'Failed to parse OAuth 2.1 discovery document: {e}') + log.info('Failed to parse OAuth 2.1 discovery document: %s', e) raise HTTPException( status_code=400, detail=f'Failed to parse OAuth 2.1 discovery document from {discovery_url}', diff --git a/backend/open_webui/routers/files.py b/backend/open_webui/routers/files.py index 575d3a4461..811c223aaa 100644 --- a/backend/open_webui/routers/files.py +++ b/backend/open_webui/routers/files.py @@ -177,7 +177,7 @@ async def process_uploaded_file( # processing (Tools, vision models). Attempting text # extraction causes "Timeout reached while detecting # encoding" errors. - log.info(f'Video file detected ({content_type}), skipping text extraction') + log.info('Video file detected (%s), skipping text extraction', content_type) await Files.update_file_data_by_id( file_item.id, {'status': 'completed'}, @@ -190,7 +190,7 @@ async def process_uploaded_file( # Documents, or media files explicitly enabled for the # configured content extraction engine. if not content_type: - log.info(f'File type {file.content_type} is not provided, but trying to process anyway') + log.info('File type %s is not provided, but trying to process anyway', file.content_type) await process_file( request, ProcessFileForm(file_id=file_item.id), @@ -242,7 +242,7 @@ async def process_uploaded_file( ) if not knowledge_file: raise Exception(f'Failed to link file {file_item.id} to knowledge {knowledge_id}') - log.info(f'Linked file {file_item.id} to knowledge {knowledge_id}') + log.info('Linked file %s to knowledge %s', file_item.id, knowledge_id) except Exception as e: log.warning(f'Failed to link file {file_item.id} to knowledge {knowledge_id}: {e}') raise @@ -322,7 +322,7 @@ async def upload_file_handler( background_tasks: Optional[BackgroundTasks] = None, db: Optional[AsyncSession] = None, ): - log.info(f'file.content_type: {file.content_type} {process}') + log.info('file.content_type: %s %s', file.content_type, process) if isinstance(metadata, str): try: @@ -868,7 +868,7 @@ async def get_html_file_content_by_id( # Check if the file already exists in the cache if file_path.is_file(): - log.info(f'file_path: {file_path}') + log.info('file_path: %s', file_path) return FileResponse(file_path) else: raise HTTPException( diff --git a/backend/open_webui/routers/images.py b/backend/open_webui/routers/images.py index 25c66dcc10..a15dd91498 100644 --- a/backend/open_webui/routers/images.py +++ b/backend/open_webui/routers/images.py @@ -170,7 +170,7 @@ def get_image_file_item(base64_string, param_name='image'): async def set_image_model(request: Request, model: str): - log.info(f'Setting image model to {model}') + log.info('Setting image model to %s', model) await Config.upsert({'image_generation.model': model}) image_config = await get_image_config() if image_config.IMAGE_GENERATION_ENGINE in ['', 'automatic1111']: diff --git a/backend/open_webui/routers/knowledge.py b/backend/open_webui/routers/knowledge.py index e55086d866..fa0065b18e 100644 --- a/backend/open_webui/routers/knowledge.py +++ b/backend/open_webui/routers/knowledge.py @@ -351,7 +351,7 @@ async def reindex_knowledge_files( failed_files = [] start_time = time.monotonic() - log.info(f'Starting reindexing for {len(knowledge_bases)} knowledge bases ({total_files} files)') + log.info('Starting reindexing for %s knowledge bases (%s files)', len(knowledge_bases), total_files) for kb_idx, (knowledge_base, files) in enumerate(knowledge_base_files, start=1): try: @@ -371,8 +371,13 @@ async def reindex_knowledge_files( eta = f', ETA: {round(elapsed / (processed_files - 1) * remaining_files)}s' log.info( - f'Reindexing knowledge base {kb_idx}/{len(knowledge_bases)} ' - f'file {processed_files}/{total_files}{eta}: {file.filename}' + 'Reindexing knowledge base %s/%s file %s/%s%s: %s', + kb_idx, + len(knowledge_bases), + processed_files, + total_files, + eta, + file.filename, ) try: @@ -397,7 +402,7 @@ async def reindex_knowledge_files( for failed in failed_files: log.warning(f'File ID: {failed["file_id"]}, Error: {failed["error"]}') - log.info(f'Reindexing completed in {round(time.monotonic() - start_time)}s.') + log.info('Reindexing completed in %ss.', round(time.monotonic() - start_time)) await publish_event( request, EVENTS.KNOWLEDGE_REINDEXED, @@ -426,14 +431,14 @@ async def reindex_knowledge_base_metadata_embeddings( this entire operation would exhaust the connection pool. """ knowledge_bases = await Knowledges.get_knowledge_bases() - log.info(f'Reindexing embeddings for {len(knowledge_bases)} knowledge bases') + log.info('Reindexing embeddings for %s knowledge bases', len(knowledge_bases)) success_count = 0 for kb in knowledge_bases: if await embed_knowledge_base_metadata(request, kb.id, kb.name, kb.description): success_count += 1 - log.info(f'Embedding reindex complete: {success_count}/{len(knowledge_bases)}') + log.info('Embedding reindex complete: %s/%s', success_count, len(knowledge_bases)) return {'total': len(knowledge_bases), 'success': success_count} @@ -1687,11 +1692,11 @@ async def delete_knowledge_by_id( detail=ERROR_MESSAGES.ACCESS_PROHIBITED, ) - log.info(f'Deleting knowledge base: {id} (name: {knowledge.name})') + log.info('Deleting knowledge base: %s (name: %s)', id, knowledge.name) # Get all models models = await Models.get_all_models(db=db) - log.info(f'Found {len(models)} models to check for knowledge base {id}') + log.info('Found %s models to check for knowledge base %s', len(models), id) # Update models that reference this knowledge base for model in models: @@ -1702,7 +1707,7 @@ async def delete_knowledge_by_id( # If the knowledge list changed, update the model if len(updated_knowledge) != len(knowledge_list): - log.info(f'Updating model {model.id} to remove knowledge base {id}') + log.info('Updating model %s to remove knowledge base %s', model.id, id) model.meta.knowledge = updated_knowledge model_form = ModelForm(**model.model_dump()) await Models.update_model_by_id(model.id, model_form, db=db) @@ -2022,7 +2027,7 @@ async def add_files_to_knowledge_batch( ) # Batch-fetch all files to avoid N+1 queries - log.info(f'files/batch/add - {len(form_data)} files') + log.info('files/batch/add - %s files', len(form_data)) file_ids = [form.file_id for form in form_data] files = await Files.get_files_by_ids(file_ids, db=db) diff --git a/backend/open_webui/routers/ollama.py b/backend/open_webui/routers/ollama.py index 8f450e5c7f..01ba8e380e 100644 --- a/backend/open_webui/routers/ollama.py +++ b/backend/open_webui/routers/ollama.py @@ -650,7 +650,7 @@ async def pull_model( form_data['model'] = form_data.get('model', form_data.get('name')) url = (await Config.get('ollama.base_urls', []))[url_idx] - log.info(f'url: {url}') + log.info('url: %s', url) # Admins may pull from any registry return await send_request( @@ -881,7 +881,7 @@ async def embed( if not await Config.get('ollama.enable'): raise HTTPException(status_code=503, detail=ERROR_MESSAGES.OLLAMA_API_DISABLED) - log.info(f'generate_ollama_batch_embeddings {form_data}') + log.info('generate_ollama_batch_embeddings %s', form_data) await check_model_access(user, await Models.get_model_by_id(form_data.model), BYPASS_MODEL_ACCESS_CONTROL) await validate_ollama_backend_idx(request, form_data.model, url_idx, user) @@ -932,7 +932,7 @@ async def embeddings( if not await Config.get('ollama.enable'): raise HTTPException(status_code=503, detail=ERROR_MESSAGES.OLLAMA_API_DISABLED) - log.info(f'generate_ollama_embeddings {form_data}') + log.info('generate_ollama_embeddings %s', form_data) await check_model_access(user, await Models.get_model_by_id(form_data.model), BYPASS_MODEL_ACCESS_CONTROL) await validate_ollama_backend_idx(request, form_data.model, url_idx, user) @@ -1637,11 +1637,11 @@ async def upload_model( async def file_process_stream(): nonlocal ollama_url total_size = os.path.getsize(file_path) - log.info(f'Total Model Size: {total_size}') + log.info('Total Model Size: %s', total_size) # Stage 2: hash the file and emit SSE progress file_hash = await asyncio.to_thread(calculate_sha256, file_path, chunk_size) - log.info(f'Model Hash: {file_hash}') + log.info('Model Hash: %s', file_hash) try: bytes_read = 0 @@ -1675,13 +1675,13 @@ async def upload_model( # Stage 4: create the model model, _ext = os.path.splitext(filename) - log.info(f'Created Model: {model}') + log.info('Created Model: %s', model) create_payload = { 'model': model, 'files': {filename: f'sha256:{file_hash}'}, } - log.info(f'Model Payload: {create_payload}') + log.info('Model Payload: %s', create_payload) async with session.post( f'{ollama_url}/api/create', diff --git a/backend/open_webui/routers/pipelines.py b/backend/open_webui/routers/pipelines.py index 1cfc4e77dd..827fadf7c9 100644 --- a/backend/open_webui/routers/pipelines.py +++ b/backend/open_webui/routers/pipelines.py @@ -221,7 +221,7 @@ async def upload_pipeline( file: UploadFile = File(...), user=Depends(get_admin_user), ): - log.info(f'upload_pipeline: urlIdx={urlIdx}, filename={file.filename}') + log.info('upload_pipeline: urlIdx=%s, filename=%s', urlIdx, file.filename) filename = os.path.basename(file.filename) # Check if the uploaded file is a python file diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index f8362c24dd..9dc95909f8 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -519,7 +519,7 @@ async def unload_embedding_model(request: Request): @router.post('/embedding/update') async def update_embedding_config(request: Request, form_data: EmbeddingModelUpdateForm, user=Depends(get_admin_user)): config = await get_retrieval_config() - log.info(f'Updating embedding model: {config.RAG_EMBEDDING_MODEL} to {form_data.RAG_EMBEDDING_MODEL}') + log.info('Updating embedding model: %s to %s', config.RAG_EMBEDDING_MODEL, form_data.RAG_EMBEDDING_MODEL) await unload_embedding_model(request) try: config.RAG_EMBEDDING_ENGINE = form_data.RAG_EMBEDDING_ENGINE @@ -1159,7 +1159,7 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend else config.RAG_RERANKING_BATCH_SIZE ) - log.info(f'Updating reranking model: {config.RAG_RERANKING_MODEL} to {form_data.RAG_RERANKING_MODEL}') + log.info('Updating reranking model: %s to %s', config.RAG_RERANKING_MODEL, form_data.RAG_RERANKING_MODEL) try: config.RAG_RERANKING_MODEL = ( form_data.RAG_RERANKING_MODEL if form_data.RAG_RERANKING_MODEL is not None else config.RAG_RERANKING_MODEL @@ -1663,7 +1663,7 @@ def save_docs_to_vector_db( existing_file_id = result.metadatas[0][0].get('file_id') if existing_file_id != metadata.get('file_id'): - log.info(f'Document with hash {metadata["hash"]} already exists') + log.info('Document with hash %s already exists', metadata['hash']) raise ValueError(ERROR_MESSAGES.DUPLICATE_CONTENT) if split: @@ -1706,7 +1706,7 @@ def save_docs_to_vector_db( ) docs = text_splitter.split_documents(docs) elif config.TEXT_SPLITTER == 'token': - log.info(f'Using token text splitter: {config.TIKTOKEN_ENCODING_NAME}') + log.info('Using token text splitter: %s', config.TIKTOKEN_ENCODING_NAME) tiktoken.get_encoding(str(config.TIKTOKEN_ENCODING_NAME)) text_splitter = TokenTextSplitter( @@ -1748,16 +1748,16 @@ def save_docs_to_vector_db( try: if VECTOR_DB_CLIENT.has_collection(collection_name=collection_name): - log.info(f'collection {collection_name} already exists') + log.info('collection %s already exists', collection_name) if overwrite: VECTOR_DB_CLIENT.delete_collection(collection_name=collection_name) - log.info(f'deleting existing collection {collection_name}') + log.info('deleting existing collection %s', collection_name) elif add is False: - log.info(f'collection {collection_name} already exists, overwrite is False and add is False') + log.info('collection %s already exists, overwrite is False and add is False', collection_name) return True - log.info(f'generating embeddings for {collection_name}') + log.info('generating embeddings for %s', collection_name) embedding_function = get_embedding_function( config.RAG_EMBEDDING_ENGINE, config.RAG_EMBEDDING_MODEL, @@ -1801,7 +1801,7 @@ def save_docs_to_vector_db( request.app.state.main_loop, ) embeddings = future.result(timeout=embedding_timeout) - log.info(f'embeddings generated {len(embeddings)} for {len(texts)} items') + log.info('embeddings generated %s for %s items', len(embeddings), len(texts)) items = [ { @@ -1813,13 +1813,13 @@ def save_docs_to_vector_db( for idx, text in enumerate(texts) ] - log.info(f'adding to collection {collection_name}') + log.info('adding to collection %s', collection_name) VECTOR_DB_CLIENT.insert( collection_name=collection_name, items=items, ) - log.info(f'added {len(items)} items to collection {collection_name}') + log.info('added %s items to collection %s', len(items), collection_name) return True except Exception as e: log.exception(e) @@ -2011,7 +2011,7 @@ async def process_file( add=(True if form_data.collection_name else False), user=user, ) - log.info(f'added {len(docs)} items to collection {collection_name}') + log.info('added %s items to collection %s', len(docs), collection_name) if result: # Fresh session for the final update. diff --git a/backend/open_webui/routers/scim.py b/backend/open_webui/routers/scim.py index 491e34e1b6..9a96c0b097 100644 --- a/backend/open_webui/routers/scim.py +++ b/backend/open_webui/routers/scim.py @@ -258,7 +258,7 @@ def get_scim_auth(request: Request, authorization: Optional[str] = Header(None)) # Check if SCIM is enabled enable_scim = getattr(request.app.state, 'ENABLE_SCIM', False) - log.info(f'SCIM auth check - raw ENABLE_SCIM: {enable_scim}, type: {type(enable_scim)}') + log.info('SCIM auth check - raw ENABLE_SCIM: %s, type: %s', enable_scim, type(enable_scim)) if not enable_scim: raise HTTPException( diff --git a/backend/open_webui/routers/tasks.py b/backend/open_webui/routers/tasks.py index 21ace03e5a..01e0fa2158 100644 --- a/backend/open_webui/routers/tasks.py +++ b/backend/open_webui/routers/tasks.py @@ -412,7 +412,7 @@ async def generate_queries(request: Request, form_data: dict, user=Depends(get_v ) if getattr(request.state, 'cached_queries', None): - log.info(f'Reusing cached queries: {request.state.cached_queries}') + log.info('Reusing cached queries: %s', request.state.cached_queries) return request.state.cached_queries if getattr(request.state, 'direct', False) and hasattr(request.state, 'model'): diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py index 3abd09b40e..5dbd071dee 100644 --- a/backend/open_webui/socket/main.py +++ b/backend/open_webui/socket/main.py @@ -331,7 +331,7 @@ async def disconnect_user_sessions(user_id: str): for sid in session_ids: await sio.disconnect(sid) if session_ids: - log.info(f'Disconnected {len(session_ids)} session(s) for user {user_id}') + log.info('Disconnected %s session(s) for user %s', len(session_ids), user_id) except Exception as e: log.warning(f'Failed to disconnect sessions for user {user_id}: {e}') @@ -626,7 +626,7 @@ async def ydoc_document_join(sid, data): user_name = data.get('user_name', 'Anonymous') user_color = data.get('user_color', '#000000') - log.info(f'User {user_id} joining document {document_id}') + log.info('User %s joining document %s', user_id, document_id) await YDOC_MANAGER.add_user(document_id=document_id, user_id=sid) # Join Socket.IO room @@ -665,7 +665,7 @@ async def ydoc_document_join(sid, data): skip_sid=sid, ) - log.info(f'User {user_id} successfully joined document {document_id}') + log.info('User %s successfully joined document %s', user_id, document_id) except Exception as e: log.error(f'Error in yjs_document_join: {e}') @@ -826,7 +826,7 @@ async def yjs_document_leave(sid, data): try: document_id = normalize_document_id(data['document_id']) - log.info(f'User {user["id"]} leaving document {document_id}') + log.info('User %s leaving document %s', user['id'], document_id) # Remove user from the document await YDOC_MANAGER.remove_user(document_id=document_id, user_id=sid) @@ -842,7 +842,7 @@ async def yjs_document_leave(sid, data): ) if await YDOC_MANAGER.document_exists(document_id) and len(await YDOC_MANAGER.get_users(document_id)) == 0: - log.info(f'Cleaning up document {document_id} as no users are left') + log.info('Cleaning up document %s as no users are left', document_id) await YDOC_MANAGER.clear_document(document_id) except Exception as e: diff --git a/backend/open_webui/utils/auth.py b/backend/open_webui/utils/auth.py index 42cc8235fe..42d6c77dfa 100644 --- a/backend/open_webui/utils/auth.py +++ b/backend/open_webui/utils/auth.py @@ -542,7 +542,7 @@ async def create_admin_user(email: str, password: str, name: str = 'Admin'): log.debug('Users already exist, skipping admin creation') return None - log.info(f'Creating admin account from environment variables: {email}') + log.info('Creating admin account from environment variables: %s', email) try: hashed = await get_password_hash(password) user = await Auths.insert_new_auth( @@ -552,7 +552,7 @@ async def create_admin_user(email: str, password: str, name: str = 'Admin'): role='admin', ) if user: - log.info(f'Admin account created successfully: {email}') + log.info('Admin account created successfully: %s', email) return user else: log.error('Failed to create admin account from environment variables') diff --git a/backend/open_webui/utils/automations.py b/backend/open_webui/utils/automations.py index 61495c93f6..38045d4096 100644 --- a/backend/open_webui/utils/automations.py +++ b/backend/open_webui/utils/automations.py @@ -211,8 +211,9 @@ async def scheduler_worker_loop(app) -> None: SCHEDULER_POLL_INTERVAL env var (default: 10 seconds). """ log.info( - f'Scheduler worker started (timer poll interval: {TIMER_POLL_INTERVAL}s, ' - f'scheduler poll interval: {SCHEDULER_POLL_INTERVAL}s)' + 'Scheduler worker started (timer poll interval: %ss, scheduler poll interval: %ss)', + TIMER_POLL_INTERVAL, + SCHEDULER_POLL_INTERVAL, ) next_scheduler_poll = 0.0 @@ -240,7 +241,7 @@ async def scheduler_worker_loop(app) -> None: async with get_async_db() as db: batch = await Automations.claim_due(int(time.time_ns()), limit=10, db=db) if batch: - log.info(f'Claimed {len(batch)} due automation(s)') + log.info('Claimed %s due automation(s)', len(batch)) for automation in batch: asyncio.create_task(execute_automation(app, automation)) except Exception: diff --git a/backend/open_webui/utils/chat.py b/backend/open_webui/utils/chat.py index 66cc454e98..d74c15e7d1 100644 --- a/backend/open_webui/utils/chat.py +++ b/backend/open_webui/utils/chat.py @@ -68,7 +68,7 @@ async def generate_direct_chat_completion( ) channel = f'{user_id}:{session_id}:{request_id}' - logging.info(f'WebSocket channel: {channel}') + logging.info('WebSocket channel: %s', channel) if form_data.get('stream'): q = asyncio.Queue() @@ -95,7 +95,7 @@ async def generate_direct_chat_completion( } ) - log.info(f'res: {res}') + log.info('res: %s', res) if res.get('status', False): # Define a generator to stream responses diff --git a/backend/open_webui/utils/misc.py b/backend/open_webui/utils/misc.py index f609fdc85a..4e0f756cb8 100644 --- a/backend/open_webui/utils/misc.py +++ b/backend/open_webui/utils/misc.py @@ -1193,7 +1193,7 @@ def stream_chunks_handler(stream: aiohttp.StreamReader): if len(line) > max_buffer_size: skip_mode = True yield b'data: {}\n' - log.info(f'Skip mode triggered, line size: {len(line)}') + log.info('Skip mode triggered, line size: %s', len(line)) else: yield line + b'\n' @@ -1203,7 +1203,7 @@ def stream_chunks_handler(stream: aiohttp.StreamReader): # Check if buffer exceeds limit if not skip_mode and len(buffer) > max_buffer_size: skip_mode = True - log.info(f'Skip mode triggered, buffer size: {len(buffer)}') + log.info('Skip mode triggered, buffer size: %s', len(buffer)) # Clear oversized buffer to prevent unlimited growth buffer = b'' diff --git a/backend/open_webui/utils/models.py b/backend/open_webui/utils/models.py index 8e0ca1688d..fdb1c7e999 100644 --- a/backend/open_webui/utils/models.py +++ b/backend/open_webui/utils/models.py @@ -377,13 +377,13 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None) if items is None: action_function = functions_by_id.get(action_id) if action_function is None: - log.info(f'Action not found: {action_id}') + log.info('Action not found: %s', action_id) action_items_by_id[action_id] = [] continue function_module = functions_cache.get(action_id) if function_module is None: - log.info(f'Failed to load action module: {action_id}') + log.info('Failed to load action module: %s', action_id) action_items_by_id[action_id] = [] continue items = get_action_items_from_module(action_function, function_module) @@ -397,13 +397,13 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None) if items is None: filter_function = functions_by_id.get(filter_id) if filter_function is None: - log.info(f'Filter not found: {filter_id}') + log.info('Filter not found: %s', filter_id) filter_items_by_id[filter_id] = [] continue function_module = functions_cache.get(filter_id) if function_module is None: - log.info(f'Failed to load filter module: {filter_id}') + log.info('Failed to load filter module: %s', filter_id) filter_items_by_id[filter_id] = [] continue if getattr(function_module, 'toggle', None): diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py index 6a9d897752..265067390a 100644 --- a/backend/open_webui/utils/oauth.py +++ b/backend/open_webui/utils/oauth.py @@ -608,7 +608,9 @@ async def get_oauth_client_info_with_dynamic_client_registration( } ) log.info( - f'Dynamic client registration successful at {registration_url}, client_id: {oauth_client_info.client_id}' + 'Dynamic client registration successful at %s, client_id: %s', + registration_url, + oauth_client_info.client_id, ) return oauth_client_info except Exception as e: @@ -701,7 +703,7 @@ async def get_oauth_client_info_with_static_credentials( ) log.info( - f'Static OAuth client info built for {oauth_client_id} using metadata from {oauth_server_metadata_url}' + 'Static OAuth client info built for %s using metadata from %s', oauth_client_id, oauth_server_metadata_url ) return oauth_client_info except Exception as e: @@ -807,7 +809,7 @@ async def recover_static_oauth_client_metadata(connection: dict, oauth_client_in recovered = {**oauth_client_info} if not recovered.get('scope') and resource_metadata.scopes_supported: recovered['scope'] = ' '.join(resource_metadata.scopes_supported) - log.info(f'Recovered static OAuth scopes for {server_url} from protected resource metadata') + log.info('Recovered static OAuth scopes for %s from protected resource metadata', server_url) if not recovered.get('resource') and resource_metadata.resource: recovered['resource'] = resource_metadata.resource @@ -916,7 +918,7 @@ class OAuthClientManager: def remove_client(self, client_id): if client_id in self.clients: del self.clients[client_id] - log.info(f'Removed OAuth client {client_id}') + log.info('Removed OAuth client %s', client_id) if hasattr(self.oauth, '_clients'): if client_id in self.oauth._clients: @@ -1074,7 +1076,7 @@ class OAuthClientManager: if refreshed_token: # Update the session with new token data session = await OAuthSessions.update_session_by_id(session.id, refreshed_token) - log.info(f'Successfully refreshed token for session {session.id}') + log.info('Successfully refreshed token for session %s', session.id) return session.token else: log.error(f'Failed to refresh token for session {session.id}') @@ -1242,7 +1244,7 @@ class OAuthClientManager: provider=client_id, token=token, ) - log.info(f'Stored OAuth session server-side for user {user_id}, client_id {client_id}') + log.info('Stored OAuth session server-side for user %s, client_id %s', user_id, client_id) except Exception as e: error_message = 'Failed to store OAuth session server-side' log.error(f'Failed to store OAuth session server-side: {e}') @@ -1369,7 +1371,7 @@ class OAuthManager: if refreshed_token: # Update the session with new token data session = await OAuthSessions.update_session_by_id(session.id, refreshed_token) - log.info(f'Successfully refreshed token for session {session.id}') + log.info('Successfully refreshed token for session %s', session.id) return session.token else: log.error(f'Failed to refresh token for session {session.id}') @@ -1594,7 +1596,7 @@ class OAuthManager: for group_name in user_oauth_groups: if group_name not in all_group_names: - log.info(f"Group '{group_name}' not found via OAuth claim. Creating group...") + log.info("Group '%s' not found via OAuth claim. Creating group...", group_name) try: new_group_form = GroupForm( name=group_name, @@ -1606,7 +1608,10 @@ class OAuthManager: created_group = await Groups.insert_new_group(creator_id, new_group_form, db=db) if created_group: log.info( - f"Successfully created group '{group_name}' with ID {created_group.id} using creator ID {creator_id}" + "Successfully created group '%s' with ID %s using creator ID %s", + group_name, + created_group.id, + creator_id, ) groups_created = True # Add to local set to prevent duplicate creation attempts in this run @@ -2096,7 +2101,7 @@ class OAuthManager: **({'max_age': cookie_max_age} if cookie_max_age is not None else {}), ) - log.info(f'Stored OAuth session server-side for user {user.id}, provider {provider}') + log.info('Stored OAuth session server-side for user %s, provider %s', user.id, provider) else: log.warning(f'Failed to create OAuth session for user {user.id}, provider {provider}') except Exception as e: @@ -2280,11 +2285,14 @@ class OAuthManager: revoked_count += 1 log.info( - f'Back-channel logout: revoked sessions for user {user.id} ' - f'(email={user.email}, provider={matched_provider}, sessions_deleted={len(sessions)})' + 'Back-channel logout: revoked sessions for user %s (email=%s, provider=%s, sessions_deleted=%s)', + user.id, + user.email, + matched_provider, + len(sessions), ) log.info( - f'Back-channel logout: completed for {len(users_to_logout)} user(s), {revoked_count} revocation(s) set' + 'Back-channel logout: completed for %s user(s), %s revocation(s) set', len(users_to_logout), revoked_count ) return JSONResponse(status_code=200, content={}) diff --git a/backend/open_webui/utils/plugin.py b/backend/open_webui/utils/plugin.py index d6d5ff3388..1c6be3858f 100644 --- a/backend/open_webui/utils/plugin.py +++ b/backend/open_webui/utils/plugin.py @@ -241,7 +241,7 @@ async def load_tool_module_by_id(tool_id, content=None): exec(content, module.__dict__) if frontmatter is None: frontmatter = extract_frontmatter(content) - log.info(f'Loaded module: {module.__name__}') + log.info('Loaded module: %s', module.__name__) # Create and return the object if the class 'Tools' is found in the module if hasattr(module, 'Tools'): @@ -291,7 +291,7 @@ async def load_function_module_by_id(function_id: str, content: str | None = Non exec(content, module.__dict__) if frontmatter is None: frontmatter = extract_frontmatter(content) - log.info(f'Loaded module: {module.__name__}') + log.info('Loaded module: %s', module.__name__) # Create appropriate object based on available class type in the module if hasattr(module, 'Pipe'): @@ -437,7 +437,7 @@ def install_frontmatter_requirements(requirements: str): if not new_reqs: return - log.info(f'Installing requirements: {" ".join(new_reqs)}') + log.info('Installing requirements: %s', ' '.join(new_reqs)) subprocess.check_call( [sys.executable, '-m', 'pip', 'install'] + PIP_OPTIONS + new_reqs + PIP_PACKAGE_INDEX_OPTIONS )