Bonosa2 commited on
Commit
2570129
Β·
verified Β·
1 Parent(s): 6a9fed6

Upload 3 files

Browse files
Files changed (2) hide show
  1. app.py +94 -130
  2. generate_audio.py +3 -0
app.py CHANGED
@@ -1,137 +1,101 @@
 
 
 
1
  import streamlit as st
2
  import requests
3
- import time
4
- import os
5
-
6
- API_URL = "http://127.0.0.1:8000"
7
-
8
- st.set_page_config(page_title="Voice Agent", layout="centered")
9
- st.title(" Ask Me Anything – With Voice!")
10
- st.caption("Summarized answers with expressive AI voices.")
11
-
12
- # πŸ’š Pulse animation CSS
13
- st.markdown("""
14
- <style>
15
- .pulse-box {
16
- animation: pulse 1.5s ease-in-out;
17
- border: 2px solid #4CAF50 !important;
18
- border-radius: 0.5rem;
19
- padding: 0.5rem;
20
- }
21
- @keyframes pulse {
22
- 0% { box-shadow: 0 0 0px rgba(0, 255, 0, 0.5); }
23
- 50% { box-shadow: 0 0 20px rgba(0, 255, 0, 0.9); }
24
- 100% { box-shadow: 0 0 0px rgba(0, 255, 0, 0.5); }
25
- }
26
- </style>
27
- """, unsafe_allow_html=True)
28
-
29
- voice_options = {
30
- "Grandma GG": "grandma GG",
31
- "Tech Wizard": "tech wizard",
32
- "Perky Sidekick": "perky sidekick",
33
- "Bill the Newscaster": "bill the newscaster",
34
- "Spunky Charlie": "spunky charlie",
35
- "Sassy Teen": "sassy teen"
36
- }
37
-
38
- st.sidebar.header("πŸ”Š Voice Settings")
39
- voice_choice = st.sidebar.selectbox("Choose a voice:", list(voice_options.keys()))
40
- voice = voice_options[voice_choice]
41
-
42
- for key in ["query", "url", "file_text"]:
43
- if key not in st.session_state:
44
- st.session_state[key] = ""
45
 
 
46
  if st.button("🧹 Clear All"):
47
  st.session_state.query = ""
48
- st.session_state.url = ""
49
  st.session_state.file_text = ""
50
- st.rerun()
51
-
52
- # Bullet-to-query safe logic BEFORE rendering input box
53
- for i in range(10):
54
- if st.session_state.get(f"use_bullet_{i}", False):
55
- st.session_state["load_bullet_text"] = st.session_state.get(f"bullet_text_{i}", "")
56
- st.rerun()
57
-
58
- pulse = False
59
- if "load_bullet_text" in st.session_state:
60
- st.session_state.query = st.session_state["load_bullet_text"]
61
- del st.session_state["load_bullet_text"]
62
- pulse = True
63
-
64
- # Query box with optional pulse animation
65
- if pulse:
66
- st.markdown('<div class="pulse-box">', unsafe_allow_html=True)
67
- query = st.text_area("πŸ’¬ Ask or refine something based on the bullets:", key="query")
68
- st.markdown("</div>", unsafe_allow_html=True)
69
- else:
70
- query = st.text_area("πŸ’¬ Ask or refine something based on the bullets:", key="query")
71
-
72
- url = st.text_input("🌐 Optional URL to summarize:", placeholder="https://example.com", key="url")
73
- uploaded_file = st.file_uploader("πŸ“Ž Or upload a file (PDF, TXT, DOCX)", type=["pdf", "txt", "docx"])
74
-
75
- file_text = ""
76
- if uploaded_file is not None:
77
- file_text = uploaded_file.read().decode("utf-8", errors="ignore")
78
- st.session_state.file_text = file_text
79
- st.success("βœ… File uploaded successfully!")
80
- else:
81
- file_text = st.session_state.get("file_text", "")
82
-
83
- if st.button("🎯 Summarize"):
84
- with st.spinner("Generating response..."):
85
- try:
86
- payload = {
87
- "query": query,
88
- "url": url,
89
- "voice": voice,
90
- "file_text": file_text
91
- }
92
-
93
  try:
94
- response = requests.post(f"{API_URL}/process", json=payload, timeout=15)
95
- except requests.exceptions.RequestException as req_err:
96
- st.error(f"οΏ½οΏ½ Connection to backend failed:\n{req_err}")
97
- st.stop()
98
-
99
- if response.status_code == 200:
100
- data = response.json()
101
-
102
- if not query.strip() and data.get("key_points"):
103
- st.markdown("### πŸ“‹ Key Points from File")
104
- for i, point in enumerate(data["key_points"]):
105
- st.markdown(f"- {point}")
106
- st.session_state[f"bullet_text_{i}"] = point
107
- st.button("β†ͺ️ Use This", key=f"use_bullet_{i}")
108
- else:
109
- st.markdown("### πŸ“œ Answer")
110
- st.success(data["answer"])
111
-
112
- # βœ… Audio playback with retries
113
- if data.get("audio_key"):
114
- audio_key = data["audio_key"]
115
- audio_url = f"{API_URL}/get-audio/{audio_key}"
116
-
117
- for attempt in range(10):
118
- try:
119
- audio_check = requests.get(audio_url)
120
- if audio_check.status_code == 200:
121
- audio_bytes = audio_check.content
122
- if audio_bytes and len(audio_bytes) > 2000:
123
- st.audio(audio_bytes, format="audio/mp3")
124
- break
125
- else:
126
- st.warning("⚠️ Audio not ready yet. Waiting...")
127
- else:
128
- st.warning("⚠️ Couldn't reach audio endpoint.")
129
- except Exception as e:
130
- st.warning(f"⚠️ Audio request failed: {e}")
131
- time.sleep(0.5)
132
- else:
133
- st.warning("⚠️ Audio not ready or empty after retries.")
134
- else:
135
- st.error(f"❌ Backend error: {response.status_code} - {response.text}")
136
- except Exception as e:
137
- st.error(f"πŸ”₯ Unexpected error:\n{e}")
 
