Ajout Effi-OCR (Tesseract) + Web Scraper pour OpenWebUI

- Effi-OCR: Service OCR avec Tesseract (multilingue FR/EN)
  * API FastAPI sur port 8300
  * Prétraitement images (contraste, niveaux de gris)
  * Tool OpenWebUI intégré

- Web Scraper: Service Python (BeautifulSoup) sur port 11235
  * Alternative à Crawl4AI (incompatible ARM64/J5040)
  * Scraping HTML basique mais fonctionnel
  * Intégré au docker-compose d'effi-server

- Documentation:
  * ROADMAP-FONCTIONNALITES.md: Plan des 9 fonctionnalités
  * WEB-SCRAPER-CONFIG.md: Configuration OpenWebUI
  * WEB-SCRAPER-TOOL.md: Code du Tool

- Script start-all-services.sh mis à jour (6 services)
This commit is contained in:
Folzo
2026-04-06 16:34:27 +02:00
parent 5f1e19ff91
commit 93f31cdaeb
25 changed files with 2469 additions and 6 deletions
+111
View File
@@ -0,0 +1,111 @@
# Configuration Web Search and Crawl (OpenWebUI)
Guide de configuration du Tool **Web Search and Crawl** dans OpenWebUI avec Crawl4AI.
## Prérequis
- [ ] Crawl4AI installé et démarré sur effi-server (voir `install-on-effiserver.sh`)
- [ ] OpenWebUI accessible sur http://192.168.1.27:3000
## Étapes de configuration
### 1. Vérifier que Crawl4AI tourne
Sur **effi-server** :
```bash
curl http://localhost:11235/health
# Doit retourner : {"status":"healthy"}
```
### 2. Accéder au Tool dans OpenWebUI
1. Ouvrir http://192.168.1.27:3000
2. Se connecter en admin
3. Aller dans **Admin Panel** → **Tools**
4. Trouver **Web Search and Crawl**
5. Cliquer sur l'icône ⚙️ (Settings/Valves)
### 3. Configurer les Valves
| Valve | Valeur | Description |
|-------|--------|-------------|
| **Crawl4AI Base URL** | `http://localhost:11235` | URL du service Crawl4AI |
| **Crawl4AI API Token** | (vide) | Token si configuré dans docker-compose |
| **Timeout** | `30000` | Timeout en millisecondes (30s) |
| **Max Length** | `10000` | Nombre max de caractères extraits |
| **Enable JavaScript** | ✅ | Activer l'exécution JavaScript |
### 4. Activer le Tool
1. Cocher la case **Enable** sur le Tool
2. Cliquer sur **Save**
3. Le Tool est maintenant actif !
## Test de fonctionnement
Créer une nouvelle conversation et demander :
```
"Effi, cherche les dernières nouvelles sur Fedora 42 et résume-moi les articles"
```
L'agent devrait :
1. Utiliser la recherche web
2. Scraper les pages trouvées
3. Te présenter un résumé
## Troubleshooting
### "Crawl4AI not responding"
Vérifier sur effi-server :
```bash
docker ps | grep crawl4ai
docker logs effi-crawl4ai
```
### "Timeout error"
Augmenter la valve **Timeout** à `60000` (60 secondes) dans les settings du Tool.
### "Memory error"
La version CPU est limitée à 2GB. Pour les pages très lourdes :
- Augmenter dans `docker-compose.yml` : `memory: 3G`
- Redémarrer : `docker-compose up -d`
## Commandes utiles
**Redémarrer Crawl4AI** :
```bash
cd ~/effi-crawl4ai
docker-compose restart
```
**Voir les logs** :
```bash
docker logs -f effi-crawl4ai
```
**Mettre à jour l'image** :
```bash
cd ~/effi-crawl4ai
docker-compose pull
docker-compose up -d
```
## Fonctionnalités disponibles
Une fois configuré, tu peux demander à Effi :
| Requête | Description |
|---------|-------------|
| "Cherche X et résume" | Recherche + résumé automatique |
| "Extraire le contenu de [URL]" | Scraping direct d'une page |
| "Comparer [produit] sur plusieurs sites" | Multi-site scraping |
| "Vérifier les prix de [article]" | Surveillance de prix |
| "Trouver des documentations sur [sujet]" | Recherche technique |
---
*Configuration créée le 6 Avril 2026*
+188
View File
@@ -0,0 +1,188 @@
# Tool Web Scraping pour OpenWebUI
## Configuration
Dans OpenWebUI (http://192.168.1.27:3000) :
1. **Aller dans** : Admin → Tools → Create Tool
2. **Nom** : Web Scraper
3. **ID** : web_scraper
4. **Description** : Extrait le contenu texte de n'importe quelle URL web
## Code du Tool
```python
from typing import Optional
import requests
from urllib.parse import urlparse
import re
class Tools:
def __init__(self):
"""Initialize Web Scraper Tool"""
self.timeout = 15
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.0'
}
def scrape_url(self, url: str, max_chars: int = 5000) -> str:
"""
Scrape le contenu texte d'une URL web
Args:
url: L'URL à scraper (doit commencer par http:// ou https://)
max_chars: Nombre maximum de caractères à retourner (défaut: 5000)
Returns:
Le contenu texte extrait de la page
"""
try:
# Validation URL
if not url.startswith(('http://', 'https://')):
return "Erreur: L'URL doit commencer par http:// ou https://"
parsed = urlparse(url)
if not parsed.netloc:
return "Erreur: URL invalide"
# Télécharger la page
response = requests.get(url, headers=self.headers, timeout=self.timeout)
response.raise_for_status()
# Détecter l'encodage
if response.encoding == 'ISO-8859-1':
response.encoding = response.apparent_encoding
html = response.text
# Extraction simple du texte (sans BeautifulSoup pour garder le Tool léger)
# Supprimer les scripts et styles
text = re.sub(r'<script[^>]*>[^<]*</script>', '', html, flags=re.IGNORECASE | re.DOTALL)
text = re.sub(r'<style[^>]*>[^<]*</style>', '', text, flags=re.IGNORECASE | re.DOTALL)
# Supprimer les balises HTML
text = re.sub(r'<[^>]+>', ' ', text)
# Nettoyer le texte
text = re.sub(r'\s+', ' ', text) # Espaces multiples
text = text.strip()
# Décoder les entités HTML
text = text.replace('&amp;', '&')
text = text.replace('&lt;', '<')
text = text.replace('&gt;', '>')
text = text.replace('&quot;', '"')
text = text.replace('&#39;', "'")
text = text.replace('&nbsp;', ' ')
# Tronquer si nécessaire
if len(text) > max_chars:
text = text[:max_chars] + f"\n\n[...] {len(text) - max_chars} caractères supplémentaires"
return f"Contenu extrait de {url}:\n\n{text}"
except requests.exceptions.Timeout:
return f"Erreur: Timeout après {self.timeout}s pour {url}"
except requests.exceptions.ConnectionError:
return f"Erreur: Impossible de se connecter à {url}"
except requests.exceptions.HTTPError as e:
return f"Erreur HTTP {e.response.status_code}: {url}"
except Exception as e:
return f"Erreur lors du scraping: {str(e)}"
def scrape_article(self, url: str) -> str:
"""
Scrape un article en essayant d'extraire le contenu principal uniquement
Args:
url: L'URL de l'article
Returns:
Le contenu principal de l'article (titre + texte)
"""
try:
if not url.startswith(('http://', 'https://')):
return "Erreur: L'URL doit commencer par http:// ou https://"
response = requests.get(url, headers=self.headers, timeout=self.timeout)
response.raise_for_status()
if response.encoding == 'ISO-8859-1':
response.encoding = response.apparent_encoding
html = response.text
# Extraire le titre
title_match = re.search(r'<title[^>]*>([^<]*)</title>', html, re.IGNORECASE)
title = title_match.group(1).strip() if title_match else "Titre non trouvé"
# Essayer d'extraire le contenu article (heuristique simple)
# Chercher les balises article, main, ou div avec classe contenant "content"
article_patterns = [
r'<article[^>]*>(.*?)</article>',
r'<main[^>]*>(.*?)</main>',
r'<div[^>]*class=["\'][^"\']*(?:content|article|post)[^"\']*["\'][^>]*>(.*?)</div>',
]
content = None
for pattern in article_patterns:
match = re.search(pattern, html, re.IGNORECASE | re.DOTALL)
if match:
content = match.group(1)
break
# Si pas trouvé, prendre tout le body
if not content:
body_match = re.search(r'<body[^>]*>(.*?)</body>', html, re.IGNORECASE | re.DOTALL)
content = body_match.group(1) if body_match else html
# Nettoyer le contenu
content = re.sub(r'<script[^>]*>[^<]*</script>', '', content, flags=re.IGNORECASE | re.DOTALL)
content = re.sub(r'<style[^>]*>[^<]*</style>', '', content, flags=re.IGNORECASE | re.DOTALL)
content = re.sub(r'<[^>]+>', ' ', content)
content = re.sub(r'\s+', ' ', content).strip()
# Décoder les entités HTML
content = content.replace('&amp;', '&').replace('&lt;', '<').replace('&gt;', '>')
content = content.replace('&quot;', '"').replace('&#39;', "'")
# Limiter la taille
if len(content) > 8000:
content = content[:8000] + "\n\n[...] Contenu tronqué"
return f"Titre: {title}\nURL: {url}\n\n{content}"
except Exception as e:
return f"Erreur: {str(e)}"
```
## Utilisation
Une fois activé, les agents peuvent utiliser :
- `scrape_url("https://example.com/article")` - Extraire tout le texte d'une page
- `scrape_article("https://example.com/article")` - Extraire le contenu article uniquement
## Exemples de prompts
```
"Effi, résume-moi cet article : https://..."
→ L'agent utilise scrape_article automatiquement
"Qu'est-ce qui est écrit sur cette page ? https://..."
→ L'agent utilise scrape_url
"Cherche des infos sur X et lis les 3 premiers liens"
→ Web search + scrape_url pour chaque résultat
```
## Limitations
- Extraction basique (pas de JavaScript)
- Timeout 15 secondes
- Sites avec protection anti-bot peuvent bloquer
- Ne fonctionne pas avec les paywalls
---
*Tool créé le 6 Avril 2026*