"""AI research setup component for the API key manager."""
import streamlit as st
from loguru import logger
from typing import Dict, Any
from ..manager import APIKeyManager
from .base import render_navigation_buttons
import os
from dotenv import load_dotenv
import sys
# Configure logger
logger.remove() # Remove default handler
logger.add(
"logs/ai_research_setup.log",
rotation="500 MB",
retention="10 days",
level="DEBUG",
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}"
)
logger.add(
sys.stdout,
level="INFO",
format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message}"
)
def get_existing_api_key(key_name: str) -> str:
"""Get existing API key from environment or .env file.
Args:
key_name (str): Name of the API key to retrieve
Returns:
str: The API key value if found, empty string otherwise
"""
# First try to get from environment
api_key = os.getenv(key_name)
# If not in environment, try to get from .env file
if not api_key and os.path.exists('.env'):
try:
with open('.env', 'r') as f:
for line in f:
if line.strip().startswith(f"{key_name}="):
api_key = line.strip().split('=')[1]
break
except Exception as e:
logger.error(f"[get_existing_api_key] Failed to read {key_name} from .env: {str(e)}")
return api_key if api_key else ""
def update_env_file(api_keys: Dict[str, str]) -> None:
"""Update the .env file with new API keys, avoiding duplicates.
Args:
api_keys (Dict[str, str]): Dictionary of API keys to update
"""
try:
# Read existing .env file content
env_content = []
if os.path.exists('.env'):
with open('.env', 'r') as f:
env_content = f.readlines()
# Remove trailing newlines and empty lines
env_content = [line.strip() for line in env_content if line.strip()]
# Create a dictionary of existing variables
env_dict = {}
for line in env_content:
if '=' in line:
key, value = line.split('=', 1)
env_dict[key.strip()] = value.strip()
# Update with new values
env_dict.update(api_keys)
# Write back to .env file
with open('.env', 'w') as f:
for key, value in env_dict.items():
f.write(f"{key}={value}\n")
logger.info("[update_env_file] Successfully updated .env file with API keys")
except Exception as e:
logger.error(f"[update_env_file] Error updating .env file: {str(e)}")
raise
def render_ai_research_setup(api_key_manager: APIKeyManager) -> Dict[str, Any]:
"""Render the AI research setup step."""
logger.info("[render_ai_research_setup] Rendering AI research setup component")
st.markdown("""
""", unsafe_allow_html=True)
# Create two columns for different search types
col1, col2 = st.columns(2)
with col1:
st.markdown("### The Usual")
# Get existing API keys
existing_serpapi_key = get_existing_api_key("SERPAPI_KEY")
existing_firecrawl_key = get_existing_api_key("FIRECRAWL_API_KEY")
serpapi_key = st.text_input(
"## Enter 🔎 SerpAPI",
value=existing_serpapi_key,
type="password",
key="serpapi_key",
help="Enter your SerpAPI key",
placeholder="Access search engine results for research"
)
if serpapi_key or existing_serpapi_key:
st.markdown("""
✓ API key configured
""", unsafe_allow_html=True)
st.markdown("""
📋 How to get your SerpAPI key
Step-by-step guide:
- Visit SerpAPI
- Create an account
- Go to your dashboard
- Copy your API key
- Paste it here
Note: SerpAPI provides real-time search results from multiple engines.
""", unsafe_allow_html=True)
st.markdown("", unsafe_allow_html=True)
firecrawl_key = st.text_input(
"Enter 🕷️ Firecrawl API Key",
value=existing_firecrawl_key,
type="password",
key="firecrawl_key",
help="Enter your Firecrawl API key",
placeholder="Web content extraction and analysis"
)
if firecrawl_key or existing_firecrawl_key:
st.markdown("""
✓ Firecrawl API key configured
""", unsafe_allow_html=True)
st.markdown("""
📋 How to get your Firecrawl API key
Step-by-step guide:
- Visit Firecrawl
- Create an account
- Go to your dashboard
- Generate your API key
- Copy and paste it here
Note: Firecrawl provides powerful web content extraction and analysis capabilities.
""", unsafe_allow_html=True)
st.markdown("", unsafe_allow_html=True)
with col2:
st.markdown("### AI Deep Research")
# Get existing API keys
existing_tavily_key = get_existing_api_key("TAVILY_API_KEY")
existing_metaphor_key = get_existing_api_key("METAPHOR_API_KEY")
tavily_key = st.text_input(
"Enter 🤖 Tavily API Key",
value=existing_tavily_key,
type="password",
key="tavily_key",
help="Enter your Tavily API key",
placeholder="AI-powered search with semantic understanding"
)
if tavily_key or existing_tavily_key:
st.markdown("""
✓ Tavily API key configured
""", unsafe_allow_html=True)
st.markdown("""
📋 How to get your Tavily API key
Step-by-step guide:
- Visit Tavily
- Create an account
- Go to API settings
- Generate a new API key
- Copy and paste it here
Note: Tavily provides AI-powered semantic search capabilities.
""", unsafe_allow_html=True)
st.markdown("", unsafe_allow_html=True)
metaphor_key = st.text_input(
"Enter 🧠 Metaphor/Exa API Key",
value=existing_metaphor_key,
type="password",
key="metaphor_key",
help="Enter your Metaphor/Exa API key",
placeholder="Neural search engine for deep research"
)
if metaphor_key or existing_metaphor_key:
st.markdown("""
✓ API key configured
""", unsafe_allow_html=True)
st.markdown("""
📋 How to get your Metaphor/Exa API key
Step-by-step guide:
- Visit Metaphor/Exa
- Create an account
- Navigate to API settings
- Generate your API key
- Copy and paste it here
Note: Metaphor/Exa provides neural search capabilities for deep research.
""", unsafe_allow_html=True)
st.markdown("", unsafe_allow_html=True)
# Track changes
changes_made = bool(serpapi_key or tavily_key or metaphor_key or firecrawl_key)
# Navigation buttons with correct arguments
if render_navigation_buttons(3, 5, changes_made):
if changes_made:
try:
# Prepare API keys dictionary with only non-empty values
api_keys = {}
if serpapi_key:
api_keys['SERPAPI_KEY'] = serpapi_key
if tavily_key:
api_keys['TAVILY_API_KEY'] = tavily_key
if metaphor_key:
api_keys['METAPHOR_API_KEY'] = metaphor_key
if firecrawl_key:
api_keys['FIRECRAWL_API_KEY'] = firecrawl_key
# Update .env file with new API keys
update_env_file(api_keys)
# Update environment variables
for key, value in api_keys.items():
os.environ[key] = value
# Store the API keys in session state
st.session_state['api_keys'] = {
'serpapi': serpapi_key,
'tavily': tavily_key,
'metaphor': metaphor_key,
'firecrawl': firecrawl_key
}
# Update progress and move to next step
st.session_state['current_step'] = 4
st.rerun()
except Exception as e:
error_msg = f"Error saving API keys: {str(e)}"
logger.error(f"[render_ai_research_setup] {error_msg}")
st.error(error_msg)
else:
st.error("Please configure at least one research provider to continue")
# Detailed Information Section
st.markdown("---")
st.markdown("### Understanding Your Research Options")
# Create four columns for the information popovers
info_col1, info_col2, info_col3, info_col4 = st.columns(4)
# The Usual: Traditional Search Popover
with info_col1:
with st.popover("#### The Usual: Traditional Search"):
st.markdown("""
**SerpAPI**
- Real-time search results from multiple search engines
- Access to structured data from search results
- Great for gathering general information and market research
- Includes features like:
- Web search results
- News articles
- Knowledge graphs
- Related questions
""")
# AI Deep Research Popover
with info_col2:
with st.popover("#### AI Deep Research: Advanced Search Capabilities"):
st.markdown("""
**Tavily AI**
- AI-powered search with semantic understanding
- Automatically summarizes and analyzes search results
- Perfect for:
- Deep research tasks
- Academic research
- Fact-checking
- Real-time information gathering
**Metaphor/Exa**
- Neural search engine that understands context and meaning
- Specialized in finding highly relevant content
- Ideal for:
- Technical research
- Finding similar content
- Discovering patterns in research
- Understanding topic landscapes
""")
# Choosing the Right Tool Popover
with info_col3:
with st.popover("#### Choosing the Right Tool"):
st.markdown("""
1. **For General Research:**
- Start with SerpAPI for broad coverage and structured data
2. **For Deep Analysis:**
- Use Tavily AI when you need AI-powered insights
- Choose Metaphor/Exa for neural search and pattern discovery
3. **For Comprehensive Research:**
- Combine multiple tools to get the most complete picture
- Use SerpAPI for initial research
- Follow up with AI tools for deeper insights
> **Pro Tip:** Configure multiple providers to ensure you have backup options and can cross-reference results for better accuracy.
""")
# Coming Soon Popover
with info_col4:
with st.popover("#### 🔜 Coming Soon - More Search Options"):
st.markdown("""
**Bing Search API**
- Microsoft's powerful search API with comprehensive capabilities
- Features include:
- Web search with advanced filtering
- News articles with sentiment analysis
- Image search with visual recognition
- Video search with content understanding
- Custom search parameters for targeted results
**Google Search API**
- Google's programmable search engine with extensive features
- Capabilities include:
- Custom search engine creation
- Site-specific search
- Image and video search
- News search with time-based filtering
- Knowledge graph integration
**Additional Planned Integrations:**
- **DuckDuckGo API**: Privacy-focused search with no tracking
- **Brave Search API**: Independent search engine with unique features
- **Perplexity API**: AI-powered research assistant with real-time data
> **Note:** These integrations are under active development and will be available in future updates.
""")
return {"current_step": 3, "changes_made": changes_made}