voiceAI / src /streamlit_app.py
ahanbose's picture
Update src/streamlit_app.py
7e5fab5 verified
"""
app.py
──────────────────────────────────────────────────────────────────────────────
VoiceVerse Pro β€” Streamlit Orchestrator (2026 Stable Build)
This file is intentionally thin. Its only jobs are:
1. Configure the Streamlit page
2. Inject global CSS
3. Render the header and stage tracker
4. Delegate every pipeline stage to its own UI module
5. Provide a debug panel for development
All business logic lives in modules/
All UI rendering logic lives in ui/
Pipeline flow:
ui.sidebar β†’ SidebarConfig
ui.stage_upload β†’ PipelineState.stage 0 β†’ 1
ui.stage_retrieve β†’ PipelineState.stage 1 β†’ 2
ui.stage_generate β†’ PipelineState.stage 2 β†’ 3
ui.stage_audio β†’ PipelineState.stage 3 β†’ 4
"""
import logging
import os
import sys
# ── Path fix: ensure the project root is on sys.path regardless of where
# `streamlit run` is invoked from. Without this, `import ui` and
# `import modules` fail when the CWD is not the project root.
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import streamlit as st
from dotenv import load_dotenv
load_dotenv() # loads .env β†’ os.environ before any module reads env vars
from ui import (
get_pipeline_state,
inject_css,
render_header,
render_stage_tracker,
render_mode_selector,
sidebar,
stage_upload,
stage_retrieve,
stage_generate,
stage_audio,
)
# ── Logging ───────────────────────────────────────────────────────────────────
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
)
# ── Page config (must be first Streamlit call) ────────────────────────────────
st.set_page_config(
page_title="VoiceVerse Pro",
page_icon="πŸŽ™οΈ",
layout="wide",
initial_sidebar_state="expanded",
)
# ── Global styles ─────────────────────────────────────────────────────────────
inject_css()
# ── Session state ─────────────────────────────────────────────────────────────
state = get_pipeline_state()
# ── Sidebar β†’ typed SidebarConfig ────────────────────────────────────────────
config = sidebar.render(current_stage=state.stage)
# ── Header + stage tracker ────────────────────────────────────────────────────
render_header()
render_stage_tracker(state.stage)
# ── Mode selector (prominent main-area toggle) ────────────────────────────────
selected_mode = render_mode_selector()
# Sync the session-state selection back into config so all stages see it
from ui.state import OutputMode
config.output_mode = OutputMode(selected_mode)
# ── Two-column layout ─────────────────────────────────────────────────────────
col_left, col_right = st.columns([1, 1.3], gap="large")
with col_left:
stage_upload.render(state, config)
stage_retrieve.render(state, config)
with col_right:
stage_generate.render(state, config)
stage_audio.render(state, config)
# ── Debug panel ───────────────────────────────────────────────────────────────
st.divider()
with st.expander("πŸ› οΈ Debug & System Info", expanded=False):
import sys, platform
st.markdown(
f"**Python:** `{sys.version}` \n"
f"**Platform:** `{platform.platform()}` \n"
f"**Pipeline Stage:** `{state.stage}` \n"
f"**Chunks Indexed:** `{state.total_chunks}` \n"
f"**Context Retrieved:** `{state.has_context}` \n"
f"**Script Generated:** `{state.has_script}` \n"
f"**Audio Ready:** `{state.has_audio}`"
)
if st.button("πŸ”„ Reset All State"):
for k in list(st.session_state.keys()):
del st.session_state[k]
st.rerun()