shanusherly commited on
Commit
f0e7f66
·
verified ·
1 Parent(s): bac6824

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +170 -99
app.py CHANGED
@@ -1,126 +1,197 @@
1
  import os
2
- import gradio as gr
 
3
  import requests
 
4
  import google.generativeai as genai
 
5
 
6
- # -----------------------------
7
- # Load API keys from environment
8
- # -----------------------------
9
  GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
10
  ELEVENLABS_API_KEY = os.environ.get("ELEVENLABS_API_KEY")
11
- ELEVENLABS_VOICE_ID = "21m00Tcm4TlvDq8ikWAM"
 
12
 
13
  if not GEMINI_API_KEY:
14
- raise RuntimeError("Missing GEMINI_API_KEY in environment")
15
 
16
- # Configure Gemini
17
  genai.configure(api_key=GEMINI_API_KEY)
 
 
18
 
19
- # -----------------------------
20
- # Simple prompt + in-memory history
21
- # -----------------------------
22
- PROMPT_TEMPLATE = """You are a helpful assistant.
23
- {chat_history}
24
- User: {user_message}
25
- Chatbot:"""
26
-
27
  class SimpleMemory:
28
  def __init__(self, max_messages=20):
29
  self.max_messages = max_messages
30
- self.history = []
31
-
32
- def add_user(self, text):
33
- self.history.append(f"User: {text}")
34
- self._trim()
35
 
36
- def add_bot(self, text):
37
- self.history.append(f"Chatbot: {text}")
38
- self._trim()
39
-
40
- def _trim(self):
41
  if len(self.history) > self.max_messages:
42
- self.history = self.history[-self.max_messages:]
43
-
44
- def as_text(self):
45
- return "\n".join(self.history)
 
 
 
 
 
 
 
46
 
47
  memory = SimpleMemory(max_messages=20)
48
 
49
- # -----------------------------
50
- # Gemini wrapper
51
- # -----------------------------
52
- gemini_model = genai.GenerativeModel("gemini-2.5-flash")
 
 
 
53
 
54
  def generate_text_with_gemini(user_message):
55
- chat_history = memory.as_text()
56
- full_prompt = PROMPT_TEMPLATE.format(chat_history=chat_history, user_message=user_message)
57
- response = gemini_model.generate_content(full_prompt)
58
- text = response.text if hasattr(response, "text") else str(response)
59
- return text
60
-
61
- # -----------------------------
62
- # ElevenLabs audio generation (lazy import)
63
- # -----------------------------
64
- def generate_audio_elevenlabs(text):
65
  try:
66
- from elevenlabs.client import ElevenLabs
67
- from elevenlabs import save
 
 
 
 
 
68
  except Exception as e:
69
- print("ElevenLabs import failed:", e)
70
- return ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
  try:
73
- client = ElevenLabs(api_key=ELEVENLABS_API_KEY)
74
- audio = client.generate(
75
- text=text,
76
- voice=ELEVENLABS_VOICE_ID,
77
- model="eleven_monolingual_v1"
78
- )
79
-
80
- output_path = f"/tmp/audio_{abs(hash(text)) % 100000}.mp3"
81
- save(audio, output_path)
82
- return output_path
83
  except Exception as e:
84
- print("ElevenLabs generation error:", e)
85
- return ""
86
-
87
- # -----------------------------
88
- # Combined response
89
- # -----------------------------
90
- def get_text_and_audio(user_message):
91
- text = generate_text_with_gemini(user_message)
92
- memory.add_user(user_message)
93
- memory.add_bot(text)
94
-
95
- audio_path = ""
96
- if ELEVENLABS_API_KEY:
97
- audio_path = generate_audio_elevenlabs(text)
98
-
99
- return text, audio_path
100
-
101
- # -----------------------------
102
- # Gradio handler (UI unchanged except theme)
103
- # -----------------------------
104
- def chat_bot_response(message, history):
105
- text, audio_path = get_text_and_audio(message)
106
- # Return text only to keep UI identical; audio is generated in /tmp
107
- return text
108
-
109
- # -----------------------------
110
- # UI (theme parameter removed to avoid Gradio version mismatch)
111
- # -----------------------------
112
- demo = gr.ChatInterface(
113
- fn=chat_bot_response,
114
- title="🤖 Gemini + ElevenLabs Chatbot",
115
- description="Chat with Google Gemini AI with voice responses from ElevenLabs",
116
- examples=[
117
- "How are you doing?",
118
- "What are your interests?",
119
- "Tell me a short story",
120
- "What's the weather like today?",
121
- "Explain quantum computing in simple terms"
122
- ]
123
- )
124
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  if __name__ == "__main__":
126
- demo.launch(debug=True, share=True)
 
 
1
  import os
