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 import json
from datetime import datetime from datetime import datetime
import time import time
import streamlit as st
load_dotenv() 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"): def setup_qdrant_collection(qdrant_url: str, qdrant_api_key: str, collection_name: str = "docs_embeddings"):
print("\n--- Step 1: Setting up Qdrant Collection ---") print("\n--- Step 1: Setting up Qdrant Collection ---")
@ -194,167 +313,157 @@ async def process_query(
collection_name: str, collection_name: str,
openai_api_key: str openai_api_key: str
): ):
print("\n--- Step 5: Processing Query ---")
try: try:
# Generate query embedding # Generate query embedding
print("Generating query embedding...")
query_embedding = list(embedding_model.embed([query]))[0] 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 # Search in Qdrant
print("\nVerifying collection status...") search_response = client.query_points(
try: collection_name=collection_name,
collection_info = client.get_collection(collection_name) query=query_embedding.tolist(),
print(f"Collection exists with {collection_info.points_count} points") limit=3,
except Exception as e: with_payload=True
print(f"Warning: Could not get collection info: {str(e)}") )
# Attempt search with query parameter (confirmed working) search_results = search_response.points if hasattr(search_response, 'points') else []
print("\nAttempting vector search...")
try:
print("Querying with 'query' parameter...")
search_response = client.query_points(
collection_name=collection_name,
query=query_embedding.tolist(),
limit=3,
with_payload=True
)
print("✓ Query successful")
# Debug search response if not search_results:
print("\nSearch Response Debug:") raise Exception("No relevant documents found in the vector database")
print(f"Response type: {type(search_response)}")
# Get points from the response # Build context from search results
if hasattr(search_response, 'points'): context = "Based on the following documentation:\n\n"
search_results = search_response.points for result in search_results:
else: payload = result.payload
search_results = [] if not payload:
continue
url = payload.get('url', 'Unknown URL')
content = payload.get('content', '')
context += f"From {url}:\n{content}\n\n"
print(f"\n✓ Found {len(search_results)} relevant documents") context += f"\nUser Question: {query}\n\n"
context += "Please provide a clear, concise answer that can be easily spoken out loud."
if not search_results: # Process response with agents
raise Exception("No relevant documents found in the vector database") processor_result = await Runner.run(processor_agent, context)
processor_response = processor_result.final_output
# Build context from search results tts_result = await Runner.run(tts_agent, processor_response)
context = "Based on the following documentation:\n\n" tts_response = tts_result.final_output
for result in search_results:
payload = result.payload
if not payload:
print(f"Warning: Result missing payload")
continue
url = payload.get('url', 'Unknown URL') # Generate audio
content = payload.get('content', '') async_openai = AsyncOpenAI(api_key=openai_api_key)
score = getattr(result, 'score', 'N/A') 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="mp3"
)
print(f"\nDocument from {url}") # Save audio to a temporary file
print(f"Relevance score: {score}") temp_dir = tempfile.gettempdir()
context += f"From {url}:\n{content}\n\n" audio_path = os.path.join(temp_dir, f"response_{uuid.uuid4()}.mp3")
context += f"\nUser Question: {query}\n\n" # Write the audio content to the file
context += "Please provide a clear, concise answer that can be easily spoken out loud." with open(audio_path, "wb") as f:
f.write(audio_response.content)
# Process response with agents return {
print("\nProcessing with Documentation Agent...") "status": "success",
processor_result = await Runner.run(processor_agent, context) "text_response": processor_response,
processor_response = processor_result.final_output "tts_instructions": tts_response,
print("✓ Generated text response") "audio_path": audio_path,
"sources": [r.payload.get("url", "Unknown URL") for r in search_results if r.payload],
print("\nProcessing with TTS Agent...") "query_details": {
tts_result = await Runner.run(tts_agent, processor_response) "vector_size": len(query_embedding),
tts_response = tts_result.final_output "results_found": len(search_results),
print("✓ Generated TTS instructions") "collection_name": collection_name
# Generate and play audio
print("\nGenerating audio response...")
async_openai = AsyncOpenAI(api_key=openai_api_key)
async with async_openai.audio.speech.with_streaming_response.create(
model="tts-1",
voice="alloy",
input=processor_response,
instructions=tts_response,
response_format="pcm"
) as response:
print("✓ Streaming audio response")
await LocalAudioPlayer().play(response)
return {
"status": "success",
"text_response": processor_response,
"tts_instructions": tts_response,
"sources": [r.payload.get("url", "Unknown URL") for r in search_results if r.payload],
"query_details": {
"vector_size": len(query_embedding),
"results_found": len(search_results),
"collection_name": collection_name
}
} }
}
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: except Exception as e:
print(f"\nError processing query: {str(e)}") print(f"\nError processing query: {str(e)}")
print("Full error details:")
import traceback
traceback.print_exc()
return { return {
"status": "error", "status": "error",
"error": str(e), "error": str(e),
"error_details": traceback.format_exc(),
"query": query "query": query
} }
async def main(): def run_streamlit():
try: """Main Streamlit application."""
env_vars = get_env_vars() st.set_page_config(
print("✓ Loaded environment variables") page_title="AI Voice Documentation Agent Team",
page_icon="🎙️",
layout="wide"
)
client, embedding_model = setup_qdrant_collection( init_session_state()
env_vars["QDRANT_URL"], sidebar_config()
env_vars["QDRANT_API_KEY"]
)
pages = crawl_documentation( # Main content area
env_vars["FIRECRAWL_API_KEY"], st.title("🎙️ AI Voice Documentation Agent Team")
"https://docs.agentmail.to/api-reference", st.markdown("""
"crawled_docs" 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
""")
store_embeddings(client, embedding_model, pages, "docs_embeddings") # 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
)
processor_agent, tts_agent = setup_agents(env_vars["OPENAI_API_KEY"]) if query and st.session_state.setup_complete:
with st.status("Processing your query...", expanded=True) as status:
try:
st.markdown("🔄 Searching documentation and generating response...")
result = asyncio.run(process_query(
query,
st.session_state.client,
st.session_state.embedding_model,
st.session_state.processor_agent,
st.session_state.tts_agent,
"docs_embeddings",
st.session_state.openai_api_key
))
query = "What are the required parameters for List Threads API of Agent Mail?" if result["status"] == "success":
result = await process_query( status.update(label="✅ Query processed!", state="complete")
query,
client,
embedding_model,
processor_agent,
tts_agent,
"docs_embeddings",
env_vars["OPENAI_API_KEY"]
)
print("\n--- Final Results ---") st.markdown("### Response:")
print(json.dumps(result, indent=2)) st.write(result["text_response"])
except ValueError as e: if "audio_path" in result:
print(f"\nConfiguration Error: {str(e)}") st.markdown(f"### 🔊 Audio Response (Voice: {st.session_state.selected_voice})")
print("\nPlease ensure your .env file contains all required variables:") # Pass the file path directly to st.audio
print("FIRECRAWL_API_KEY=your_key") st.audio(result["audio_path"], format="audio/mp3", start_time=0)
print("QDRANT_URL=your_qdrant_url")
print("QDRANT_API_KEY=your_qdrant_key") # For download button, we still need to read the bytes
print("OPENAI_API_KEY=your_openai_key") with open(result["audio_path"], "rb") as audio_file:
except Exception as e: audio_bytes = audio_file.read()
print(f"\nError: {str(e)}") st.download_button(
label="📥 Download Audio Response",
data=audio_bytes,
file_name=f"voice_response_{st.session_state.selected_voice}.mp3",
mime="audio/mp3"
)
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 Exception as 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__": if __name__ == "__main__":
asyncio.run(main()) run_streamlit()