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

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -614
app.py DELETED
@@ -1,614 +0,0 @@
1
-
2
- import spaces
3
-
4
- # Standard library imports
5
- import logging
6
- import os
7
- import tempfile
8
- from typing import List, Dict, Any
9
- from pathlib import Path
10
-
11
- # Third-party imports
12
- import gradio as gr
13
- import torch
14
- import pandas as pd
15
- import numpy as np
16
- import requests
17
- from bs4 import BeautifulSoup
18
- import whisper
19
- import yt_dlp
20
-
21
- # Document processing imports
22
- import fitz # PyMuPDF
23
- from docx import Document
24
- from pydub import AudioSegment
25
- from moviepy.editor import VideoFileClip
26
-
27
- # Hugging Face imports
28
- from transformers import (
29
- pipeline,
30
- AutoModelForCausalLM,
31
- AutoTokenizer
32
- )
33
-
34
- # Configure logging
35
- logging.basicConfig(
36
- level=logging.INFO,
37
- format='%(asctime)s - %(levelname)s - %(message)s'
38
- )
39
- logger = logging.getLogger(__name__)
40
-
41
- # Environment variables
42
- HUGGINGFACE_TOKEN = os.environ.get('HUGGINGFACE_TOKEN')
43
- if not HUGGINGFACE_TOKEN:
44
- logger.error("HUGGINGFACE_TOKEN environment variable not set")
45
- raise ValueError("Please set the HUGGINGFACE_TOKEN environment variable")
46
-
47
- # Global variables for models
48
- tokenizer = None
49
- model = None
50
- news_generator = None
51
- whisper_model = None
52
-
53
- def custom_css():
54
- return """
55
- #main-container {
56
- max-width: 1200px;
57
- margin: 0 auto;
58
- padding: 20px;
59
- }
60
-
61
- .main-title {
62
- text-align: center;
63
- padding: 20px 0;
64
- margin-bottom: 30px;
65
- border-bottom: 2px solid #eee;
66
- }
67
-
68
- .section-title {
69
- font-size: 1.2em;
70
- margin-bottom: 15px;
71
- color: #2c3e50;
72
- }
73
-
74
- .input-container {
75
- background: #f8f9fa;
76
- padding: 20px;
77
- border-radius: 10px;
78
- margin-bottom: 20px;
79
- }
80
-
81
- .source-tab {
82
- padding: 15px;
83
- background: white;
84
- border-radius: 8px;
85
- margin: 10px 0;
86
- }
87
-
88
- .generate-btn {
89
- background: #2c3e50 !important;
90
- color: white !important;
91
- padding: 12px 24px !important;
92
- }
93
-
94
- .output-container {
95
- background: #f8f9fa;
96
- padding: 20px;
97
- border-radius: 10px;
98
- margin-top: 20px;
99
- }
100
- """
101
-
102
- @spaces.GPU(duration=60)
103
- def initialize_models():
104
- """Initialize models with Zero GPU optimizations"""
105
- global tokenizer, model, news_generator, whisper_model
106
-
107
- try:
108
- logger.info("Starting model initialization...")
109
- model_name = "meta-llama/Llama-2-7b-chat-hf"
110
-
111
- # Load tokenizer
112
- logger.info("Loading tokenizer...")
113
- tokenizer = AutoTokenizer.from_pretrained(
114
- model_name,
115
- token=HUGGINGFACE_TOKEN
116
- )
117
- tokenizer.pad_token = tokenizer.eos_token
118
-
119
- # Load model
120
- logger.info("Loading model...")
121
- model = AutoModelForCausalLM.from_pretrained(
122
- model_name,
123
- token=HUGGINGFACE_TOKEN,
124
- torch_dtype=torch.bfloat16,
125
- device_map="auto",
126
- low_cpu_mem_usage=True
127
- )
128
-
129
- # Create pipeline
130
- logger.info("Creating pipeline...")
131
- news_generator = pipeline(
132
- "text-generation",
133
- model=model,
134
- tokenizer=tokenizer,
135
- device_map="auto",
136
- torch_dtype=torch.bfloat16,
137
- max_length=2048,
138
- do_sample=True,
139
- temperature=0.7,
140
- top_p=0.95,
141
- repetition_penalty=1.2
142
- )
143
-
144
- # Load Whisper model
145
- logger.info("Loading Whisper model...")
146
- whisper_model = whisper.load_model("base")
147
-
148
- logger.info("All models initialized successfully")
149
- return True
150
- except Exception as e:
151
- logger.error(f"Error during model initialization: {str(e)}")
152
- raise
153
-
154
- def download_social_media_video(url):
155
- """Download a video from social media."""
156
- ydl_opts = {
157
- 'format': 'bestaudio/best',
158
- 'postprocessors': [{
159
- 'key': 'FFmpegExtractAudio',
160
- 'preferredcodec': 'mp3',
161
- 'preferredquality': '192',
162
- }],
163
- 'outtmpl': '%(id)s.%(ext)s',
164
- }
165
- try:
166
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
167
- info_dict = ydl.extract_info(url, download=True)
168
- audio_file = f"{info_dict['id']}.mp3"
169
- logger.info(f"Video downloaded successfully: {audio_file}")
170
- return audio_file
171
- except Exception as e:
172
- logger.error(f"Error downloading video: {str(e)}")
173
- raise
174
-
175
- def convert_video_to_audio(video_file):
176
- """Convert a video file to audio."""
177
- try:
178
- video = VideoFileClip(video_file)
179
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_file:
180
- video.audio.write_audiofile(temp_file.name)
181
- logger.info(f"Video converted to audio: {temp_file.name}")
182
- return temp_file.name
183
- except Exception as e:
184
- logger.error(f"Error converting video: {str(e)}")
185
- raise
186
-
187
- def preprocess_audio(audio_file):
188
- """Preprocess the audio file to improve quality."""
189
- try:
190
- audio = AudioSegment.from_file(audio_file)
191
- audio = audio.apply_gain(-audio.dBFS + (-20))
192
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_file:
193
- audio.export(temp_file.name, format="mp3")
194
- logger.info(f"Audio preprocessed: {temp_file.name}")
195
- return temp_file.name
196
- except Exception as e:
197
- logger.error(f"Error preprocessing audio: {str(e)}")
198
- raise
199
-
200
- @spaces.GPU(duration=60)
201
- def transcribe_audio(file):
202
- """Transcribe an audio or video file."""
203
- try:
204
- if isinstance(file, str) and file.startswith('http'):
205
- file_path = download_social_media_video(file)
206
- elif isinstance(file, str) and file.lower().endswith(('.mp4', '.avi', '.mov', '.mkv')):
207
- file_path = convert_video_to_audio(file)
208
- else:
209
- file_path = preprocess_audio(file)
210
-
211
- logger.info(f"Transcribing audio: {file_path}")
212
- with torch.inference_mode():
213
- result = whisper_model.transcribe(file_path)
214
- transcription = result.get("text", "Error in transcription")
215
- logger.info(f"Transcription completed: {transcription[:50]}...")
216
- return transcription
217
- except Exception as e:
218
- logger.error(f"Error transcribing: {str(e)}")
219
- return f"Error processing the file: {str(e)}"
220
-
221
- def read_document(document_path):
222
- """Read the content of a document."""
223
- try:
224
- if document_path.endswith(".pdf"):
225
- doc = fitz.open(document_path)
226
- return "\n".join([page.get_text() for page in doc])
227
- elif document_path.endswith(".docx"):
228
- doc = Document(document_path)
229
- return "\n".join([paragraph.text for paragraph in doc.paragraphs])
230
- elif document_path.endswith(".xlsx"):
231
- return pd.read_excel(document_path).to_string()
232
- elif document_path.endswith(".csv"):
233
- return pd.read_csv(document_path).to_string()
234
- else:
235
- return "Unsupported file type. Please upload a PDF, DOCX, XLSX or CSV document."
236
- except Exception as e:
237
- return f"Error reading document: {str(e)}"
238
-
239
- def read_url(url):
240
- """Read the content of a URL."""
241
- try:
242
- response = requests.get(url)
243
- response.raise_for_status()
244
- soup = BeautifulSoup(response.content, 'html.parser')
245
- return soup.get_text()
246
- except Exception as e:
247
- return f"Error reading URL: {str(e)}"
248
-
249
- def process_social_content(url):
250
- """Process social media content."""
251
- try:
252
- text_content = read_url(url)
253
- try:
254
- video_content = transcribe_audio(url)
255
- except Exception:
256
- video_content = None
257
-
258
- return {
259
- "text": text_content,
260
- "video": video_content
261
- }
262
- except Exception as e:
263
- logger.error(f"Error processing social content: {str(e)}")
264
- return None
265
-
266
- @spaces.GPU(duration=60)
267
- def generate_news(instructions, facts, size, tone, *args):
268
- try:
269
- # Initialize knowledge base
270
- knowledge_base = {
271
- "instructions": instructions,
272
- "facts": facts,
273
- "document_content": [],
274
- "audio_data": [],
275
- "url_content": [],
276
- "social_content": []
277
- }
278
-
279
- # Parse arguments
280
- num_audios = 5 * 3
281
- num_social_urls = 3 * 3
282
- num_urls = 5
283
-
284
- audios = args[:num_audios]
285
- social_urls = args[num_audios:num_audios+num_social_urls]
286
- urls = args[num_audios+num_social_urls:num_audios+num_social_urls+num_urls]
287
- documents = args[num_audios+num_social_urls+num_urls:]
288
-
289
- # Process URLs
290
- for url in urls:
291
- if url:
292
- knowledge_base["url_content"].append(read_url(url))
293
-
294
- # Process documents
295
- for document in documents:
296
- if document is not None:
297
- knowledge_base["document_content"].append(read_document(document.name))
298
-
299
- # Process audio files
300
- for i in range(0, len(audios), 3):
301
- audio_file, name, position = audios[i:i+3]
302
- if audio_file is not None:
303
- knowledge_base["audio_data"].append({
304
- "audio": audio_file,
305
- "name": name,
306
- "position": position
307
- })
308
-
309
- # Process social media content
310
- for i in range(0, len(social_urls), 3):
311
- social_url, social_name, social_context = social_urls[i:i+3]
312
- if social_url:
313
- social_content = process_social_content(social_url)
314
- if social_content:
315
- knowledge_base["social_content"].append({
316
- "url": social_url,
317
- "name": social_name,
318
- "context": social_context,
319
- "text": social_content["text"],
320
- "video": social_content["video"]
321
- })
322
-
323
- # Build transcriptions
324
- transcriptions_text = ""
325
- raw_transcriptions = ""
326
-
327
- for idx, data in enumerate(knowledge_base["audio_data"]):
328
- if data["audio"] is not None:
329
- transcription = transcribe_audio(data["audio"])
330
- transcriptions_text += f'"{transcription}" - {data["name"]}, {data["position"]}\n'
331
- raw_transcriptions += f'[Audio/Video {idx + 1}]: "{transcription}" - {data["name"]}, {data["position"]}\n\n'
332
-
333
- for data in knowledge_base["social_content"]:
334
- if data["text"]:
335
- transcriptions_text += f'[Social media text]: "{data["text"][:200]}..." - {data["name"]}, {data["context"]}\n'
336
- raw_transcriptions += transcriptions_text + "\n\n"
337
- if data["video"]:
338
- video_transcription = f'[Social media video]: "{data["video"]}" - {data["name"]}, {data["context"]}\n'
339
- transcriptions_text += video_transcription
340
- raw_transcriptions += video_transcription + "\n\n"
341
-
342
- document_content = "\n\n".join(knowledge_base["document_content"])
343
- url_content = "\n\n".join(knowledge_base["url_content"])
344
-
345
- # Create prompt
346
- prompt = f"""[INST] You are a professional news writer. Write a news article based on the following information:
347
-
348
- Instructions: {knowledge_base["instructions"]}
349
- Facts: {knowledge_base["facts"]}
350
- Additional content from documents: {document_content}
351
- Additional content from URLs: {url_content}
352
-
353
- Use these transcriptions as direct and indirect quotes:
354
- {transcriptions_text}
355
-
356
- Follow these requirements:
357
- - Write a title
358
- - Write a 15-word hook that complements the title
359
- - Write the body with {size} words
360
- - Use a {tone} tone
361
- - Answer the 5 Ws (Who, What, When, Where, Why) in the first paragraph
362
- - Use at least 80% direct quotes (in quotation marks)
363
- - Use proper journalistic style
364
- - Do not invent information
365
- - Be rigorous with the provided facts [/INST]"""
366
-
367
- # Generate article
368
- with torch.inference_mode():
369
- outputs = news_generator(
370
- prompt,
371
- max_new_tokens=min(int(size * 2), 1024),
372
- return_full_text=False,
373
- pad_token_id=tokenizer.eos_token_id,
374
- num_return_sequences=1,
375
- do_sample=True,
376
- temperature=0.7,
377
- top_p=0.95,
378
- repetition_penalty=1.2
379
- )
380
-
381
- news_article = outputs[0]['generated_text']
382
- news_article = news_article.replace('[INST]', '').replace('[/INST]', '').strip()
383
-
384
- return news_article, raw_transcriptions
385
-
386
- except Exception as e:
387
- logger.error(f"Error generating news: {str(e)}")
388
- return f"Error generating the news article: {str(e)}", ""
389
-
390
- # Create Gradio interface
391
- def create_demo():
392
- with gr.Blocks(css="""
393
- /* Container styles */
394
- .gradio-container {
395
- max-width: 1200px !important;
396
- margin: auto !important;
397
- }
398
-
399
- /* Header styles */
400
- .header {
401
- margin-bottom: 1rem;
402
- }
403
-
404
- .header h1 {
405
- font-size: 1.5rem !important;
406
- margin-bottom: 0.5rem !important;
407
- }
408
-
409
- /* Two column layout */
410
- .two-columns {
411
- display: grid !important;
412
- grid-template-columns: 300px 1fr !important;
413
- gap: 2rem !important;
414
- margin-top: 1rem !important;
415
- }
416
-
417
- /* Input fields */
418
- .input-field {
419
- margin-bottom: 1rem !important;
420
- }
421
-
422
- /* Tab navigation */
423
- .tabs > .tab-nav {
424
- display: flex !important;
425
- flex-wrap: wrap !important;
426
- gap: 4px !important;
427
- border-bottom: 1px solid #e5e7eb !important;
428
- padding-bottom: 0.5rem !important;
429
- margin-bottom: 1rem !important;
430
- }
431
-
432
- .tab-nav * {
433
- font-size: 0.8rem !important;
434
- padding: 0.2rem 0.5rem !important;
435
- border-radius: 4px !important;
436
- background: transparent !important;
437
- border: 1px solid #e5e7eb !important;
438
- color: #374151 !important;
439
- }
440
-
441
- /* File upload area */
442
- .file-upload {
443
- max-height: 120px !important;
444
- min-height: 120px !important;
445
- border: 1px dashed #e5e7eb !important;
446
- border-radius: 4px !important;
447
- display: flex !important;
448
- align-items: center !important;
449
- justify-content: center !important;
450
- margin-bottom: 0.5rem !important;
451
- padding: 1rem !important;
452
- }
453
-
454
- .file-upload svg {
455
- width: 24px !important;
456
- height: 24px !important;
457
- opacity: 0.5 !important;
458
- }
459
-
460
- /* Button styles */
461
- .generate-btn {
462
- margin-top: 1rem !important;
463
- background: #4b5563 !important;
464
- color: white !important;
465
- padding: 0.5rem 1rem !important;
466
- border-radius: 4px !important;
467
- width: auto !important;
468
- }
469
-
470
- /* Output areas */
471
- .output-box {
472
- margin-top: 1rem !important;
473
- border: 1px solid #e5e7eb !important;
474
- border-radius: 4px !important;
475
- padding: 0.5rem !important;
476
- }
477
- """) as demo:
478
- # Header
479
- with gr.Group(elem_classes=["header"]):
480
- gr.Markdown("# All-in-One News Generator")
481
- gr.Markdown("""
482
- **About this tool**
483
-
484
- This AI-powered news generator helps journalists and content creators produce news articles by processing multiple types of input:
485
- - Audio and video files with automatic transcription
486
- - Social media content
487
- - Documents (PDF, DOCX, XLSX, CSV)
488
- - Web URLs
489
-
490
- The tool uses advanced AI to generate well-structured news articles following journalistic principles and maintaining the integrity of source quotes.
491
- """)
492
- gr.Markdown("*Created by Camilo Vega, AI Consultant*")
493
-
494
- with gr.Row(elem_classes=["two-columns"]):
495
- # Left column - Main inputs
496
- with gr.Column():
497
- instructions = gr.Textbox(
498
- label="News article instructions",
499
- lines=3,
500
- elem_classes=["input-field"]
501
- )
502
- facts = gr.Textbox(
503
- label="Describe the news facts",
504
- lines=4,
505
- elem_classes=["input-field"]
506
- )
507
- size = gr.Number(
508
- label="Content body size (in words)",
509
- value=100,
510
- elem_classes=["input-field"]
511
- )
512
- tone = gr.Dropdown(
513
- label="News tone",
514
- choices=["serious", "neutral", "lighthearted"],
515
- value="neutral",
516
- elem_classes=["input-field"]
517
- )
518
-
519
- # Right column - Source inputs
520
- with gr.Column():
521
- inputs_list = [instructions, facts, size, tone]
522
-
523
- with gr.Tabs() as tabs:
524
- # Audio/Video Sources
525
- for i in range(1, 6):
526
- with gr.Tab(f"Audio/Video {i}"):
527
- with gr.Group():
528
- file = gr.File(
529
- label="Upload Audio/Video",
530
- file_types=["audio", "video"],
531
- elem_classes=["file-upload"]
532
- )
533
- name = gr.Textbox(
534
- label="Name",
535
- elem_classes=["input-field"]
536
- )
537
- position = gr.Textbox(
538
- label="Position",
539
- elem_classes=["input-field"]
540
- )
541
- inputs_list.extend([file, name, position])
542
-
543
- # Social Media Sources
544
- for i in range(1, 4):
545
- with gr.Tab(f"Social Media {i}"):
546
- social_url = gr.Textbox(
547
- label="URL",
548
- elem_classes=["input-field"]
549
- )
550
- social_name = gr.Textbox(
551
- label="Person/account name",
552
- elem_classes=["input-field"]
553
- )
554
- social_context = gr.Textbox(
555
- label="Content context",
556
- elem_classes=["input-field"]
557
- )
558
- inputs_list.extend([social_url, social_name, social_context])
559
-
560
- # URLs
561
- for i in range(1, 6):
562
- with gr.Tab(f"URL {i}"):
563
- url = gr.Textbox(
564
- label=f"URL {i}",
565
- elem_classes=["input-field"]
566
- )
567
- inputs_list.append(url)
568
-
569
- # Documents
570
- for i in range(1, 6):
571
- with gr.Tab(f"Document {i}"):
572
- document = gr.File(
573
- label=f"Document {i}",
574
- file_types=["pdf", "docx", "xlsx", "csv"],
575
- elem_classes=["file-upload"]
576
- )
577
- inputs_list.append(document)
578
-
579
- # Output areas
580
- transcriptions_output = gr.Textbox(
581
- label="Transcriptions",
582
- lines=6,
583
- elem_classes=["output-box"]
584
- )
585
-
586
- generate = gr.Button(
587
- "Generate Draft",
588
- elem_classes=["generate-btn"]
589
- )
590
-
591
- news_output = gr.Textbox(
592
- label="Generated Draft",
593
- lines=10,
594
- elem_classes=["output-box"]
595
- )
596
-
597
- # Connect the generate button
598
- generate.click(
599
- fn=generate_news,
600
- inputs=inputs_list,
601
- outputs=[news_output, transcriptions_output]
602
- )
603
-
604
- return demo
605
-
606
- # Initialize and launch
607
- if __name__ == "__main__":
608
- demo = create_demo()
609
- demo.queue()
610
- demo.launch(
611
- share=True,
612
- server_name="0.0.0.0",
613
- server_port=7860
614
- )