Spaces:
Runtime error
Runtime error
File size: 5,456 Bytes
2f1c164 a8584b7 2f1c164 a8584b7 2f1c164 a8584b7 2f1c164 a8584b7 6de23e9 2f1c164 6de23e9 2f1c164 6de23e9 2f1c164 6de23e9 2f1c164 6de23e9 2f1c164 6de23e9 2f1c164 6de23e9 2f1c164 6de23e9 a8584b7 2f1c164 6de23e9 2f1c164 6de23e9 2f1c164 a8584b7 6de23e9 2f1c164 a8584b7 2f1c164 a8584b7 2f1c164 6de23e9 2f1c164 a8584b7 2f1c164 a8584b7 2f1c164 a8584b7 2f1c164 a8584b7 6de23e9 2f1c164 a8584b7 2f1c164 a8584b7 2f1c164 6de23e9 a8584b7 2f1c164 a8584b7 6de23e9 2f1c164 a8584b7 2f1c164 a8584b7 6de23e9 a8584b7 2f1c164 6de23e9 2f1c164 6de23e9 2f1c164 a8584b7 2f1c164 a8584b7 2f1c164 6de23e9 a8584b7 2f1c164 a8584b7 6de23e9 2f1c164 6de23e9 3c429f1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | import os
import gradio as gr
import base64
import io
import re
from PIL import Image
import fitz # PyMuPDF
# --- CONFIGURATION ---
# Récupère la clé HF depuis les secrets du Space (sécurisé)
HF_TOKEN = os.getenv("HF_TOKEN")
if not HF_TOKEN:
raise ValueError(
"❌ Token HF manquant. Ajoutez HF_TOKEN dans les 'Repository Secrets' de votre Space."
)
MODEL_ID = "google/gemma-4-E4B-it"
API_URL = f"https://api-inference.huggingface.co/models/{MODEL_ID}/v1/chat/completions"
# --- FONCTIONS AUXILIAIRES ---
def encode_image(image: Image.Image) -> str:
"""Convertit une image PIL en base64 JPEG."""
buffer = io.BytesIO()
image.save(buffer, format="JPEG", quality=85)
return base64.b64encode(buffer.getvalue()).decode()
def pdf_to_image(pdf_path: str) -> Image.Image:
"""Convertit la première page d'un PDF en image PIL."""
doc = fitz.open(pdf_path)
page = doc.load_page(0)
mat = fitz.Matrix(150 / 72, 150 / 72) # 150 DPI — lisible et léger
pix = page.get_pixmap(matrix=mat)
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
doc.close()
return img
def sanitize_filename(name: str) -> str:
"""Nettoie le nom retourné par le modèle pour en faire un nom de fichier valide."""
name = name.strip().strip('"').strip("'")
name = re.sub(r"[^\w\s\-]", "", name)
name = re.sub(r"\s+", "_", name)
return name[:80] if name else "document_sans_nom"
# --- FONCTION PRINCIPALE ---
def generate_filename(file, custom_prompt: str):
"""
Génère un nom de fichier à partir d'une image ou d'un PDF (première page).
Args:
file : Fichier uploadé (image ou PDF).
custom_prompt : Prompt/format personnalisé pour le nom.
Returns:
tuple: (aperçu PIL Image, nom généré str)
"""
# 1. Vérification du fichier
if file is None:
return None, "❌ Veuillez uploader un fichier (image ou PDF)."
try:
# 2. Traitement selon le type de fichier
if file.name.lower().endswith(".pdf"):
image = pdf_to_image(file.name)
else:
image = Image.open(file.name).convert("RGB")
# 3. Encodage base64
image_base64 = encode_image(image)
# 4. Prompt
if custom_prompt and custom_prompt.strip():
prompt = custom_prompt.strip()
else:
prompt = (
"Analyse ce document. Génère un nom de fichier court et descriptif "
"en snake_case (3 à 6 mots max).\n"
"Réponds UNIQUEMENT par le nom de fichier, sans extension, sans explication."
)
# 5. Appel à l'Inference API HF (même structure que ton ancien script Mistral)
import requests
response = requests.post(
API_URL,
headers={
"Authorization": f"Bearer {HF_TOKEN}",
"Content-Type": "application/json",
},
json={
"model": MODEL_ID,
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": prompt},
{
"type": "image_url",
"image_url": {
"url": f"data:image/jpeg;base64,{image_base64}"
},
},
],
}
],
"max_tokens": 60,
"temperature": 0.1,
},
timeout=60,
)
# 6. Traitement de la réponse
if response.status_code == 200:
raw = response.json()["choices"][0]["message"]["content"].strip()
clean = sanitize_filename(raw)
return image, f"{clean}.pdf"
else:
return image, f"❌ Erreur API HF : {response.status_code} — {response.text}"
except Exception as e:
return None, f"❌ Erreur : {str(e)}"
# --- INTERFACE GRADIO ---
with gr.Blocks(title="PDF Auto-Namer") as demo:
gr.Markdown("""
# 📄 PDF Auto-Namer
**Basé sur Gemma 4 E4B (multimodal)** via Hugging Face Inference API
Upload un **fichier image ou PDF** → la première page est analysée → un nom est généré.
""")
with gr.Row():
with gr.Column():
file_input = gr.File(
label="Document (Image ou PDF)",
file_types=[".jpg", ".jpeg", ".png", ".pdf"],
height=200,
)
prompt_input = gr.Textbox(
label="Format personnalisé (optionnel)",
placeholder='Ex: "Format: Contrat_[TYPE]_[CLIENT]_[JJMMAAAA]"',
lines=3,
)
btn = gr.Button("🚀 Générer le nom", variant="primary")
with gr.Column():
preview = gr.Image(label="👁️ Aperçu — Page 1", type="pil")
output = gr.Textbox(label="📝 Nom de fichier généré", lines=2)
btn.click(
fn=generate_filename,
inputs=[file_input, prompt_input],
outputs=[preview, output],
)
gr.Markdown(
"---\n"
"ℹ️ *Modèle : `google/gemma-4-E4B-it` · "
"La clé `HF_TOKEN` doit être définie dans les Secrets du Space.*"
)
demo.launch(show_error=True) |