2
+ import time
3
+ import json
4
  import requests
5
+ import gradio as gr
6
  import google.generativeai as genai
7
+ from google.api_core.exceptions import ResourceExhausted
8
 
9
+ # -----------------------
10
+ # Configuration / secrets
11
+ # -----------------------
12
  GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
13
  ELEVENLABS_API_KEY = os.environ.get("ELEVENLABS_API_KEY")
14
+ ELEVENLABS_VOICE_ID = "21m00Tcm4TlvDq8ikWAM" # change if you prefer another voice
15
+ AUDIO_TMP_DIR = "/tmp"
16
 
17
  if not GEMINI_API_KEY:
18
+ raise RuntimeError("Missing GEMINI_API_KEY in environment. Set it in HF Space Secrets.")
19
 
20
+ # Configure Gemini SDK
21
  genai.configure(api_key=GEMINI_API_KEY)
22
+ # single model instance
23
+ gemini_model = genai.GenerativeModel("gemini-2.5-flash")
24
 
25
+ # -----------------------
26
+ # Simple in-memory chat memory
27
+ # -----------------------
 
 
 
 
 
28
  class SimpleMemory:
29
  def __init__(self, max_messages=20):
30
  self.max_messages = max_messages
31
+ self.history = [] # tuples (role, text)
 
 
 
 
32
 
33
+ def add(self, role, text):
34
+ self.history.append((role, text))
 
 
 
35
  if len(self.history) > self.max_messages:
36
+ self.history = self.history[-self.max_messages :]
37
+
38
+ def as_prompt_text(self):
39
+ # produce compact prompt history
40
+ lines = []
41
+ for role, text in self.history:
42
+ if role == "user":
43
+ lines.append(f"User: {text}")
44
+ else:
45
+ lines.append(f"Chatbot: {text}")
46
+ return "\n".join(lines)
47
 
48
  memory = SimpleMemory(max_messages=20)
49
 
50
+ # -----------------------
51
+ # Gemini text generation (safe)
52
+ # -----------------------
53
+ PROMPT_TEMPLATE = """You are a helpful assistant.
54
+ {chat_history}
55
+ User: {user_message}
56
+ Chatbot:"""
57
 
58
  def generate_text_with_gemini(user_message):
59
+ chat_history_text = memory.as_prompt_text()
60
+ prompt = PROMPT_TEMPLATE.format(chat_history=chat_history_text, user_message=user_message)
61
+
 
 
 
 
 
 
 
62
  try:
63
+ response = gemini_model.generate_content(prompt)
64
+ text = response.text if hasattr(response, "text") else str(response)
65
+ return text, None
66
+ except ResourceExhausted as e:
67
+ # quota exceeded — return friendly message
68
+ print("Gemini quota exhausted:", e)
69
+ return None, "Gemini quota exceeded. Please try again later."
70
  except Exception as e:
71
+ print("Gemini error:", e)
72
+ return None, f"Gemini error: {str(e)}"
73
+
74
+ # -----------------------
75
+ # ElevenLabs HTTP fallback (robust)
76
+ # -----------------------
77
+ def generate_audio_elevenlabs_http(text):
78
+ """
79
+ Returns (output_path, error_message). On success: output_path path string, error_message empty.
80
+ On failure: output_path '', error_message string.
81
+ """
82
+ if not ELEVENLABS_API_KEY:
83
+ return "", "ELEVENLABS_API_KEY not configured."
84
+
85
+ url = f"https://api.elevenlabs.io/v1/text-to-speech/{ELEVENLABS_VOICE_ID}"
86
+ headers = {
87
+ "Accept": "audio/mpeg",
88
+ "Content-Type": "application/json",
89
+ "xi-api-key": ELEVENLABS_API_KEY
90
+ }
91
+ payload = {
92
+ "text": text,
93
+ "model_id": "eleven_monolingual_v1",
94
+ "voice_settings": {"stability": 0.5, "similarity_boost": 0.5}
95
+ }
96
 
