Wosqa commited on
Commit
1cb8518
·
verified ·
1 Parent(s): 815904c

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +206 -0
  2. chat_history.json +10 -0
  3. requirements.txt +9 -0
app.py ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import requests
4
+ import gradio as gr
5
+ from bs4 import BeautifulSoup
6
+ from groq import Groq
7
+ from youtube_transcript_api import YouTubeTranscriptApi
8
+ from dotenv import load_dotenv
9
+
10
+ load_dotenv()
11
+
12
+ # --- API KEYS ---
13
+ BRIGHTDATA_API_KEY = os.getenv("BRIGHTDATA_API_KEY")
14
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
15
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
16
+
17
+ # --- Clients ---
18
+ client = Groq(api_key=GROQ_API_KEY)
19
+ openai_client = None
20
+ if OPENAI_API_KEY:
21
+ from openai import OpenAI
22
+ openai_client = OpenAI(api_key=OPENAI_API_KEY)
23
+
24
+ # --- Persistent Storage ---
25
+ HISTORY_FILE = "chat_history.json"
26
+
27
+ if os.path.exists(HISTORY_FILE):
28
+ try:
29
+ with open(HISTORY_FILE, "r") as f:
30
+ conversation_history = json.load(f)
31
+ if not isinstance(conversation_history, list):
32
+ conversation_history = []
33
+ except (json.JSONDecodeError, Exception):
34
+ conversation_history = []
35
+ else:
36
+ conversation_history = []
37
+
38
+ # ----------------------
39
+ # LLM Wrapper
40
+ # ----------------------
41
+ def ask_llm(query, context=None):
42
+ system_prompt = """
43
+ You are a helpful AI assistant.
44
+ Use ONLY the provided context and conversation history to answer the question.
45
+ If the answer is not found in the context, respond clearly that you don't know based on the provided info.
46
+ """
47
+
48
+ messages = [{"role": "system", "content": system_prompt}]
49
+
50
+ # Add context if available
51
+ if context:
52
+ messages.append({"role": "system", "content": f"CONTEXT:\n{context}"})
53
+
54
+ messages.extend(conversation_history)
55
+ messages.append({"role": "user", "content": query})
56
+
57
+ try:
58
+ response = client.chat.completions.create(
59
+ model="llama-3.1-8b-instant",
60
+ messages=messages,
61
+ temperature=0.3
62
+ )
63
+ answer = response.choices[0].message.content
64
+
65
+ # Update conversation history
66
+ conversation_history.append({"role": "user", "content": query})
67
+ conversation_history.append({"role": "assistant", "content": answer})
68
+
69
+ # Save to persistent storage
70
+ with open(HISTORY_FILE, "w") as f:
71
+ json.dump(conversation_history, f, indent=2)
72
+
73
+ return answer
74
+ except Exception as e:
75
+ return f"Error communicating with LLM: {str(e)}"
76
+
77
+ # ----------------------
78
+ # Website Scraper
79
+ # ----------------------
80
+ def scrape_website(url, question):
81
+ try:
82
+ headers = {"Authorization": f"Bearer {BRIGHTDATA_API_KEY}"}
83
+ payload = {"zone": "web_unlocker1", "url": url, "format": "raw"}
84
+
85
+ response = requests.post(
86
+ "https://api.brightdata.com/request",
87
+ headers=headers,
88
+ json=payload,
89
+ timeout=60
90
+ )
91
+ if response.status_code != 200:
92
+ return f"Bright Data Error: {response.status_code}"
93
+
94
+ soup = BeautifulSoup(response.text, "html.parser")
95
+ text = soup.get_text(separator=" ", strip=True)
96
+
97
+ if not text:
98
+ return "⚠️ Could not extract content from the website."
99
+
100
+ return ask_llm(question, context=text[:12000])
101
+
102
+ except Exception as e:
103
+ return f"Error scraping website: {str(e)}"
104
+
105
+ # ----------------------
106
+ # YouTube Transcript Q&A
107
+ # ----------------------
108
+ def youtube_qa(video_id, question):
109
+ try:
110
+ transcript = YouTubeTranscriptApi.get_transcript(video_id)
111
+ full_text = " ".join([entry["text"] for entry in transcript])
112
+
113
+ if not full_text.strip():
114
+ return "⚠️ No transcript text found for this video."
115
+
116
+ return ask_llm(question, context=full_text[:12000])
117
+
118
+ except Exception:
119
+ return "❌ Could not retrieve transcript. Invalid video ID or no transcript available."
120
+
121
+ # ----------------------
122
+ # Voice Chat (STT + TTS)
123
+ # ----------------------
124
+ def voice_chat(audio_file):
125
+ if not audio_file:
126
+ return "", "⚠️ No audio provided.", None
127
+
128
+ # Transcribe audio using Groq (since model is whisper-large-v3)
129
+ try:
130
+ with open(audio_file, "rb") as f:
131
+ transcription = client.audio.transcriptions.create(
132
+ file=f,
133
+ model="whisper-large-v3"
134
+ )
135
+ user_text = transcription.text
136
+ except Exception as e:
137
+ return "", f"❌ Could not transcribe audio: {e}", None
138
+
139
+ # Ask LLM
140
+ answer_text = ask_llm(user_text)
141
+
142
+ # Convert answer to speech using OpenAI TTS
143
+ audio_path = "temp_audio/output.mp3"
144
+ try:
145
+ if not openai_client:
146
+ return user_text, f"{answer_text}\n\n(Voice output unavailable - OpenAI key missing)", None
147
+
148
+ tts_response = openai_client.audio.speech.create(
149
+ model="tts-1",
150
+ voice="alloy",
151
+ input=answer_text[:4096] # Limit input for TTS
152
+ )
153
+
154
+ os.makedirs("temp_audio", exist_ok=True)
155
+ with open(audio_path, "wb") as f:
156
+ f.write(tts_response.content)
157
+ except Exception as e:
158
+ return user_text, f"{answer_text}\n\n❌ Could not generate audio: {e}", None
159
+
160
+ return user_text, answer_text, audio_path
161
+
162
+ # ----------------------
163
+ # Gradio Interface
164
+ # ----------------------
165
+ with gr.Blocks() as demo:
166
+ gr.Markdown("# 🤖 Multimodal AI Assistant (Voice + Text)")
167
+
168
+ with gr.Tabs():
169
+ # Tab 1: Website Q&A
170
+ with gr.Tab("🌐 Website Q&A"):
171
+ url_input = gr.Textbox(label="Enter Website URL")
172
+ website_question = gr.Textbox(label="Ask a Question")
173
+ website_output = gr.Textbox(label="Answer")
174
+ website_btn = gr.Button("Ask")
175
+ website_btn.click(
176
+ scrape_website,
177
+ inputs=[url_input, website_question],
178
+ outputs=website_output
179
+ )
180
+
181
+ # Tab 2: YouTube Transcript Q&A
182
+ with gr.Tab("🎥 YouTube Transcript Q&A"):
183
+ video_id_input = gr.Textbox(label="Enter YouTube Video ID")
184
+ youtube_question = gr.Textbox(label="Ask a Question")
185
+ youtube_output = gr.Textbox(label="Answer")
186
+ youtube_btn = gr.Button("Ask")
187
+ youtube_btn.click(
188
+ youtube_qa,
189
+ inputs=[video_id_input, youtube_question],
190
+ outputs=youtube_output
191
+ )
192
+
193
+ # Tab 3: Voice Chat
194
+ with gr.Tab("🎤 Voice Chat"):
195
+ audio_input = gr.Audio(sources=["microphone"], type="filepath", label="Record your question")
196
+ voice_text_output = gr.Textbox(label="Transcribed Text")
197
+ voice_answer_output = gr.Textbox(label="AI Answer")
198
+ voice_audio_output = gr.Audio(label="AI Voice Response", autoplay=True)
199
+ voice_btn = gr.Button("Ask")
200
+ voice_btn.click(
201
+ voice_chat,
202
+ inputs=[audio_input],
203
+ outputs=[voice_text_output, voice_answer_output, voice_audio_output]
204
+ )
205
+
206
+ demo.launch()
chat_history.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "role": "user",
4
+ "content": " Hey, how are you? I'm looking to meet you soon."
5
+ },
6
+ {
7
+ "role": "assistant",
8
+ "content": "I'm just a computer program, so I don't have feelings like humans do, but I'm functioning properly and ready to help. As for meeting you in person, I'm a large language model, I don't have a physical presence, so we can only interact through text-based conversations like this one. How can I assist you today?"
9
+ }
10
+ ]
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ requests>=2.31
3
+ beautifulsoup4>=4.12
4
+ pandas>=2.0
5
+ groq>=0.0.3
6
+ youtube-transcript-api>=0.6.0
7
+ python-dotenv>=1.0.0
8
+ openai>=0.28
9
+ soundfile>=0.12.1