import gradio as gr import os import time import requests from datetime import datetime from langchain_openai import ChatOpenAI from langchain_anthropic import ChatAnthropic from langchain_google_genai import ChatGoogleGenerativeAI from langchain_core.messages import HumanMessage import PyPDF2 import docx import pandas as pd from pptx import Presentation import io import tempfile from urllib.parse import urlparse import re # Configuration des modèles MODELS = { "Gemini 2.5 Flash (Google AI)": { "provider": "Google AI", "class": ChatGoogleGenerativeAI, "model_name": "gemini-2.0-flash-exp", "default_api": True }, "ChatGPT 5 (OpenAI)": { "provider": "OpenAI", "class": ChatOpenAI, "model_name": "gpt-4o", "default_api": False }, "Claude Sonnet 4 (Anthropic)": { "provider": "Anthropic", "class": ChatAnthropic, "model_name": "claude-3-5-sonnet-20241022", "default_api": False }, "Gemini 2.5 Pro (Google AI)": { "provider": "Google AI", "class": ChatGoogleGenerativeAI, "model_name": "gemini-2.0-flash-exp", "default_api": False } } # API par défaut pour Gemini 2.5 Flash DEFAULT_GEMINI_API = os.getenv("GOOGLE_API_KEY", "") def extract_text_from_file(file): """Extrait le texte d'un fichier uploadé""" if file is None: return "" file_extension = os.path.splitext(file.name)[1].lower() try: if file_extension == '.pdf': with open(file.name, 'rb') as f: reader = PyPDF2.PdfReader(f) text = "" for page in reader.pages: text += page.extract_text() + "\n" return text elif file_extension == '.docx': doc = docx.Document(file.name) text = "" for paragraph in doc.paragraphs: text += paragraph.text + "\n" return text elif file_extension == '.txt': with open(file.name, 'r', encoding='utf-8') as f: return f.read() elif file_extension in ['.xlsx', '.xls']: df = pd.read_excel(file.name) return df.to_string() elif file_extension == '.pptx': prs = Presentation(file.name) text = "" for slide in prs.slides: for shape in slide.shapes: if hasattr(shape, "text"): text += shape.text + "\n" return text else: return "Format de fichier non supporté" except Exception as e: return f"Erreur lors de la lecture du fichier: {str(e)}" def extract_text_from_url(url): """Extrait le texte d'une URL""" try: response = requests.get(url, timeout=10) response.raise_for_status() # Simple extraction du contenu textuel content = response.text # Suppression basique des balises HTML content = re.sub(r'<[^>]+>', '', content) content = re.sub(r'\s+', ' ', content).strip() return content[:10000] # Limite à 10k caractères except Exception as e: return f"Erreur lors de la récupération de l'URL: {str(e)}" def get_document_content(text_input, url_input, file_input): """Récupère le contenu du document selon la source""" if text_input.strip(): return text_input.strip() elif url_input.strip(): return extract_text_from_url(url_input.strip()) elif file_input is not None: return extract_text_from_file(file_input) else: return "" def create_llm_instance(model_name, api_key): """Crée une instance du modèle LLM""" model_config = MODELS[model_name] if model_config["provider"] == "OpenAI": return model_config["class"]( model=model_config["model_name"], api_key=api_key, temperature=0.7 ) elif model_config["provider"] == "Anthropic": return model_config["class"]( model=model_config["model_name"], api_key=api_key, temperature=0.7 ) elif model_config["provider"] == "Google AI": api_to_use = api_key if api_key else DEFAULT_GEMINI_API return model_config["class"]( model=model_config["model_name"], google_api_key=api_to_use, temperature=0.7 ) def generate_html(model_name, api_key, text_input, url_input, file_input): """Génère le fichier HTML éducatif""" start_time = time.time() # Validation des entrées if model_name != "Gemini 2.5 Flash (Google AI)" and not api_key.strip(): return None, "❌ Erreur: Veuillez fournir une clé API pour ce modèle.", 0 document_content = get_document_content(text_input, url_input, file_input) if not document_content: return None, "❌ Erreur: Veuillez fournir un document (texte, URL ou fichier).", 0 try: # Création de l'instance LLM llm = create_llm_instance(model_name, api_key) # Lecture du prompt template with open("creation_educational_html_from_any_document_18082025.txt", "r", encoding="utf-8") as f: prompt_template = f.read() # Remplacement des variables model_config = MODELS[model_name] prompt = prompt_template.format( model_name=model_config["model_name"], provider_name=model_config["provider"], document=document_content ) # Génération du contenu message = HumanMessage(content=prompt) response = llm.invoke([message]) html_content = response.content # Calcul du temps de génération generation_time = time.time() - start_time # Sauvegarde du fichier HTML timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"document_educatif_{timestamp}.html" with open(filename, "w", encoding="utf-8") as f: f.write(html_content) success_message = f"✅ Fichier HTML généré avec succès en {generation_time:.2f} secondes!" return filename, success_message, generation_time except Exception as e: error_message = f"❌ Erreur lors de la génération: {str(e)}" return None, error_message, 0 def reset_form(): """Remet à zéro le formulaire""" return ( "Gemini 2.5 Flash (Google AI)", # model_name "", # api_key "", # text_input "", # url_input None, # file_input "", # status_message None, # html_file "" # html_preview ) def update_api_info(model_name): """Met à jour les informations sur l'API selon le modèle sélectionné""" if model_name == "Gemini 2.5 Flash (Google AI)": return gr.update( label="Clé API (optionnelle)", placeholder="API gratuite disponible jusqu'à épuisement, ou utilisez votre propre clé", info="💡 Une API gratuite est déjà configurée pour ce modèle. Vous pouvez utiliser votre propre clé si vous le souhaitez." ) else: return gr.update( label="Clé API (obligatoire)", placeholder="Entrez votre clé API", info="🔑 Clé API requise pour ce modèle" ) # Interface Gradio with gr.Blocks( title="EduHTML Creator - Générateur de contenu éducatif HTML", theme=gr.themes.Soft(), css=""" .main-container { max-width: 1200px; margin: 0 auto; padding: 20px; } .header { text-align: center; margin-bottom: 30px; padding: 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; color: white; } .form-section { background: white; padding: 25px; border-radius: 15px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); margin-bottom: 20px; } .apple-button { background: #007AFF; color: white; border: none; border-radius: 8px; padding: 12px 24px; font-weight: 500; transition: all 0.3s ease; } .apple-button:hover { background: #0056CC; transform: translateY(-1px); } .reset-button { background: #FF3B30; color: white; border: none; border-radius: 8px; padding: 12px 24px; font-weight: 500; } .status-success { color: #34C759; font-weight: 500; } .status-error { color: #FF3B30; font-weight: 500; } """ ) as app: gr.HTML("""