97
  try:
98
+ resp = requests.post(url, json=payload, headers=headers, timeout=30)
 
 
 
 
 
 
 
 
 
99
  except Exception as e:
100
+ err = f"HTTP request to ElevenLabs failed: {e}"
101
+ print(err)
102
+ return "", err
103
+
104
+ if resp.status_code == 200:
105
+ try:
106
+ # save audio bytes to temp file
107
+ filename = f"audio_{int(time.time()*1000)}_{abs(hash(text))%100000}.mp3"
108
+ path = os.path.join(AUDIO_TMP_DIR, filename)
109
+ with open(path, "wb") as f:
110
+ f.write(resp.content)
111
+ return path, ""
112
+ except Exception as e:
113
+ err = f"Failed to save audio file: {e}"
114
+ print(err)
115
+ return "", err
116
+ else:
117
+ # return response body if available
118
+ try:
119
+ body = resp.json()
120
+ except Exception:
121
+ body = resp.text
122
+ err = f"ElevenLabs API error {resp.status_code}: {body}"
123
+ print(err)
124
+ return "", err
125
+
126
+ # -----------------------
127
+ # Main combined workflow
128
+ # -----------------------
129
+ def process_user_message(user_message):
130
+ """
131
+ Returns tuple: (chat_history_list, audio_path_or_empty, error_message_or_empty)
132
+ chat_history_list is a list of (speaker, message) for the UI chat component.
133
+ """
134
+ # 1) Get text from Gemini with error handling
135
+ text, gen_err = generate_text_with_gemini(user_message)
136
+ if gen_err:
137
+ # don't crash show friendly message and no audio
138
+ memory.add("user", user_message)
139
+ fallback_text = "Sorry — the assistant is temporarily unavailable: " + gen_err
140
+ memory.add("bot", fallback_text)
141
+ # build chat list for UI
142
+ chat_list = [(role, msg) for role, msg in memory.history]
143
+ return chat_list, "", gen_err
144
+
145
+ # 2) Update memory
146
+ memory.add("user", user_message)
147
+ memory.add("bot", text)
148
+
149
+ # 3) Try to generate audio (HTTP fallback)
150
+ audio_path, audio_err = generate_audio_elevenlabs_http(text)
151
+ if audio_err:
152
+ print("Audio generation error:", audio_err)
153
+ # Return history and audio (audio path may be empty)
154
+ chat_list = [(role, msg) for role, msg in memory.history]
155
+ return chat_list, audio_path or "", audio_err or ""
156
+
157
+ # -----------------------
158
+ # Gradio UI (Blocks)
159
+ # -----------------------
160
+ with gr.Blocks() as demo:
161
+ gr.Markdown("## 🤖 Gemini + ElevenLabs Chatbot (Text + Audio replies)")
162
+ chatbot = gr.Chatbot(elem_id="chatbot")
163
+ with gr.Row():
164
+ txt = gr.Textbox(show_label=False, placeholder="Type your message and press Enter")
165
+ send_btn = gr.Button("Send")
166
+
167
+ audio_player = gr.Audio(label="Last reply audio (if available)", visible=False)
168
+
169
+ # submit action
170
+ def submit_message(message):
171
+ # process and return chat content and audio
172
+ history, audio_path, audio_err = process_user_message(message)
173
+ # format chat history for gr.Chatbot: list of [user, bot] pairs for display
174
+ # our memory stores alternating user/bot entries; convert to pairs
175
+ pairs = []
176
+ temp_user = None
177
+ for role, msg in history:
178
+ if role == "user":
179
+ temp_user = msg
180
+ else:
181
+ pairs.append((temp_user or "", msg))
182
+ temp_user = None
183
+
184
+ # show audio if available
185
+ if audio_path:
186
+ return pairs, gr.update(value=audio_path, visible=True)
187
+ else:
188
+ return pairs, gr.update(value=None, visible=False)
189
+
190
+ # wire button and textbox
191
+ send_btn.click(fn=submit_message, inputs=[txt], outputs=[chatbot, audio_player])
192
+ txt.submit(fn=submit_message, inputs=[txt], outputs=[chatbot, audio_player])
193
+
194
+ # Launch
195
  if __name__ == "__main__":
196
+ # Do not enable share=True unless you want a public link
197
+ demo.launch(debug=True)