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: if not search_results:
print("Querying with 'query' parameter...") raise Exception("No relevant documents found in the vector database")
search_response = client.query_points(
collection_name=collection_name, # Build context from search results
query=query_embedding.tolist(), context = "Based on the following documentation:\n\n"
limit=3, for result in search_results:
with_payload=True payload = result.payload
) if not payload:
print("✓ Query successful") continue
url = payload.get('url', 'Unknown URL')
# Debug search response content = payload.get('content', '')
print("\nSearch Response Debug:") context += f"From {url}:\n{content}\n\n"
print(f"Response type: {type(search_response)}")
context += f"\nUser Question: {query}\n\n"
# Get points from the response context += "Please provide a clear, concise answer that can be easily spoken out loud."
if hasattr(search_response, 'points'):
search_results = search_response.points # Process response with agents
else: processor_result = await Runner.run(processor_agent, context)
search_results = [] processor_response = processor_result.final_output
tts_result = await Runner.run(tts_agent, processor_response)
tts_response = tts_result.final_output
# Generate audio
async_openai = AsyncOpenAI(api_key=openai_api_key)
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"
)
# 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)
print(f"\n✓ Found {len(search_results)} relevant documents") return {
"status": "success",
if not search_results: "text_response": processor_response,
raise Exception("No relevant documents found in the vector database") "tts_instructions": tts_response,
"audio_path": audio_path,
# Build context from search results "sources": [r.payload.get("url", "Unknown URL") for r in search_results if r.payload],
context = "Based on the following documentation:\n\n" "query_details": {
for result in search_results: "vector_size": len(query_embedding),
payload = result.payload "results_found": len(search_results),
if not payload: "collection_name": collection_name
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...")
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="🎙️",
client, embedding_model = setup_qdrant_collection( layout="wide"
env_vars["QDRANT_URL"], )
env_vars["QDRANT_API_KEY"]
) init_session_state()
sidebar_config()
pages = crawl_documentation(
env_vars["FIRECRAWL_API_KEY"], # Main content area
"https://docs.agentmail.to/api-reference", st.title("🎙️ AI Voice Documentation Agent Team")
"crawled_docs" st.markdown("""
) Get OpenAI SDK voice-powered answers to your documentation questions! Simply:
1. Configure your API keys in the sidebar
store_embeddings(client, embedding_model, pages, "docs_embeddings") 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
processor_agent, tts_agent = setup_agents(env_vars["OPENAI_API_KEY"]) """)
query = "What are the required parameters for List Threads API of Agent Mail?" # Query input and processing
result = await process_query( query = st.text_input(
query, "What would you like to know about the documentation?",
client, placeholder="e.g., How do I authenticate API requests?",
embedding_model, disabled=not st.session_state.setup_complete
processor_agent, )
tts_agent,
"docs_embeddings", if query and st.session_state.setup_complete:
env_vars["OPENAI_API_KEY"] with st.status("Processing your query...", expanded=True) as status:
) try:
st.markdown("🔄 Searching documentation and generating response...")
print("\n--- Final Results ---") result = asyncio.run(process_query(
print(json.dumps(result, indent=2)) query,
st.session_state.client,
except ValueError as e: st.session_state.embedding_model,
print(f"\nConfiguration Error: {str(e)}") st.session_state.processor_agent,
print("\nPlease ensure your .env file contains all required variables:") st.session_state.tts_agent,
print("FIRECRAWL_API_KEY=your_key") "docs_embeddings",
print("QDRANT_URL=your_qdrant_url") st.session_state.openai_api_key
print("QDRANT_API_KEY=your_qdrant_key") ))
print("OPENAI_API_KEY=your_openai_key")
except Exception as e: if result["status"] == "success":
print(f"\nError: {str(e)}") 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"
)
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()