🎓 EduHTML Creator

Transformez n'importe quel document en contenu éducatif HTML interactif

Cette application utilise l'intelligence artificielle pour créer des pages HTML éducatives élégantes et interactives à partir de vos documents. Le design s'inspire du style Apple pour une expérience utilisateur premium. L'objectif éducatif est de faciliter l'apprentissage grâce à la structuration, l'interactivité et la visualisation des informations clés de vos documents originaux.

""") with gr.Row(): with gr.Column(scale=1): gr.HTML("
") # Sélection du modèle model_dropdown = gr.Dropdown( choices=list(MODELS.keys()), value="Gemini 2.5 Flash (Google AI)", label="🤖 Modèle LLM", info="Choisissez le modèle d'IA à utiliser" ) # Champ API api_input = gr.Textbox( label="Clé API (optionnelle)", placeholder="API gratuite disponible jusqu'à épuisement, ou utilisez votre propre clé", info="💡 Une API gratuite est déjà configurée pour ce modèle. Vous pouvez utiliser votre propre clé si vous le souhaitez.", type="password" ) gr.HTML("
") gr.HTML("
") gr.HTML("

📄 Source du document

") # Entrées de document text_input = gr.Textbox( label="Texte copié/collé", placeholder="Collez votre texte ici...", lines=5 ) url_input = gr.Textbox( label="Lien Web", placeholder="https://exemple.com/article" ) file_input = gr.File( label="Fichier", file_types=[".pdf", ".txt", ".docx", ".xlsx", ".xls", ".pptx"] ) gr.HTML("
") # Boutons with gr.Row(): submit_btn = gr.Button( "🚀 Générer le HTML", variant="primary", elem_classes=["apple-button"] ) reset_btn = gr.Button( "🔄 Reset", elem_classes=["reset-button"] ) with gr.Column(scale=1): # Statut et résultats status_output = gr.HTML(label="Statut") # Fichier téléchargeable html_file_output = gr.File( label="📥 Fichier HTML téléchargeable", visible=False ) # Prévisualisation html_preview = gr.HTML( label="👀 Prévisualisation", visible=False ) # Événements model_dropdown.change( fn=update_api_info, inputs=[model_dropdown], outputs=[api_input] ) submit_btn.click( fn=generate_html, inputs=[model_dropdown, api_input, text_input, url_input, file_input], outputs=[html_file_output, status_output, gr.State()] ).then( fn=lambda file, status, time_taken: ( gr.update(visible=file is not None), status, gr.update(visible=file is not None, value=open(file, 'r', encoding='utf-8').read() if file else "") ), inputs=[html_file_output, status_output, gr.State()], outputs=[html_file_output, status_output, html_preview] ) reset_btn.click( fn=reset_form, outputs=[model_dropdown, api_input, text_input, url_input, file_input, status_output, html_file_output, html_preview] ) if __name__ == "__main__": app.launch( server_name="0.0.0.0", server_port=7860, share=True )