CamiloVega commited on
Commit
1b167bf
verified
1 Parent(s): e12f85e

Upload app (10).py

Browse files
Files changed (1) hide show
  1. app (10).py +456 -0
app (10).py ADDED
@@ -0,0 +1,456 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spaces
2
+ import gradio as gr
3
+ import logging
4
+ import os
5
+ import tempfile
6
+ import pandas as pd
7
+ import requests
8
+ from bs4 import BeautifulSoup
9
+ from transformers import pipeline, AutoModelForCausalLM, AutoTokenizer
10
+ import torch
11
+ import whisper
12
+ from moviepy.editor import VideoFileClip
13
+ from pydub import AudioSegment
14
+ import fitz # PyMuPDF for handling PDFs
15
+ import docx # For handling .docx files
16
+ import yt_dlp
17
+
18
+ # Configure logging
19
+ logging.basicConfig(
20
+ level=logging.INFO,
21
+ format='%(asctime)s - %(levelname)s - %(message)s'
22
+ )
23
+ logger = logging.getLogger(__name__)
24
+
25
+ # Get HuggingFace token from environment variable
26
+ HUGGINGFACE_TOKEN = os.environ.get('HUGGINGFACE_TOKEN')
27
+ if not HUGGINGFACE_TOKEN:
28
+ logger.error("HUGGINGFACE_TOKEN environment variable not set")
29
+ raise ValueError("Please set the HUGGINGFACE_TOKEN environment variable")
30
+
31
+ # Global variables for models
32
+ tokenizer = None
33
+ model = None
34
+ news_generator = None
35
+ whisper_model = None
36
+
37
+ @spaces.GPU(duration=60)
38
+ def initialize_models():
39
+ """Initialize models with Zero GPU optimizations"""
40
+ global tokenizer, model, news_generator, whisper_model
41
+
42
+ try:
43
+ logger.info("Starting model initialization...")
44
+ model_name = "meta-llama/Llama-2-7b-chat-hf"
45
+
46
+ # Load tokenizer
47
+ logger.info("Loading tokenizer...")
48
+ tokenizer = AutoTokenizer.from_pretrained(
49
+ model_name,
50
+ token=HUGGINGFACE_TOKEN
51
+ )
52
+ tokenizer.pad_token = tokenizer.eos_token
53
+
54
+ # Load model
55
+ logger.info("Loading model...")
56
+ model = AutoModelForCausalLM.from_pretrained(
57
+ model_name,
58
+ token=HUGGINGFACE_TOKEN,
59
+ torch_dtype=torch.bfloat16,
60
+ device_map="auto",
61
+ low_cpu_mem_usage=True
62
+ )
63
+
64
+ # Create pipeline
65
+ logger.info("Creating pipeline...")
66
+ news_generator = pipeline(
67
+ "text-generation",
68
+ model=model,
69
+ tokenizer=tokenizer,
70
+ device_map="auto",
71
+ torch_dtype=torch.bfloat16,
72
+ max_length=2048,
73
+ do_sample=True,
74
+ temperature=0.7,
75
+ top_p=0.95,
76
+ repetition_penalty=1.2
77
+ )
78
+
79
+ # Load Whisper model
80
+ logger.info("Loading Whisper model...")
81
+ whisper_model = whisper.load_model("base")
82
+
83
+ logger.info("All models initialized successfully")
84
+ return True
85
+ except Exception as e:
86
+ logger.error(f"Error during model initialization: {str(e)}")
87
+ raise
88
+
89
+ # Inicializar los modelos
90
+ initialize_models()
91
+
92
+ def download_social_media_video(url):
93
+ """Download a video from social media."""
94
+ ydl_opts = {
95
+ 'format': 'bestaudio/best',
96
+ 'postprocessors': [{
97
+ 'key': 'FFmpegExtractAudio',
98
+ 'preferredcodec': 'mp3',
99
+ 'preferredquality': '192',
100
+ }],
101
+ 'outtmpl': '%(id)s.%(ext)s',
102
+ }
103
+ try:
104
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
105
+ info_dict = ydl.extract_info(url, download=True)
106
+ audio_file = f"{info_dict['id']}.mp3"
107
+ logger.info(f"Video downloaded successfully: {audio_file}")
108
+ return audio_file
109
+ except Exception as e:
110
+ logger.error(f"Error downloading video: {str(e)}")
111
+ raise
112
+
113
+ def convert_video_to_audio(video_file):
114
+ """Convert a video file to audio."""
115
+ try:
116
+ video = VideoFileClip(video_file)
117
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_file:
118
+ video.audio.write_audiofile(temp_file.name)
119
+ logger.info(f"Video converted to audio: {temp_file.name}")
120
+ return temp_file.name
121
+ except Exception as e:
122
+ logger.error(f"Error converting video: {str(e)}")
123
+ raise
124
+
125
+ def preprocess_audio(audio_file):
126
+ """Preprocess the audio file to improve quality."""
127
+ try:
128
+ audio = AudioSegment.from_file(audio_file)
129
+ audio = audio.apply_gain(-audio.dBFS + (-20))
130
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_file:
131
+ audio.export(temp_file.name, format="mp3")
132
+ logger.info(f"Audio preprocessed: {temp_file.name}")
133
+ return temp_file.name
134
+ except Exception as e:
135
+ logger.error(f"Error preprocessing audio: {str(e)}")
136
+ raise
137
+
138
+ @spaces.GPU(duration=60)
139
+ def transcribe_audio(file):
140
+ """Transcribe an audio or video file."""
141
+ try:
142
+ if isinstance(file, str) and file.startswith('http'):
143
+ file_path = download_social_media_video(file)
144
+ elif isinstance(file, str) and file.lower().endswith(('.mp4', '.avi', '.mov', '.mkv')):
145
+ file_path = convert_video_to_audio(file)
146
+ else:
147
+ file_path = preprocess_audio(file)
148
+
149
+ logger.info(f"Transcribing audio: {file_path}")
150
+ with torch.inference_mode():
151
+ result = whisper_model.transcribe(file_path)
152
+ transcription = result.get("text", "Error in transcription")
153
+ logger.info(f"Transcription completed: {transcription[:50]}...")
154
+ return transcription
155
+ except Exception as e:
156
+ logger.error(f"Error transcribing: {str(e)}")
157
+ return f"Error processing the file: {str(e)}"
158
+
159
+ def read_document(document_path):
160
+ """Read the content of a document."""
161
+ try:
162
+ if document_path.endswith(".pdf"):
163
+ doc = fitz.open(document_path)
164
+ return "\n".join([page.get_text() for page in doc])
165
+ elif document_path.endswith(".docx"):
166
+ doc = docx.Document(document_path)
167
+ return "\n".join([paragraph.text for paragraph in doc.paragraphs])
168
+ elif document_path.endswith(".xlsx"):
169
+ return pd.read_excel(document_path).to_string()
170
+ elif document_path.endswith(".csv"):
171
+ return pd.read_csv(document_path).to_string()
172
+ else:
173
+ return "Unsupported file type. Please upload a PDF, DOCX, XLSX or CSV document."
174
+ except Exception as e:
175
+ return f"Error reading document: {str(e)}"
176
+
177
+ def read_url(url):
178
+ """Read the content of a URL."""
179
+ try:
180
+ response = requests.get(url)
181
+ response.raise_for_status()
182
+ soup = BeautifulSoup(response.content, 'html.parser')
183
+ return soup.get_text()
184
+ except Exception as e:
185
+ return f"Error reading URL: {str(e)}"
186
+
187
+ def process_social_content(url):
188
+ """Process social media content."""
189
+ try:
190
+ text_content = read_url(url)
191
+ try:
192
+ video_content = transcribe_audio(url)
193
+ except Exception:
194
+ video_content = None
195
+
196
+ return {
197
+ "text": text_content,
198
+ "video": video_content
199
+ }
200
+ except Exception as e:
201
+ logger.error(f"Error processing social content: {str(e)}")
202
+ return None
203
+
204
+ @spaces.GPU(duration=60)
205
+ def generate_news(instructions, facts, size, tone, *args):
206
+ try:
207
+ # Initialize knowledge base
208
+ knowledge_base = {
209
+ "instructions": instructions,
210
+ "facts": facts,
211
+ "document_content": [],
212
+ "audio_data": [],
213
+ "url_content": [],
214
+ "social_content": []
215
+ }
216
+
217
+ # Parse arguments
218
+ num_audios = 5 * 3
219
+ num_social_urls = 3 * 3
220
+ num_urls = 5
221
+
222
+ audios = args[:num_audios]
223
+ social_urls = args[num_audios:num_audios+num_social_urls]
224
+ urls = args[num_audios+num_social_urls:num_audios+num_social_urls+num_urls]
225
+ documents = args[num_audios+num_social_urls+num_urls:]
226
+
227
+ # Process URLs
228
+ for url in urls:
229
+ if url:
230
+ knowledge_base["url_content"].append(read_url(url))
231
+
232
+ # Process documents
233
+ for document in documents:
234
+ if document is not None:
235
+ knowledge_base["document_content"].append(read_document(document.name))
236
+
237
+ # Process audio files
238
+ for i in range(0, len(audios), 3):
239
+ audio_file, name, position = audios[i:i+3]
240
+ if audio_file is not None:
241
+ knowledge_base["audio_data"].append({
242
+ "audio": audio_file,
243
+ "name": name,
244
+ "position": position
245
+ })
246
+
247
+ # Process social media content
248
+ for i in range(0, len(social_urls), 3):
249
+ social_url, social_name, social_context = social_urls[i:i+3]
250
+ if social_url:
251
+ social_content = process_social_content(social_url)
252
+ if social_content:
253
+ knowledge_base["social_content"].append({
254
+ "url": social_url,
255
+ "name": social_name,
256
+ "context": social_context,
257
+ "text": social_content["text"],
258
+ "video": social_content["video"]
259
+ })
260
+
261
+ # Build transcriptions
262
+ transcriptions_text = ""
263
+ raw_transcriptions = ""
264
+
265
+ for idx, data in enumerate(knowledge_base["audio_data"]):
266
+ if data["audio"] is not None:
267
+ transcription = transcribe_audio(data["audio"])
268
+ transcriptions_text += f'"{transcription}" - {data["name"]}, {data["position"]}\n'
269
+ raw_transcriptions += f'[Audio/Video {idx + 1}]: "{transcription}" - {data["name"]}, {data["position"]}\n\n'
270
+
271
+ for data in knowledge_base["social_content"]:
272
+ if data["text"]:
273
+ transcriptions_text += f'[Social media text]: "{data["text"][:200]}..." - {data["name"]}, {data["context"]}\n'
274
+ raw_transcriptions += transcriptions_text + "\n\n"
275
+ if data["video"]:
276
+ video_transcription = f'[Social media video]: "{data["video"]}" - {data["name"]}, {data["context"]}\n'
277
+ transcriptions_text += video_transcription
278
+ raw_transcriptions += video_transcription + "\n\n"
279
+
280
+ document_content = "\n\n".join(knowledge_base["document_content"])
281
+ url_content = "\n\n".join(knowledge_base["url_content"])
282
+
283
+ # Create prompt
284
+ prompt = f"""[INST] You are a professional news writer. Write a news article based on the following information:
285
+
286
+ Instructions: {knowledge_base["instructions"]}
287
+ Facts: {knowledge_base["facts"]}
288
+ Additional content from documents: {document_content}
289
+ Additional content from URLs: {url_content}
290
+
291
+ Use these transcriptions as direct and indirect quotes:
292
+ {transcriptions_text}
293
+
294
+ Follow these requirements:
295
+ - Write a title
296
+ - Write a 15-word hook that complements the title
297
+ - Write the body with {size} words
298
+ - Use a {tone} tone
299
+ - Answer the 5 Ws (Who, What, When, Where, Why) in the first paragraph
300
+ - Use at least 80% direct quotes (in quotation marks)
301
+ - Use proper journalistic style
302
+ - Do not invent information
303
+ - Be rigorous with the provided facts [/INST]"""
304
+
305
+ # Generate article with specific handling for Zero GPU
306
+ with torch.inference_mode():
307
+ outputs = news_generator(
308
+ prompt,
309
+ max_new_tokens=min(int(size * 2), 1024),
310
+ return_full_text=False,
311
+ pad_token_id=tokenizer.eos_token_id,
312
+ num_return_sequences=1,
313
+ do_sample=True,
314
+ temperature=0.7,
315
+ top_p=0.95,
316
+ repetition_penalty=1.2
317
+ )
318
+
319
+ news_article = outputs[0]['generated_text']
320
+ news_article = news_article.replace('[INST]', '').replace('[/INST]', '').strip()
321
+
322
+ return news_article, raw_transcriptions
323
+
324
+ except Exception as e:
325
+ logger.error(f"Error generating news: {str(e)}")
326
+ return f"Error generating the news article: {str(e)}", ""
327
+
328
+ # Create Gradio interface
329
+ def create_demo():
330
+ with gr.Blocks() as demo:
331
+ gr.Markdown("## Generador de noticias todo en uno")
332
+
333
+ # Contenedor principal con dos columnas
334
+ with gr.Row():
335
+ # Columna izquierda - Formulario principal
336
+ with gr.Column(scale=2):
337
+ instrucciones = gr.Textbox(
338
+ label="Instrucciones para la noticia",
339
+ lines=2
340
+ )
341
+ hechos = gr.Textbox(
342
+ label="Describe los hechos de la noticia",
343
+ lines=4
344
+ )
345
+ tama帽o = gr.Number(
346
+ label="Tama帽o del cuerpo de la noticia (en palabras)",
347
+ value=100
348
+ )
349
+ tono = gr.Dropdown(
350
+ label="Tono de la noticia",
351
+ choices=["serio", "neutral", "divertido"],
352
+ value="neutral"
353
+ )
354
+
355
+ # Columna derecha - Tabs y campos
356
+ with gr.Column(scale=3):
357
+ # Lista de inputs que empezamos a construir
358
+ inputs_list = [instrucciones, hechos, tama帽o, tono]
359
+
360
+ # Tabs en la parte superior
361
+ with gr.Tabs():
362
+ # Audio/Video tabs
363
+ for i in range(1, 6):
364
+ with gr.TabItem(f"Audio/Video {i}"):
365
+ file = gr.File(
366
+ label=f"Audio/Video {i}",
367
+ file_types=["audio", "video"]
368
+ )
369
+ nombre = gr.Textbox(
370
+ label="Nombre",
371
+ placeholder="Nombre del entrevistado"
372
+ )
373
+ cargo = gr.Textbox(
374
+ label="Cargo",
375
+ placeholder="Cargo o rol"
376
+ )
377
+ inputs_list.extend([file, nombre, cargo])
378
+
379
+ # Redes Sociales tabs
380
+ for i in range(1, 4):
381
+ with gr.TabItem(f"Red Social {i}"):
382
+ social_url = gr.Textbox(
383
+ label=f"URL de red social {i}",
384
+ placeholder="https://..."
385
+ )
386
+ social_nombre = gr.Textbox(
387
+ label=f"Nombre de persona/cuenta {i}"
388
+ )
389
+ social_contexto = gr.Textbox(
390
+ label=f"Contexto del contenido {i}",
391
+ lines=2
392
+ )
393
+ inputs_list.extend([social_url, social_nombre, social_contexto])
394
+
395
+ # URL tabs
396
+ for i in range(1, 6):
397
+ with gr.TabItem(f"URL {i}"):
398
+ url = gr.Textbox(
399
+ label=f"URL {i}",
400
+ placeholder="https://..."
401
+ )
402
+ inputs_list.append(url)
403
+
404
+ # Documento tabs
405
+ for i in range(1, 6):
406
+ with gr.TabItem(f"Documento {i}"):
407
+ documento = gr.File(
408
+ label=f"Documento {i}",
409
+ file_types=["pdf", "docx", "xlsx", "csv"],
410
+ file_count="single"
411
+ )
412
+ inputs_list.append(documento)
413
+
414
+ # Separador
415
+ gr.Markdown("---")
416
+
417
+ # Transcripciones
418
+ with gr.Row():
419
+ transcripciones_output = gr.Textbox(
420
+ label="Transcripciones",
421
+ lines=10,
422
+ show_copy_button=True
423
+ )
424
+
425
+ # Separador
426
+ gr.Markdown("---")
427
+
428
+ # Bot贸n y output
429
+ with gr.Row():
430
+ generar = gr.Button("Generar borrador")
431
+
432
+ with gr.Row():
433
+ noticia_output = gr.Textbox(
434
+ label="Borrador generado",
435
+ lines=20,
436
+ show_copy_button=True
437
+ )
438
+
439
+ # Event handler
440
+ generar.click(
441
+ fn=generate_news,
442
+ inputs=inputs_list,
443
+ outputs=[noticia_output, transcripciones_output]
444
+ )
445
+
446
+ return demo
447
+
448
+ # Launch the app
449
+ if __name__ == "__main__":
450
+ demo = create_demo()
451
+ demo.queue()
452
+ demo.launch(
453
+ share=True,
454
+ server_name="0.0.0.0",
455
+ server_port=7860
456
+ )