streamlit inclusion

This commit is contained in:
Madhu 2025-03-25 04:02:38 +05:30
parent dc76c43d79
commit d16e2b757d

View file

@ -21,10 +21,129 @@ import asyncio
import json
from datetime import datetime
import time
import streamlit as st
load_dotenv()
def init_session_state():
"""Initialize session state variables for storing API keys and configurations."""
defaults = {
"initialized": False,
"qdrant_url": "",
"qdrant_api_key": "",
"firecrawl_api_key": "",
"openai_api_key": "",
"doc_url": "",
"setup_complete": False,
"client": None,
"embedding_model": None,
"processor_agent": None,
"tts_agent": None,
"selected_voice": "coral" # Default voice
}
for key, value in defaults.items():
if key not in st.session_state:
st.session_state[key] = value
def sidebar_config():
"""Render and handle the configuration sidebar."""
with st.sidebar:
st.title("🔑 Configuration")
st.markdown("---")
# API Keys and URLs
st.session_state.qdrant_url = st.text_input(
"Qdrant URL",
value=st.session_state.qdrant_url,
type="password"
)
st.session_state.qdrant_api_key = st.text_input(
"Qdrant API Key",
value=st.session_state.qdrant_api_key,
type="password"
)
st.session_state.firecrawl_api_key = st.text_input(
"Firecrawl API Key",
value=st.session_state.firecrawl_api_key,
type="password"
)
st.session_state.openai_api_key = st.text_input(
"OpenAI API Key",
value=st.session_state.openai_api_key,
type="password"
)
st.markdown("---")
st.session_state.doc_url = st.text_input(
"Documentation URL",
value=st.session_state.doc_url,
placeholder="https://docs.example.com"
)
# Voice selection
st.markdown("---")
st.markdown("### 🎤 Voice Settings")
voices = ["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"]
st.session_state.selected_voice = st.selectbox(
"Select Voice",
options=voices,
index=voices.index(st.session_state.selected_voice),
help="Choose the voice for the audio response"
)
# Setup button
if st.button("Initialize System", type="primary"):
if all([
st.session_state.qdrant_url,
st.session_state.qdrant_api_key,
st.session_state.firecrawl_api_key,
st.session_state.openai_api_key,
st.session_state.doc_url
]):
progress_placeholder = st.empty()
with progress_placeholder.container():
try:
# Setup Qdrant
st.markdown("🔄 Setting up Qdrant connection...")
client, embedding_model = setup_qdrant_collection(
st.session_state.qdrant_url,
st.session_state.qdrant_api_key
)
st.session_state.client = client
st.session_state.embedding_model = embedding_model
st.markdown("✅ Qdrant setup complete!")
# Crawl documentation
st.markdown("🔄 Crawling documentation pages...")
pages = crawl_documentation(
st.session_state.firecrawl_api_key,
st.session_state.doc_url
)
st.markdown(f"✅ Crawled {len(pages)} documentation pages!")
# Store embeddings
store_embeddings(
client,
embedding_model,
pages,
"docs_embeddings"
)
# Setup agents
processor_agent, tts_agent = setup_agents(
st.session_state.openai_api_key
)
st.session_state.processor_agent = processor_agent
st.session_state.tts_agent = tts_agent
st.session_state.setup_complete = True
st.success("✅ System initialized successfully!")
except Exception as e:
st.error(f"Error during setup: {str(e)}")
else:
st.error("Please fill in all the required fields!")
def setup_qdrant_collection(qdrant_url: str, qdrant_api_key: str, collection_name: str = "docs_embeddings"):
print("\n--- Step 1: Setting up Qdrant Collection ---")
@ -194,45 +313,19 @@ async def process_query(
collection_name: str,
openai_api_key: str
):
print("\n--- Step 5: Processing Query ---")
try:
# Generate query embedding
print("Generating query embedding...")
query_embedding = list(embedding_model.embed([query]))[0]
print(f"✓ Generated query embedding with shape: {len(query_embedding)}")
print(f"Vector sample (first 5 elements): {query_embedding[:5]}")
# Try to get collection info first
print("\nVerifying collection status...")
try:
collection_info = client.get_collection(collection_name)
print(f"Collection exists with {collection_info.points_count} points")
except Exception as e:
print(f"Warning: Could not get collection info: {str(e)}")
# Attempt search with query parameter (confirmed working)
print("\nAttempting vector search...")
try:
print("Querying with 'query' parameter...")
# Search in Qdrant
search_response = client.query_points(
collection_name=collection_name,
query=query_embedding.tolist(),
limit=3,
with_payload=True
)
print("✓ Query successful")
# Debug search response
print("\nSearch Response Debug:")
print(f"Response type: {type(search_response)}")
# Get points from the response
if hasattr(search_response, 'points'):
search_results = search_response.points
else:
search_results = []
print(f"\n✓ Found {len(search_results)} relevant documents")
search_results = search_response.points if hasattr(search_response, 'points') else []
if not search_results:
raise Exception("No relevant documents found in the vector database")
@ -242,48 +335,44 @@ async def process_query(
for result in search_results:
payload = result.payload
if not payload:
print(f"Warning: Result missing payload")
continue
url = payload.get('url', 'Unknown URL')
content = payload.get('content', '')
score = getattr(result, 'score', 'N/A')
print(f"\nDocument from {url}")
print(f"Relevance score: {score}")
context += f"From {url}:\n{content}\n\n"
context += f"\nUser Question: {query}\n\n"
context += "Please provide a clear, concise answer that can be easily spoken out loud."
# Process response with agents
print("\nProcessing with Documentation Agent...")
processor_result = await Runner.run(processor_agent, context)
processor_response = processor_result.final_output
print("✓ Generated text response")
print("\nProcessing with TTS Agent...")
tts_result = await Runner.run(tts_agent, processor_response)
tts_response = tts_result.final_output
print("✓ Generated TTS instructions")
# Generate and play audio
print("\nGenerating audio response...")
# Generate audio
async_openai = AsyncOpenAI(api_key=openai_api_key)
async with async_openai.audio.speech.with_streaming_response.create(
model="tts-1",
voice="alloy",
audio_response = await async_openai.audio.speech.create(
model="gpt-4o-mini-tts",
voice=st.session_state.selected_voice,
input=processor_response,
instructions=tts_response,
response_format="pcm"
) as response:
print("✓ Streaming audio response")
await LocalAudioPlayer().play(response)
response_format="mp3"
)
# Save audio to a temporary file
temp_dir = tempfile.gettempdir()
audio_path = os.path.join(temp_dir, f"response_{uuid.uuid4()}.mp3")
# Write the audio content to the file
with open(audio_path, "wb") as f:
f.write(audio_response.content)
return {
"status": "success",
"text_response": processor_response,
"tts_instructions": tts_response,
"audio_path": audio_path,
"sources": [r.payload.get("url", "Unknown URL") for r in search_results if r.payload],
"query_details": {
"vector_size": len(query_embedding),
@ -292,69 +381,89 @@ async def process_query(
}
}
except Exception as e:
print(f"Error during vector search: {str(e)}")
print("Full error details:")
import traceback
traceback.print_exc()
raise
except Exception as e:
print(f"\nError processing query: {str(e)}")
print("Full error details:")
import traceback
traceback.print_exc()
return {
"status": "error",
"error": str(e),
"error_details": traceback.format_exc(),
"query": query
}
async def main():
def run_streamlit():
"""Main Streamlit application."""
st.set_page_config(
page_title="AI Voice Documentation Agent Team",
page_icon="🎙️",
layout="wide"
)
init_session_state()
sidebar_config()
# Main content area
st.title("🎙️ AI Voice Documentation Agent Team")
st.markdown("""
Get OpenAI SDK voice-powered answers to your documentation questions! Simply:
1. Configure your API keys in the sidebar
2. Enter the documentation URL you want to learn about or have questions about
3. Ask your question below and get both text and voice responses
""")
# Query input and processing
query = st.text_input(
"What would you like to know about the documentation?",
placeholder="e.g., How do I authenticate API requests?",
disabled=not st.session_state.setup_complete
)
if query and st.session_state.setup_complete:
with st.status("Processing your query...", expanded=True) as status:
try:
env_vars = get_env_vars()
print("✓ Loaded environment variables")
client, embedding_model = setup_qdrant_collection(
env_vars["QDRANT_URL"],
env_vars["QDRANT_API_KEY"]
)
pages = crawl_documentation(
env_vars["FIRECRAWL_API_KEY"],
"https://docs.agentmail.to/api-reference",
"crawled_docs"
)
store_embeddings(client, embedding_model, pages, "docs_embeddings")
processor_agent, tts_agent = setup_agents(env_vars["OPENAI_API_KEY"])
query = "What are the required parameters for List Threads API of Agent Mail?"
result = await process_query(
st.markdown("🔄 Searching documentation and generating response...")
result = asyncio.run(process_query(
query,
client,
embedding_model,
processor_agent,
tts_agent,
st.session_state.client,
st.session_state.embedding_model,
st.session_state.processor_agent,
st.session_state.tts_agent,
"docs_embeddings",
env_vars["OPENAI_API_KEY"]
st.session_state.openai_api_key
))
if result["status"] == "success":
status.update(label="✅ Query processed!", state="complete")
st.markdown("### Response:")
st.write(result["text_response"])
if "audio_path" in result:
st.markdown(f"### 🔊 Audio Response (Voice: {st.session_state.selected_voice})")
# Pass the file path directly to st.audio
st.audio(result["audio_path"], format="audio/mp3", start_time=0)
# For download button, we still need to read the bytes
with open(result["audio_path"], "rb") as audio_file:
audio_bytes = audio_file.read()
st.download_button(
label="📥 Download Audio Response",
data=audio_bytes,
file_name=f"voice_response_{st.session_state.selected_voice}.mp3",
mime="audio/mp3"
)
print("\n--- Final Results ---")
print(json.dumps(result, indent=2))
st.markdown("### Sources:")
for source in result["sources"]:
st.markdown(f"- {source}")
else:
status.update(label="❌ Error processing query", state="error")
st.error(f"Error: {result.get('error', 'Unknown error occurred')}")
except ValueError as e:
print(f"\nConfiguration Error: {str(e)}")
print("\nPlease ensure your .env file contains all required variables:")
print("FIRECRAWL_API_KEY=your_key")
print("QDRANT_URL=your_qdrant_url")
print("QDRANT_API_KEY=your_qdrant_key")
print("OPENAI_API_KEY=your_openai_key")
except Exception as e:
print(f"\nError: {str(e)}")
status.update(label="❌ Error processing query", state="error")
st.error(f"Error processing query: {str(e)}")
elif not st.session_state.setup_complete:
st.info("👈 Please configure the system using the sidebar first!")
if __name__ == "__main__":
asyncio.run(main())
run_streamlit()