1
+ import os
2
+ import uuid
3
+ import logging
4
  import streamlit as st
5
  import requests
6
+ from dotenv import load_dotenv
7
+ from utils import voice_map, get_voice_prompt_style, AUDIO_DIR
8
+ from generate_audio import generate_audio
9
+
10
+ # Load secrets
11
+ load_dotenv()
12
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
13
+ ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
14
+
15
+ # Setup
16
+ os.makedirs(AUDIO_DIR, exist_ok=True)
17
+ logging.basicConfig(filename="frontend.log", level=logging.INFO)
18
+
19
+ # Page config
20
+ st.set_page_config(page_title="Voice Agent Pro", page_icon="🎀")
21
+ st.title("πŸŽ™οΈ Voice Agent Pro")
22
+ st.markdown("Summarized answers with expressive AI voices.")
23
+
24
+ # Sidebar voice selector
25
+ st.sidebar.header("🎚️ Voice Settings")
26
+ voice_label = st.sidebar.selectbox("Choose a voice:", list(voice_map.keys()))
27
+ voice_id = voice_map[voice_label]
28
+ tone_prompt = get_voice_prompt_style(voice_label)
29
+
30
+ # App state
31
+ if "answer" not in st.session_state: st.session_state.answer = ""
32
+ if "audio_key" not in st.session_state: st.session_state.audio_key = None
33
+ if "file_text" not in st.session_state: st.session_state.file_text = ""
34
+ if "key_points" not in st.session_state: st.session_state.key_points = []
35
+
36
+ # Query box
37
+ query = st.text_area(
38
+ "πŸ—¨οΈ Ask or refine something based on the bullets:",
39
+ value="",
40
+ placeholder="e.g., What makes you so cool, Grandma?",
41
+ key="query"
42
+ )
43
+
44
+ url = st.text_input("🌐 Optional URL to summarize:")
45
+ uploaded_file = st.file_uploader("πŸ“Ž Or upload a file (PDF, TXT, DOCX)", type=["pdf", "txt", "docx"])
 
 
46
 
47
+ # Clear all
48
  if st.button("🧹 Clear All"):
49
  st.session_state.query = ""
 
50
  st.session_state.file_text = ""
51
+ st.session_state.answer = ""
52
+ st.session_state.audio_key = None
53
+ st.session_state.key_points = []
54
+
55
+ # Summarize
56
+ if st.button("πŸ” Summarize"):
57
+ if not query and not url and not uploaded_file:
58
+ st.warning("Please enter a question, a URL, or upload a file.")
59
+ else:
60
+ with st.spinner("Talking to GPT..."):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  try:
62
+ if uploaded_file:
63
+ st.session_state.file_text = uploaded_file.read().decode("utf-8")
64
+
65
+ # Compose prompt
66
+ context = ""
67
+ if st.session_state.file_text:
68
+ context += st.session_state.file_text + "\n\n"
69
+ if url:
70
+ context += f"Summarize this page: {url}\n\n"
71
+ context += f"{tone_prompt}\n\nNow answer: {query}"
72
+
73
+ # GPT call
74
+ headers = {"Authorization": f"Bearer {OPENAI_API_KEY}"}
75
+ response = requests.post(
76
+ "https://api.openai.com/v1/chat/completions",
77
+ headers=headers,
78
+ json={
79
+ "model": "gpt-4o",
80
+ "messages": [{"role": "user", "content": context}],
81
+ "temperature": 0.7
82
+ }
83
+ )
84
+ answer = response.json()["choices"][0]["message"]["content"]
85
+ st.session_state.answer = answer
86
+
87
+ # Generate audio
88
+ audio_key = str(uuid.uuid4())
89
+ generate_audio(answer, voice_id, audio_key)
90
+ st.session_state.audio_key = audio_key
91
+
92
+ except Exception as e:
93
+ st.error(f"πŸ”₯ Error: {e}")
94
+
95
+ # Display answer
96
+ if st.session_state.answer:
97
+ st.subheader("πŸ“œ Answer")
98
+ st.success(st.session_state.answer)
99
+ if st.session_state.audio_key:
100
+ audio_path = os.path.join(AUDIO_DIR, f"{st.session_state.audio_key}.mp3")
101
+ st.audio(audio_path)
 
 
 
 
generate_audio.py CHANGED
@@ -2,6 +2,9 @@ import os
2
  import logging
3
  from elevenlabs import stream
4
  from elevenlabs.client import ElevenLabs
 
 
 
5
 
6
  AUDIO_DIR = "audio_outputs"
7
  logger = logging.getLogger(__name__)
 
2
  import logging
3
  from elevenlabs import stream
4
  from elevenlabs.client import ElevenLabs
5
+ from dotenv import load_dotenv
6
+ load_dotenv()
7
+
8
 
9
  AUDIO_DIR = "audio_outputs"
10
  logger = logging.getLogger(__name__)