Search_Agent / app.py
kardwalker's picture
Update app.py
882be04 verified
import gradio as gr
import asyncio
import json
import time
from datetime import datetime
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from typing import Dict, List, Optional, Tuple
import nest_asyncio
# Apply nest_asyncio for compatibility with Gradio
nest_asyncio.apply()
# Import your existing search agent classes and functions
# (Assuming all the previous code is imported or defined above)
class GradioSearchInterface:
def __init__(self):
self.search_workflow = create_search_workflow()
self.search_history = []
self.performance_metrics = {
'queries': 0,
'avg_processing_time': 0,
'avg_confidence': 0,
'total_results': 0
}
async def process_search_async(self, query: str, intent_override: str = None) -> Tuple[str, str, str, str, str]:
"""Process search query asynchronously"""
if not query.strip():
return "Please enter a search query.", "", "", "", ""
# Initialize state
initial_state = AgentState(
query=query.strip(),
intent=QueryIntent[intent_override] if intent_override and intent_override != "Auto-detect" else None,
expanded_queries=[],
search_results=[],
semantic_index=None,
ranked_results=[],
verified_facts=[],
answer="",
confidence_score=0.0,
error_log=[],
cache_hits=0,
processing_time=0.0,
user_context={},
iteration=0
)
start_time = time.time()
try:
# Run the workflow
final_state = await self.search_workflow.ainvoke(initial_state)
processing_time = time.time() - start_time
# Update performance metrics
self.performance_metrics['queries'] += 1
self.performance_metrics['avg_processing_time'] = (
(self.performance_metrics['avg_processing_time'] * (self.performance_metrics['queries'] - 1) + processing_time)
/ self.performance_metrics['queries']
)
self.performance_metrics['avg_confidence'] = (
(self.performance_metrics['avg_confidence'] * (self.performance_metrics['queries'] - 1) + final_state['confidence_score'])
/ self.performance_metrics['queries']
)
self.performance_metrics['total_results'] += len(final_state['search_results'])
# Store in history
search_record = {
'timestamp': datetime.now().isoformat(),
'query': query,
'intent': final_state['intent'].value if final_state['intent'] else 'unknown',
'processing_time': processing_time,
'confidence': final_state['confidence_score'],
'results_count': len(final_state['search_results']),
'answer': final_state['answer']
}
self.search_history.append(search_record)
# Format results
answer = final_state['answer']
# Create summary
summary = f"""
## Search Summary
- **Query Intent**: {final_state['intent'].value if final_state['intent'] else 'Unknown'}
- **Expanded Queries**: {len(final_state['expanded_queries'])} queries generated
- **Total Results Found**: {len(final_state['search_results'])} results
- **Top Results Analyzed**: {len(final_state['ranked_results'])} results
- **Verified Facts**: {len(final_state['verified_facts'])} facts
- **Processing Time**: {processing_time:.2f} seconds
- **Confidence Score**: {final_state['confidence_score']:.2%}
"""
# Format search results
results_df = []
for i, result in enumerate(final_state['ranked_results'][:10]): # Top 10 results
results_df.append({
'Rank': i + 1,
'Title': result['title'][:100] + '...' if len(result['title']) > 100 else result['title'],
'Source': result['source'].title(),
'Authority Score': f"{result.get('authority_score', 0):.2f}",
'Relevance Score': f"{result.get('relevance_score', 0):.2f}",
'Composite Score': f"{result.get('composite_score', 0):.2f}",
'URL': result['url']
})
results_table = pd.DataFrame(results_df) if results_df else pd.DataFrame()
# Format verified facts
facts_text = ""
if final_state['verified_facts']:
facts_text = "## Verified Facts\n\n"
for i, fact in enumerate(final_state['verified_facts'][:5], 1):
confidence = fact.get('confidence', 0)
facts_text += f"{i}. **{fact['fact']}** (Confidence: {confidence:.1%})\n\n"
# Error log
errors = "\n".join(final_state['error_log']) if final_state['error_log'] else "No errors occurred."
return answer, summary, results_table, facts_text, errors
except Exception as e:
error_msg = f"Error processing search: {str(e)}"
return error_msg, "", pd.DataFrame(), "", error_msg
def process_search(self, query: str, intent_override: str = "Auto-detect") -> Tuple[str, str, str, str, str]:
"""Synchronous wrapper for async search processing"""
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
return loop.run_until_complete(self.process_search_async(query, intent_override))
finally:
loop.close()
def get_search_history(self) -> pd.DataFrame:
"""Get search history as DataFrame"""
if not self.search_history:
return pd.DataFrame()
df = pd.DataFrame(self.search_history)
df['timestamp'] = pd.to_datetime(df['timestamp'])
return df[['timestamp', 'query', 'intent', 'processing_time', 'confidence', 'results_count']]
def get_performance_chart(self):
"""Create performance visualization"""
if not self.search_history:
return None
df = pd.DataFrame(self.search_history)
df['timestamp'] = pd.to_datetime(df['timestamp'])
# Processing time over time
fig = go.Figure()
fig.add_trace(go.Scatter(
x=df['timestamp'],
y=df['processing_time'],
mode='lines+markers',
name='Processing Time (s)',
line=dict(color='blue')
))
fig.update_layout(
title='Search Performance Over Time',
xaxis_title='Time',
yaxis_title='Processing Time (seconds)',
hovermode='x unified'
)
return fig
def get_confidence_distribution(self):
"""Create confidence score distribution"""
if not self.search_history:
return None
df = pd.DataFrame(self.search_history)
fig = px.histogram(
df,
x='confidence',
nbins=20,
title='Confidence Score Distribution',
labels={'confidence': 'Confidence Score', 'count': 'Frequency'}
)
return fig
def clear_history(self):
"""Clear search history"""
self.search_history = []
self.performance_metrics = {
'queries': 0,
'avg_processing_time': 0,
'avg_confidence': 0,
'total_results': 0
}
return "Search history cleared!", pd.DataFrame(), None, None
# Initialize the interface
search_interface = GradioSearchInterface()
# Create the Gradio interface
def create_gradio_app():
with gr.Blocks(
title="Advanced Multi-Source Search Agent",
theme=gr.themes.Soft(),
css="""
.gradio-container {
max-width: 1200px !important;
}
.main-header {
text-align: center;
color: #2563eb;
margin-bottom: 20px;
}
"""
) as app:
gr.Markdown(
"""
# 🔍 Advanced Multi-Source Search Agent
This intelligent search agent combines multiple search engines, semantic analysis, and fact verification
to provide comprehensive and reliable answers to your queries.
**Features:**
- Multi-source search (Google, DuckDuckGo)
- Intent classification and query expansion
- Semantic ranking and fact verification
- Real-time performance analytics
""",
elem_classes=["main-header"]
)
with gr.Tab("🔍 Search"):
with gr.Row():
with gr.Column(scale=3):
query_input = gr.Textbox(
label="Search Query",
placeholder="Enter your search query here...",
lines=2
)
intent_dropdown = gr.Dropdown(
choices=["Auto-detect"] + [intent.value.title() for intent in QueryIntent],
value="Auto-detect",
label="Query Intent (Optional)",
info="Override automatic intent detection"
)
search_btn = gr.Button("🔍 Search", variant="primary", size="lg")
with gr.Column(scale=1):
gr.Markdown("### Quick Stats")
stats_display = gr.Markdown("No searches yet.")
with gr.Tab("📋 Results"):
with gr.Row():
with gr.Column():
answer_output = gr.Markdown(label="Answer")
with gr.Row():
with gr.Column():
summary_output = gr.Markdown(label="Search Summary")
with gr.Column():
facts_output = gr.Markdown(label="Verified Facts")
with gr.Row():
results_table = gr.DataFrame(
label="Top Search Results",
interactive=False,
wrap=True
)
with gr.Tab("📊 Analytics"):
with gr.Row():
with gr.Column():
performance_chart = gr.Plot(label="Performance Over Time")
with gr.Column():
confidence_chart = gr.Plot(label="Confidence Distribution")
with gr.Row():
history_table = gr.DataFrame(
label="Search History",
interactive=False
)
with gr.Tab("⚙️ System"):
with gr.Row():
with gr.Column():
gr.Markdown("### System Information")
system_info = gr.Markdown(
"""
**Search Sources:** Google, DuckDuckGo
**Embedding Model:** all-MiniLM-L6-v2
**LLM:** GPT-4o-mini (Azure)
**Semantic Search:** FAISS
**Caching:** Redis (if available)
"""
)
with gr.Column():
gr.Markdown("### Controls")
clear_btn = gr.Button("🗑️ Clear History", variant="secondary")
error_log = gr.Textbox(
label="Error Log",
lines=5,
interactive=False
)
# Event handlers
def update_stats():
metrics = search_interface.performance_metrics
return f"""
**Total Queries:** {metrics['queries']}
**Avg Processing Time:** {metrics['avg_processing_time']:.2f}s
**Avg Confidence:** {metrics['avg_confidence']:.1%}
**Total Results:** {metrics['total_results']}
"""
def search_and_update(query, intent):
# Perform search
answer, summary, results_df, facts, errors = search_interface.process_search(query, intent)
# Update stats
stats = update_stats()
# Update history and charts
history_df = search_interface.get_search_history()
perf_chart = search_interface.get_performance_chart()
conf_chart = search_interface.get_confidence_distribution()
return (
answer, # answer_output
summary, # summary_output
results_df, # results_table
facts, # facts_output
errors, # error_log
stats, # stats_display
history_df, # history_table
perf_chart, # performance_chart
conf_chart # confidence_chart
)
def clear_and_update():
message, empty_df, empty_chart1, empty_chart2 = search_interface.clear_history()
stats = update_stats()
return message, empty_df, empty_chart1, empty_chart2, stats
# Connect events
search_btn.click(
fn=search_and_update,
inputs=[query_input, intent_dropdown],
outputs=[
answer_output,
summary_output,
results_table,
facts_output,
error_log,
stats_display,
history_table,
performance_chart,
confidence_chart
]
)
query_input.submit(
fn=search_and_update,
inputs=[query_input, intent_dropdown],
outputs=[
answer_output,
summary_output,
results_table,
facts_output,
error_log,
stats_display,
history_table,
performance_chart,
confidence_chart
]
)
clear_btn.click(
fn=clear_and_update,
outputs=[error_log, history_table, performance_chart, confidence_chart, stats_display]
)
# Load initial history on startup
app.load(
fn=lambda: (search_interface.get_search_history(), update_stats()),
outputs=[history_table, stats_display]
)
return app
# Launch the application
if __name__ == "__main__":
# Create and launch the Gradio app
app = create_gradio_app()
# Launch with custom settings
app.launch(
server_name="0.0.0.0", # Allow external access
server_port=7860, # Default Gradio port
share=False, # Set to True to create public link
debug=True, # Enable debug mode
show_error=True, # Show detailed errors
favicon_path=None, # Add custom favicon if desired
auth=None, # Add authentication if needed: ("username", "password")
)