diff --git a/effi-kreuzberg/TOOL-OPENWEBUI.md b/effi-kreuzberg/TOOL-OPENWEBUI.md index c4a04c5..d5fbcf7 100644 --- a/effi-kreuzberg/TOOL-OPENWEBUI.md +++ b/effi-kreuzberg/TOOL-OPENWEBUI.md @@ -1,10 +1,10 @@ -# Kreuzberg OCR Tool - Enhanced Version +# Kreuzberg OCR Tool ## Installation 1. **Admin Panel** → **Tools** → **Create Tool** 2. **Name**: `Kreuzberg OCR` -3. **Description**: `Extract text from documents using Kreuzberg OCR (PaddleOCR backend). Single: extract_file("file") or Multiple: extract_files("file1,file2")` +3. **Description**: `Extract text from documents using Kreuzberg OCR. Usage: extract_file("filename") or extract_files("file1,file2")` 4. **Coller le code ci-dessous** 5. **Save and Activate** @@ -16,7 +16,7 @@ import glob import requests import io import json -from PIL import Image, ImageEnhance +import mimetypes class Tools: @@ -25,16 +25,6 @@ class Tools: self.uploads_dir = "/app/backend/data/uploads" self.host_uploads_dir = "/home/effi/effi/data/uploads" - def _preprocess_image(self, image): - if image.mode in ('RGBA', 'LA', 'P'): - image = image.convert('RGBA').convert('RGB') - elif image.mode != 'RGB': - image = image.convert('RGB') - image = image.convert('L') - enhancer = ImageEnhance.Contrast(image) - image = enhancer.enhance(2.0) - return image - def extract_file(self, filename): return self._extract_single_file(filename) @@ -76,27 +66,16 @@ class Tools: file_path = self._find_file(filename) if not file_path: - return "[ERROR] File not found: " + filename + ". Use list_recent_files() to see available files." + available = self._list_files() + return "[ERROR] File not found: " + filename + "\n\nAvailable files:\n" + available - mime_type = self._get_mime_type(file_path) - ext = os.path.splitext(file_path)[1].lower() - is_image = ext in ('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.tif', '.webp') + mime_type, _ = mimetypes.guess_type(file_path) + mime_type = mime_type or 'application/octet-stream' - if is_image: - with open(file_path, 'rb') as f: - img = Image.open(f) - img = self._preprocess_image(img) - img_bytes = io.BytesIO() - img.save(img_bytes, format='PNG') - img_bytes.seek(0) + with open(file_path, 'rb') as f: files = { - 'files': (os.path.basename(file_path) + '.png', img_bytes, 'image/png') + 'files': (os.path.basename(file_path), f, mime_type) } - else: - with open(file_path, 'rb') as f: - files = { - 'files': (os.path.basename(file_path), f, mime_type) - } data = { 'image_export_mode': 'inline', @@ -119,11 +98,11 @@ class Tools: content = self._clean_content(content) if len(content) > 15000: - content = content[:15000] + "\n\n... (document truncated)" + content = content[:15000] + "\n\n... (truncated)" return "FILE: " + os.path.basename(file_path) + "\n\n" + content else: - return "[WARNING] No text extracted from " + os.path.basename(file_path) + ". File may be empty or contain only images." + return "[WARNING] No text extracted from " + os.path.basename(file_path) return "[WARNING] Unexpected response from Kreuzberg" @@ -134,46 +113,23 @@ class Tools: return "[ERROR] Kreuzberg error (" + str(response.status_code) + ")" except requests.Timeout: - return "[ERROR] Timeout - file is too large or Kreuzberg is overloaded" - + return "[ERROR] Timeout - file is too large" except requests.ConnectionError: return "[ERROR] Cannot connect to Kreuzberg at " + self.kreuzberg_url - except Exception as e: return "[ERROR] " + str(e) def list_recent_files(self, count=10): - try: - files = glob.glob(os.path.join(self.uploads_dir, "*")) - - if not files and os.path.exists(self.host_uploads_dir): - files = glob.glob(os.path.join(self.host_uploads_dir, "*")) - - files = [f for f in files if os.path.isfile(f)] - files = sorted(files, key=os.path.getmtime, reverse=True)[:count] - - if not files: - return "[INFO] No files found in uploads directory." - - result = "[INFO] " + str(len(files)) + " recent files:\n\n" - for f in files: - name = os.path.basename(f) - size = os.path.getsize(f) - size_str = self._format_size(size) - result += "- " + name + " (" + size_str + ")\n" - - result += "\nUse extract_file(\"filename\") to OCR a file." - return result - - except Exception as e: - return "[ERROR] " + str(e) + available = self._list_files(count) + return available def search_files(self, pattern): try: - files = glob.glob(os.path.join(self.uploads_dir, "*" + pattern + "*")) + search_dirs = [d for d in [self.uploads_dir, self.host_uploads_dir] if os.path.exists(d)] - if not files and os.path.exists(self.host_uploads_dir): - files = glob.glob(os.path.join(self.host_uploads_dir, "*" + pattern + "*")) + files = [] + for directory in search_dirs: + files.extend(glob.glob(os.path.join(directory, "*" + pattern + "*"))) files = [f for f in files if os.path.isfile(f)] @@ -191,64 +147,60 @@ class Tools: except Exception as e: return "[ERROR] " + str(e) + def _list_files(self, count=10): + try: + files = [] + search_dirs = [d for d in [self.uploads_dir, self.host_uploads_dir] if os.path.exists(d)] + + for directory in search_dirs: + files.extend(glob.glob(os.path.join(directory, "*"))) + + files = [f for f in files if os.path.isfile(f)] + files.sort(key=os.path.getmtime, reverse=True) + + if not files: + return "[INFO] No files found in uploads directory." + + result = "[INFO] " + str(len(files)) + " recent files:\n\n" + for f in files[:count]: + name = os.path.basename(f) + size = os.path.getsize(f) + size_str = self._format_size(size) + result += "- " + name + " (" + size_str + ")\n" + + result += "\nUse extract_file(\"filename\") to OCR a file." + return result + + except Exception as e: + return "[ERROR] " + str(e) + def _find_file(self, filename): filename = filename.strip().strip('"').strip("'") if os.path.exists(filename): return filename - path = os.path.join(self.uploads_dir, filename) - if os.path.exists(path): - return path + search_dirs = [d for d in [self.uploads_dir, self.host_uploads_dir] if os.path.exists(d)] - path = os.path.join(self.host_uploads_dir, filename) - if os.path.exists(path): - return path + for directory in search_dirs: + path = os.path.join(directory, filename) + if os.path.exists(path): + return path - for directory in [self.uploads_dir, self.host_uploads_dir]: - if not os.path.exists(directory): - continue - - pattern = os.path.join(directory, filename + "*") - matches = glob.glob(pattern) - if matches: - return matches[0] - - pattern = os.path.join(directory, "*" + filename + "*") - matches = glob.glob(pattern) - if matches: - for m in matches: - if filename in os.path.basename(m): - return m - return matches[0] + for directory in search_dirs: + all_files = glob.glob(os.path.join(directory, "*")) + for f in all_files: + basename = os.path.basename(f).lower() + search_name = filename.lower() + if search_name in basename or basename.startswith(search_name): + return f + parts = search_name.replace('.jpeg', '').replace('.jpg', '').replace('.png', '').split() + for part in parts: + if len(part) > 3 and part in basename: + return f return None - def _get_mime_type(self, file_path): - ext = os.path.splitext(file_path)[1].lower() - mime_types = { - '.pdf': 'application/pdf', - '.jpg': 'image/jpeg', - '.jpeg': 'image/jpeg', - '.png': 'image/png', - '.gif': 'image/gif', - '.tiff': 'image/tiff', - '.tif': 'image/tiff', - '.bmp': 'image/bmp', - '.webp': 'image/webp', - '.doc': 'application/msword', - '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - '.xls': 'application/vnd.ms-excel', - '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - '.ppt': 'application/vnd.ms-powerpoint', - '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - '.epub': 'application/epub+zip', - '.txt': 'text/plain', - '.html': 'text/html', - '.htm': 'text/html', - } - return mime_types.get(ext, 'application/octet-stream') - def _clean_content(self, content): if not content: return "" @@ -267,23 +219,15 @@ class Tools: cleaned_lines.append(stripped) prev_empty = False - result = '\n'.join(cleaned_lines) - - if len(result) > 500: - short_words = sum(1 for w in result.split() if len(w) <= 3) - total_words = len(result.split()) - if total_words > 50 and short_words / total_words > 0.3: - result += "\n\n[WARNING] This text may contain OCR errors." - - return result + return '\n'.join(cleaned_lines) def _format_size(self, size): if size < 1024: return str(size) + " B" elif size < 1024 * 1024: - return str(size / 1024) + " KB" + return str(int(size / 1024)) + " KB" else: - return str(size / (1024 * 1024)) + " MB" + return str(int(size / (1024 * 1024))) + " MB" def extract_file(filename): @@ -305,6 +249,20 @@ def search_files(pattern): ## Usage Examples +``` +extract_file("document.pdf") +extract_file("WhatsApp Image 2026-04-05 at 10.20.56.jpeg") +extract_files("page1.jpg,page2.jpg,page3.jpg") +list_recent_files() +search_files("dnd") +``` + +--- + +*Updated 11 April 2026 - Improved file search, no preprocessing (Kreuzberg handles it)* + +## Usage Examples + After installing and activating the tool: ### Extract text from a single file: