CamiloVega commited on
Commit
4cfecb3
·
verified ·
1 Parent(s): d01ead7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +170 -61
app.py CHANGED
@@ -1,141 +1,250 @@
1
  import gradio as gr
2
  import logging
3
  import torch
4
- from transformers import pipeline, AutoTokenizer
 
5
  import whisper
 
6
  from pydub import AudioSegment
7
  import requests
8
  from bs4 import BeautifulSoup
9
- from typing import Optional
 
10
 
11
- # Configuración básica de logging
12
  logging.basicConfig(
13
  level=logging.INFO,
14
  format='%(asctime)s - %(levelname)s - %(message)s'
15
  )
16
  logger = logging.getLogger(__name__)
17
 
 
 
 
 
18
  class NewsGenerator:
19
  def __init__(self):
20
  self.device = "cuda" if torch.cuda.is_available() else "cpu"
21
  self.whisper_model = None
22
- self.news_pipeline = None
23
  self.tokenizer = None
24
 
25
- # Carga diferida de modelos
26
  self._load_models()
27
 
28
  def _load_models(self):
29
- """Carga eficiente de modelos con gestión de memoria"""
30
  try:
31
- # Modelo de texto más pequeño y eficiente
32
- model_name = "facebook/bart-large-cnn"
33
- self.tokenizer = AutoTokenizer.from_pretrained(model_name)
34
- self.news_pipeline = pipeline(
35
- "summarization",
36
- model=model_name,
37
- device=self.device,
38
- torch_dtype=torch.float16 if self.device == "cuda" else torch.float32
 
 
 
 
 
 
 
39
  )
40
 
41
- # Whisper optimizado
42
  self.whisper_model = whisper.load_model(
43
- "tiny.en" if self.device == "cpu" else "small",
44
  device=self.device
45
  )
46
 
47
  except Exception as e:
48
- logger.error(f"Error loading models: {str(e)}")
49
  raise
50
 
51
  def transcribe_audio(self, audio_path: str) -> str:
52
- """Transcripción optimizada de audio"""
53
  try:
54
  result = self.whisper_model.transcribe(audio_path)
55
  return result.get("text", "")
56
  except Exception as e:
57
- logger.error(f"Transcription error: {str(e)}")
58
  return ""
59
 
60
- def generate_news(self, inputs: str, max_length: int = 200) -> str:
61
- """Generación de noticias con control de recursos"""
62
  try:
63
- return self.news_pipeline(
64
- inputs,
65
- max_length=max_length,
66
- min_length=30,
67
- do_sample=False, # Mejor rendimiento
68
- truncation=True
69
- )[0]['summary_text']
 
 
 
 
 
 
 
 
 
70
  except Exception as e:
71
- logger.error(f"Generation error: {str(e)}")
72
- return "Error generating content"
73
 
74
  def read_document(file_path: str) -> str:
75
  """Lectura optimizada de documentos"""
76
  try:
77
  if file_path.endswith(".pdf"):
78
- import fitz
79
  with fitz.open(file_path) as doc:
80
  return " ".join(page.get_text() for page in doc)
81
  elif file_path.endswith(".docx"):
82
  from docx import Document
83
  return " ".join(p.text for p in Document(file_path).paragraphs)
84
- elif file_path.endswith((".xlsx", ".csv")):
 
 
 
85
  import pandas as pd
86
- return pd.read_excel(file_path).to_string() if file_path.endswith(".xlsx") else pd.read_csv(file_path).to_string()
 
 
 
87
  return ""
 
 
 
 
 
 
 
88
  except Exception as e:
89
- logger.error(f"Document error: {str(e)}")
90
  return ""
91
 
 
 
 
 
 
 
 
 
 
92
  def create_interface():
93
- """Interfaz optimizada con Gradio"""
94
  generator = NewsGenerator()
95
 
96
- with gr.Blocks(title="Generador de Noticias Eficiente") as app:
97
- gr.Markdown("## 📰 Generador de Noticias Optimizado")
98
 
99
  with gr.Row():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  with gr.Column(scale=2):
101
- inputs = gr.Textbox(label="Entrada Principal", lines=5)
102
- max_length = gr.Slider(100, 500, value=200, label="Longitud Máxima")
 
 
 
103
  generate_btn = gr.Button("Generar Noticia", variant="primary")
104
-
105
- with gr.Column(scale=1):
106
- doc_upload = gr.File(label="Subir Documento", file_types=[".pdf", ".docx", ".xlsx", ".csv"])
107
- audio_upload = gr.File(label="Subir Audio", file_types=["audio", "video"])
108
-
109
- output = gr.Textbox(label="Noticia Generada", lines=10, interactive=False)
110
-
111
- def process_inputs(
112
  main_input: str,
 
113
  document: Optional[str],
114
  audio: Optional[str],
115
- max_len: int
 
 
 
116
  ):
117
  try:
118
- # Procesar documentos y audio
119
  doc_content = read_document(document) if document else ""
120
  audio_content = generator.transcribe_audio(audio) if audio else ""
 
 
 
 
 
 
 
 
 
121
 
122
- # Construir prompt
123
- full_input = "\n".join([
124
- main_input,
125
- f"Documento: {doc_content}",
126
- f"Audio: {audio_content}"
127
- ])
128
 
129
- return generator.generate_news(full_input, max_len)
 
 
 
 
 
 
 
130
 
131
  except Exception as e:
132
- logger.error(f"Processing error: {str(e)}")
133
- return f"Error: {str(e)}"
134
 
135
  generate_btn.click(
136
- fn=process_inputs,
137
- inputs=[inputs, doc_upload, audio_upload, max_length],
138
- outputs=output
 
 
 
 
 
 
 
 
 
139
  )
140
 
141
  return app
 
1
  import gradio as gr
2
  import logging
3
  import torch
4
+ import numpy as np
5
+ from transformers import AutoTokenizer, AutoModelForCausalLM
6
  import whisper
7
+ from huggingface_hub import login
8
  from pydub import AudioSegment
9
  import requests
10
  from bs4 import BeautifulSoup
11
+ from typing import Optional, Dict, Any
12
+ import fitz # PyMuPDF
13
 
14
+ # Configuración de logging
15
  logging.basicConfig(
16
  level=logging.INFO,
17
  format='%(asctime)s - %(levelname)s - %(message)s'
18
  )
19
  logger = logging.getLogger(__name__)
20
 
21
+ # Autenticación Hugging Face (reemplaza con tu token)
22
+ HF_TOKEN = "hf_tu_token_aqui"
23
+ login(token=HF_TOKEN)
24
+
25
  class NewsGenerator:
26
  def __init__(self):
27
  self.device = "cuda" if torch.cuda.is_available() else "cpu"
28
  self.whisper_model = None
29
+ self.llm_model = None
30
  self.tokenizer = None
31
 
 
32
  self._load_models()
33
 
34
  def _load_models(self):
35
+ """Carga optimizada de modelos con quantización 4-bit"""
36
  try:
37
+ # Modelo Llama-2 7B Chat
38
+ model_name = "meta-llama/Llama-2-7b-chat-hf"
39
+ self.tokenizer = AutoTokenizer.from_pretrained(
40
+ model_name,
41
+ use_fast=True,
42
+ token=HF_TOKEN
43
+ )
44
+
45
+ self.llm_model = AutoModelForCausalLM.from_pretrained(
46
+ model_name,
47
+ device_map="auto",
48
+ torch_dtype=torch.float16,
49
+ load_in_4bit=True,
50
+ low_cpu_mem_usage=True,
51
+ token=HF_TOKEN
52
  )
53
 
54
+ # Configuración de Whisper
55
  self.whisper_model = whisper.load_model(
56
+ "small.en" if self.device == "cpu" else "medium",
57
  device=self.device
58
  )
59
 
60
  except Exception as e:
61
+ logger.error(f"Error cargando modelos: {str(e)}")
62
  raise
63
 
64
  def transcribe_audio(self, audio_path: str) -> str:
65
+ """Transcripción de audio con manejo de errores"""
66
  try:
67
  result = self.whisper_model.transcribe(audio_path)
68
  return result.get("text", "")
69
  except Exception as e:
70
+ logger.error(f"Error en transcripción: {str(e)}")
71
  return ""
72
 
73
+ def generate_news(self, prompt: str, max_length: int = 512) -> str:
74
+ """Generación de noticias con Llama-2"""
75
  try:
76
+ inputs = self.tokenizer(
77
+ f"[INST]<<SYS>>Eres un periodista profesional. Genera una noticia bien estructurada basada en los siguientes datos:<</SYS>>\n{prompt}[/INST]",
78
+ return_tensors="pt"
79
+ ).to(self.device)
80
+
81
+ outputs = self.llm_model.generate(
82
+ **inputs,
83
+ max_new_tokens=max_length,
84
+ temperature=0.7,
85
+ top_p=0.9,
86
+ do_sample=True,
87
+ pad_token_id=self.tokenizer.eos_token_id
88
+ )
89
+
90
+ return self.tokenizer.decode(outputs[0], skip_special_tokens=True)
91
+
92
  except Exception as e:
93
+ logger.error(f"Error generando noticia: {str(e)}")
94
+ return "Error en generación"
95
 
96
  def read_document(file_path: str) -> str:
97
  """Lectura optimizada de documentos"""
98
  try:
99
  if file_path.endswith(".pdf"):
 
100
  with fitz.open(file_path) as doc:
101
  return " ".join(page.get_text() for page in doc)
102
  elif file_path.endswith(".docx"):
103
  from docx import Document
104
  return " ".join(p.text for p in Document(file_path).paragraphs)
105
+ elif file_path.endswith(".xlsx"):
106
+ import pandas as pd
107
+ return pd.read_excel(file_path).to_string()
108
+ elif file_path.endswith(".csv"):
109
  import pandas as pd
110
+ return pd.read_csv(file_path).to_string()
111
+ return ""
112
+ except Exception as e:
113
+ logger.error(f"Error leyendo documento: {str(e)}")
114
  return ""
115
+
116
+ def read_url(url: str) -> str:
117
+ """Extracción de contenido web"""
118
+ try:
119
+ response = requests.get(url, timeout=15)
120
+ response.raise_for_status()
121
+ return BeautifulSoup(response.content, 'html.parser').get_text(separator=' ', strip=True)
122
  except Exception as e:
123
+ logger.error(f"Error leyendo URL: {str(e)}")
124
  return ""
125
 
126
+ def process_social_media(url: str) -> Dict[str, Any]:
127
+ """Procesamiento de contenido social"""
128
+ try:
129
+ text = read_url(url)
130
+ return {"text": text, "video": None}
131
+ except Exception as e:
132
+ logger.error(f"Error procesando red social: {str(e)}")
133
+ return {"text": "", "video": None}
134
+
135
  def create_interface():
136
+ """Interfaz de usuario con Gradio"""
137
  generator = NewsGenerator()
138
 
139
+ with gr.Blocks(title="Generador de Noticias AI", theme=gr.themes.Soft()) as app:
140
+ gr.Markdown("# 📰 Generador de Noticias Profesional")
141
 
142
  with gr.Row():
143
+ with gr.Column(scale=3):
144
+ main_input = gr.Textbox(
145
+ label="Tema principal",
146
+ placeholder="Ingrese el tema o instrucciones principales...",
147
+ lines=3
148
+ )
149
+ additional_data = gr.Textbox(
150
+ label="Datos adicionales",
151
+ placeholder="Hechos clave, nombres, fechas, etc...",
152
+ lines=3
153
+ )
154
+
155
+ with gr.Accordion("Fuentes adicionales", open=False):
156
+ doc_upload = gr.File(
157
+ label="Subir documento",
158
+ file_types=[".pdf", ".docx", ".xlsx", ".csv"]
159
+ )
160
+ audio_upload = gr.File(
161
+ label="Subir audio/video",
162
+ file_types=["audio", "video"]
163
+ )
164
+ url_input = gr.Textbox(
165
+ label="URL de referencia",
166
+ placeholder="https://..."
167
+ )
168
+ social_input = gr.Textbox(
169
+ label="URL de red social",
170
+ placeholder="https://..."
171
+ )
172
+
173
+ length_slider = gr.Slider(
174
+ 100, 1000, value=400,
175
+ label="Longitud de la noticia (palabras)"
176
+ )
177
+ tone_select = gr.Dropdown(
178
+ label="Tono periodístico",
179
+ choices=["Formal", "Neutral", "Investigativo", "Narrativo"],
180
+ value="Neutral"
181
+ )
182
+
183
  with gr.Column(scale=2):
184
+ output_news = gr.Textbox(
185
+ label="Noticia generada",
186
+ lines=18,
187
+ interactive=False
188
+ )
189
  generate_btn = gr.Button("Generar Noticia", variant="primary")
190
+ status = gr.Textbox(label="Estado", interactive=False)
191
+
192
+ def process_and_generate(
 
 
 
 
 
193
  main_input: str,
194
+ additional_data: str,
195
  document: Optional[str],
196
  audio: Optional[str],
197
+ url: Optional[str],
198
+ social_url: Optional[str],
199
+ length: int,
200
+ tone: str
201
  ):
202
  try:
203
+ # Procesar fuentes adicionales
204
  doc_content = read_document(document) if document else ""
205
  audio_content = generator.transcribe_audio(audio) if audio else ""
206
+ url_content = read_url(url) if url else ""
207
+ social_content = process_social_media(social_url) if social_url else {"text": ""}
208
+
209
+ # Construir prompt estructurado
210
+ prompt = f"""
211
+ ## Instrucciones:
212
+ - Tema principal: {main_input}
213
+ - Datos proporcionados: {additional_data}
214
+ - Tono requerido: {tone}
215
 
216
+ ## Fuentes:
217
+ - Documento: {doc_content[:1000]}...
218
+ - Audio: {audio_content[:500]}...
219
+ - URL: {url_content[:1000]}...
220
+ - Red social: {social_content['text'][:500]}...
 
221
 
222
+ ## Requisitos:
223
+ - Estructura profesional (titular, lead, cuerpo)
224
+ - Incluir las 5W
225
+ - Citas relevantes si aplica
226
+ - Longitud: {length} palabras
227
+ """
228
+
229
+ return generator.generate_news(prompt, length), "✅ Generación exitosa"
230
 
231
  except Exception as e:
232
+ logger.error(str(e))
233
+ return f"Error: {str(e)}", "❌ Error en generación"
234
 
235
  generate_btn.click(
236
+ fn=process_and_generate,
237
+ inputs=[
238
+ main_input,
239
+ additional_data,
240
+ doc_upload,
241
+ audio_upload,
242
+ url_input,
243
+ social_input,
244
+ length_slider,
245
+ tone_select
246
+ ],
247
+ outputs=[output_news, status]
248
  )
249
 
250
  return app