AI-TALKS-BACK / app.py
Bonosa2's picture
Upload 3 files
484c797 verified
raw
history blame
4.54 kB
import os
import uuid
import json
import requests
import streamlit as st
from dotenv import load_dotenv
from utils import voice_map, get_voice_prompt_style, AUDIO_DIR
from generate_audio import generate_audio
from logger_setup import logger
# Load API keys
load_dotenv()
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
# Streamlit config
st.set_page_config(page_title="Voice Agent Pro", page_icon="🎀")
st.title("πŸŽ™οΈ Voice Agent Pro")
st.markdown("Summarized answers with expressive AI voices.")
logger.info("🎬 Streamlit app started")
# Sidebar: voice picker
st.sidebar.header("🎚️ Voice Settings")
voice_label = st.sidebar.selectbox("Choose a voice:", list(voice_map.keys()))
voice_id = voice_map[voice_label]
tone_prompt = get_voice_prompt_style(voice_label)
# Session state setup
if "answer" not in st.session_state: st.session_state.answer = ""
if "audio_key" not in st.session_state: st.session_state.audio_key = None
if "file_text" not in st.session_state: st.session_state.file_text = ""
if "key_points" not in st.session_state: st.session_state.key_points = []
# Inputs
query = st.text_area("πŸ—¨οΈ Ask or refine something based on the bullets:", value="", placeholder="e.g., What makes you so cool, Grandma?", key="query")
url = st.text_input("🌐 Optional URL to summarize:")
uploaded_file = st.file_uploader("πŸ“Ž Or upload a file (PDF, TXT, DOCX)", type=["pdf", "txt", "docx"])
# Reset state
# Reset state safely without error
if st.button("🧹 Clear All"):
logger.info("🧼 Clear All clicked β€” reloading app")
st.rerun()
# Helper: GPT streaming
def stream_openai_response(payload, headers):
with requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload, stream=True) as r:
for line in r.iter_lines():
if line and line.startswith(b"data: "):
yield line[len(b"data: "):].decode()
# Main: Summarize and speak
if st.button("πŸ” Summarize"):
if not query and not url and not uploaded_file:
st.warning("Please enter a question, a URL, or upload a file.")
else:
with st.spinner("Talking to GPT..."):
try:
if uploaded_file:
st.session_state.file_text = uploaded_file.read().decode("utf-8")
context = ""
if st.session_state.file_text:
context += st.session_state.file_text + "\n\n"
if url:
context += f"Summarize this page: {url}\n\n"
context += f"{tone_prompt}\n\nNow answer: {query}"
headers = {"Authorization": f"Bearer {OPENAI_API_KEY}"}
payload = {
"model": "gpt-4o",
"messages": [{"role": "user", "content": context}],
"temperature": 0.7,
"stream": True
}
st.session_state.answer = ""
answer_box = st.empty()
logger.info("🧠 Starting GPT streaming")
for chunk in stream_openai_response(payload, headers):
if chunk.strip() == "[DONE]":
logger.info("🟒 GPT stream complete marker received")
continue
try:
parsed = json.loads(chunk)
delta = parsed['choices'][0]['delta'].get('content', '')
st.session_state.answer += delta
answer_box.markdown(st.session_state.answer)
except json.JSONDecodeError as json_err:
logger.warning(f"⚠️ Skipping non-JSON chunk: {chunk}")
continue
logger.info("🧠 GPT response complete. Now generating audio...")
audio_key = str(uuid.uuid4())
generate_audio(st.session_state.answer, voice_id, audio_key)
st.session_state.audio_key = audio_key
except Exception as e:
st.error(f"πŸ”₯ Error: {e}")
logger.exception("πŸ”₯ Exception during summarize or audio generation")
# Final display
if st.session_state.answer:
st.subheader("πŸ“œ Answer")
st.success(st.session_state.answer)
if st.session_state.audio_key:
audio_path = os.path.join(AUDIO_DIR, f"{st.session_state.audio_key}.mp3")
if os.path.exists(audio_path):
st.audio(audio_path)
else:
st.error("❗ Audio file missing. Please check logs.")