|
|
"""
|
|
|
Streamlit App for Multi-Agent Research Assistant
|
|
|
================================================
|
|
|
Beautiful UI with real-time progress tracking and interactive results
|
|
|
"""
|
|
|
|
|
|
import streamlit as st
|
|
|
import sys
|
|
|
import os
|
|
|
from datetime import datetime
|
|
|
import json
|
|
|
|
|
|
|
|
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
|
|
|
|
|
from multi_agent_system import MultiAgentSystem
|
|
|
|
|
|
|
|
|
st.set_page_config(
|
|
|
page_title="Multi-Agent Research Assistant",
|
|
|
page_icon="π€",
|
|
|
layout="wide",
|
|
|
initial_sidebar_state="expanded"
|
|
|
)
|
|
|
|
|
|
|
|
|
st.markdown("""
|
|
|
<style>
|
|
|
.main-header {
|
|
|
font-size: 3rem;
|
|
|
font-weight: bold;
|
|
|
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
|
|
-webkit-background-clip: text;
|
|
|
-webkit-text-fill-color: transparent;
|
|
|
text-align: center;
|
|
|
margin-bottom: 1rem;
|
|
|
}
|
|
|
.sub-header {
|
|
|
text-align: center;
|
|
|
color: #666;
|
|
|
margin-bottom: 2rem;
|
|
|
}
|
|
|
.agent-box {
|
|
|
padding: 1rem;
|
|
|
border-radius: 10px;
|
|
|
margin: 1rem 0;
|
|
|
border-left: 4px solid;
|
|
|
}
|
|
|
.researcher-box {
|
|
|
background-color: #e3f2fd;
|
|
|
border-color: #2196f3;
|
|
|
}
|
|
|
.analyst-box {
|
|
|
background-color: #f3e5f5;
|
|
|
border-color: #9c27b0;
|
|
|
}
|
|
|
.writer-box {
|
|
|
background-color: #e8f5e9;
|
|
|
border-color: #4caf50;
|
|
|
}
|
|
|
.critic-box {
|
|
|
background-color: #fff3e0;
|
|
|
border-color: #ff9800;
|
|
|
}
|
|
|
.metric-card {
|
|
|
background: white;
|
|
|
padding: 1.5rem;
|
|
|
border-radius: 10px;
|
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
|
text-align: center;
|
|
|
}
|
|
|
.stButton>button {
|
|
|
width: 100%;
|
|
|
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
|
|
color: white;
|
|
|
border: none;
|
|
|
padding: 0.75rem;
|
|
|
font-size: 1.1rem;
|
|
|
font-weight: bold;
|
|
|
border-radius: 8px;
|
|
|
transition: transform 0.2s;
|
|
|
}
|
|
|
.stButton>button:hover {
|
|
|
transform: translateY(-2px);
|
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
|
|
}
|
|
|
</style>
|
|
|
""", unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
if 'system' not in st.session_state:
|
|
|
st.session_state.system = None
|
|
|
if 'research_history' not in st.session_state:
|
|
|
st.session_state.research_history = []
|
|
|
if 'current_result' not in st.session_state:
|
|
|
st.session_state.current_result = None
|
|
|
|
|
|
|
|
|
def initialize_system(token):
|
|
|
"""Initialize the multi-agent system"""
|
|
|
try:
|
|
|
with st.spinner("π€ Initializing AI agents..."):
|
|
|
system = MultiAgentSystem(token=token, max_iterations=2)
|
|
|
st.success("β
System initialized successfully!")
|
|
|
return system
|
|
|
except Exception as e:
|
|
|
st.error(f"β Initialization failed: {str(e)}")
|
|
|
return None
|
|
|
|
|
|
|
|
|
def display_progress(step):
|
|
|
"""Display progress bar based on current step"""
|
|
|
steps = {
|
|
|
"researcher": (25, "π Researching..."),
|
|
|
"analyst": (50, "π Analyzing..."),
|
|
|
"writer": (75, "βοΈ Writing report..."),
|
|
|
"critic": (90, "π― Quality check..."),
|
|
|
"complete": (100, "β
Complete!")
|
|
|
}
|
|
|
|
|
|
if step in steps:
|
|
|
progress, text = steps[step]
|
|
|
st.progress(progress)
|
|
|
st.info(text)
|
|
|
|
|
|
|
|
|
def display_agent_output(agent_name, content, box_class):
|
|
|
"""Display agent output in a styled box"""
|
|
|
st.markdown(f'<div class="agent-box {box_class}">', unsafe_allow_html=True)
|
|
|
st.markdown(f"### {agent_name}")
|
|
|
st.markdown(content)
|
|
|
st.markdown('</div>', unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
st.markdown('<h1 class="main-header">π€ Multi-Agent Research Assistant</h1>', unsafe_allow_html=True)
|
|
|
st.markdown('<p class="sub-header">Powered by LangGraph & Advanced AI Agents</p>', unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
with st.sidebar:
|
|
|
st.header("βοΈ Configuration")
|
|
|
|
|
|
|
|
|
token = st.text_input(
|
|
|
"HuggingFace API Token",
|
|
|
type="password",
|
|
|
help="Enter your HuggingFace API token"
|
|
|
)
|
|
|
|
|
|
|
|
|
if st.button("π Initialize System"):
|
|
|
if token:
|
|
|
st.session_state.system = initialize_system(token)
|
|
|
else:
|
|
|
st.error("Please enter your HuggingFace token")
|
|
|
|
|
|
st.divider()
|
|
|
|
|
|
|
|
|
st.header("π System Status")
|
|
|
if st.session_state.system:
|
|
|
st.success("π’ System Active")
|
|
|
st.metric("Research Queries", len(st.session_state.research_history))
|
|
|
else:
|
|
|
st.warning("π΄ System Inactive")
|
|
|
|
|
|
st.divider()
|
|
|
|
|
|
|
|
|
st.header("π‘ Example Questions")
|
|
|
examples = [
|
|
|
"what is 2+2",
|
|
|
"calculate (15*3)+7",
|
|
|
"what is artificial intelligence",
|
|
|
"what is machine learning",
|
|
|
"what is python programming"
|
|
|
]
|
|
|
|
|
|
for example in examples:
|
|
|
if st.button(f"π {example}", key=f"ex_{example}"):
|
|
|
st.session_state.example_question = example
|
|
|
|
|
|
st.divider()
|
|
|
|
|
|
|
|
|
st.header("π Research History")
|
|
|
if st.session_state.research_history:
|
|
|
for i, item in enumerate(reversed(st.session_state.research_history[-5:])):
|
|
|
with st.expander(f"π {item['question'][:30]}..."):
|
|
|
st.write(f"**Time:** {item['timestamp']}")
|
|
|
st.write(f"**Score:** {item['score']}/10")
|
|
|
else:
|
|
|
st.info("No research history yet")
|
|
|
|
|
|
st.divider()
|
|
|
|
|
|
|
|
|
if st.button("ποΈ Clear History"):
|
|
|
st.session_state.research_history = []
|
|
|
st.session_state.current_result = None
|
|
|
st.rerun()
|
|
|
|
|
|
|
|
|
if not st.session_state.system:
|
|
|
|
|
|
col1, col2, col3 = st.columns([1, 2, 1])
|
|
|
with col2:
|
|
|
st.info("π Please initialize the system using the sidebar")
|
|
|
|
|
|
st.markdown("### π Features")
|
|
|
features = [
|
|
|
"π **Smart Research**: Automatic tool selection and execution",
|
|
|
"π **Deep Analysis**: AI-powered insight extraction",
|
|
|
"βοΈ **Professional Reports**: Well-structured documentation",
|
|
|
"π― **Quality Assurance**: Automated review and refinement",
|
|
|
"π **Iterative Improvement**: Multiple revision cycles"
|
|
|
]
|
|
|
for feature in features:
|
|
|
st.markdown(feature)
|
|
|
|
|
|
st.markdown("### π οΈ Technology Stack")
|
|
|
tech = [
|
|
|
"LangGraph for agent orchestration",
|
|
|
"Meta Llama 3.1 8B Instruct",
|
|
|
"Pydantic for structured outputs",
|
|
|
"NumExpr for safe calculations"
|
|
|
]
|
|
|
for item in tech:
|
|
|
st.markdown(f"- {item}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
st.markdown("## π Ask Your Question")
|
|
|
|
|
|
|
|
|
default_question = st.session_state.get('example_question', '')
|
|
|
if default_question:
|
|
|
st.session_state.example_question = ''
|
|
|
|
|
|
question = st.text_input(
|
|
|
"Enter your research question:",
|
|
|
value=default_question,
|
|
|
placeholder="e.g., what is 2+2, what is artificial intelligence...",
|
|
|
key="question_input"
|
|
|
)
|
|
|
|
|
|
col1, col2 = st.columns([3, 1])
|
|
|
with col1:
|
|
|
research_button = st.button("π Start Research", type="primary")
|
|
|
with col2:
|
|
|
clear_button = st.button("π Clear Results")
|
|
|
|
|
|
if clear_button:
|
|
|
st.session_state.current_result = None
|
|
|
st.rerun()
|
|
|
|
|
|
if research_button and question:
|
|
|
|
|
|
progress_container = st.container()
|
|
|
result_container = st.container()
|
|
|
|
|
|
with progress_container:
|
|
|
st.markdown("### π Research in Progress")
|
|
|
progress_bar = st.progress(0)
|
|
|
status_text = st.empty()
|
|
|
|
|
|
|
|
|
import io
|
|
|
from contextlib import redirect_stdout
|
|
|
|
|
|
output_capture = io.StringIO()
|
|
|
|
|
|
try:
|
|
|
with redirect_stdout(output_capture):
|
|
|
|
|
|
final_state = st.session_state.system.research(question)
|
|
|
|
|
|
|
|
|
progress_bar.progress(100)
|
|
|
status_text.success("β
Research Complete!")
|
|
|
|
|
|
if final_state:
|
|
|
|
|
|
st.session_state.current_result = final_state
|
|
|
|
|
|
|
|
|
st.session_state.research_history.append({
|
|
|
'question': question,
|
|
|
'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
|
'score': final_state['critique_output'].score
|
|
|
})
|
|
|
|
|
|
st.rerun()
|
|
|
|
|
|
except Exception as e:
|
|
|
progress_bar.progress(100)
|
|
|
status_text.error(f"β Error: {str(e)}")
|
|
|
|
|
|
|
|
|
if st.session_state.current_result:
|
|
|
st.markdown("---")
|
|
|
result = st.session_state.current_result
|
|
|
|
|
|
|
|
|
st.markdown("## π Research Metrics")
|
|
|
col1, col2, col3, col4 = st.columns(4)
|
|
|
|
|
|
with col1:
|
|
|
st.markdown('<div class="metric-card">', unsafe_allow_html=True)
|
|
|
st.metric("Quality Score", f"{result['critique_output'].score}/10")
|
|
|
st.markdown('</div>', unsafe_allow_html=True)
|
|
|
|
|
|
with col2:
|
|
|
st.markdown('<div class="metric-card">', unsafe_allow_html=True)
|
|
|
st.metric("Iterations", result['report_iterations'])
|
|
|
st.markdown('</div>', unsafe_allow_html=True)
|
|
|
|
|
|
with col3:
|
|
|
st.markdown('<div class="metric-card">', unsafe_allow_html=True)
|
|
|
st.metric("Confidence", f"{result['research_output'].confidence*100:.0f}%")
|
|
|
st.markdown('</div>', unsafe_allow_html=True)
|
|
|
|
|
|
with col4:
|
|
|
st.markdown('<div class="metric-card">', unsafe_allow_html=True)
|
|
|
sources = ", ".join(result['research_output'].sources_used)
|
|
|
st.metric("Sources", len(result['research_output'].sources_used))
|
|
|
st.caption(sources)
|
|
|
st.markdown('</div>', unsafe_allow_html=True)
|
|
|
|
|
|
st.markdown("---")
|
|
|
|
|
|
|
|
|
tab1, tab2, tab3, tab4 = st.tabs(["π Final Report", "π Research", "π Analysis", "π― Quality Review"])
|
|
|
|
|
|
with tab1:
|
|
|
report = result['report_output']
|
|
|
st.markdown(f"# {report.title}")
|
|
|
st.markdown(report.content)
|
|
|
|
|
|
|
|
|
st.download_button(
|
|
|
label="π₯ Download Report",
|
|
|
data=report.content,
|
|
|
file_name=f"research_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt",
|
|
|
mime="text/plain"
|
|
|
)
|
|
|
|
|
|
with tab2:
|
|
|
research = result['research_output']
|
|
|
display_agent_output(
|
|
|
"π Research Agent",
|
|
|
f"""
|
|
|
**Answer:** {research.answer}
|
|
|
|
|
|
**Sources Used:** {', '.join(research.sources_used)}
|
|
|
|
|
|
**Confidence:** {research.confidence*100:.1f}%
|
|
|
""",
|
|
|
"researcher-box"
|
|
|
)
|
|
|
|
|
|
with tab3:
|
|
|
analysis = result['analysis_output']
|
|
|
display_agent_output(
|
|
|
"π Analysis Agent",
|
|
|
f"""
|
|
|
**Key Points:**
|
|
|
{chr(10).join(f'β’ {point}' for point in analysis.key_points)}
|
|
|
|
|
|
**Implications:**
|
|
|
{analysis.implications}
|
|
|
""",
|
|
|
"analyst-box"
|
|
|
)
|
|
|
|
|
|
with tab4:
|
|
|
critique = result['critique_output']
|
|
|
|
|
|
|
|
|
score = critique.score
|
|
|
color = "π’" if score >= 8 else "π‘" if score >= 6 else "π΄"
|
|
|
|
|
|
st.markdown(f"### {color} Quality Score: {score}/10")
|
|
|
st.progress(score / 10)
|
|
|
|
|
|
st.markdown(f"""
|
|
|
**Status:** {"β
Approved" if not critique.needs_revision else "π Needs Revision"}
|
|
|
""")
|
|
|
|
|
|
|
|
|
st.markdown("---")
|
|
|
if st.button("π¦ Export Full Research Data (JSON)"):
|
|
|
export_data = {
|
|
|
'question': result['question'],
|
|
|
'timestamp': datetime.now().isoformat(),
|
|
|
'research': {
|
|
|
'answer': result['research_output'].answer,
|
|
|
'sources': result['research_output'].sources_used,
|
|
|
'confidence': result['research_output'].confidence
|
|
|
},
|
|
|
'analysis': {
|
|
|
'key_points': result['analysis_output'].key_points,
|
|
|
'implications': result['analysis_output'].implications
|
|
|
},
|
|
|
'report': {
|
|
|
'title': result['report_output'].title,
|
|
|
'content': result['report_output'].content
|
|
|
},
|
|
|
'quality': {
|
|
|
'score': result['critique_output'].score,
|
|
|
'needs_revision': result['critique_output'].needs_revision
|
|
|
}
|
|
|
}
|
|
|
|
|
|
st.download_button(
|
|
|
label="π₯ Download JSON",
|
|
|
data=json.dumps(export_data, indent=2),
|
|
|
file_name=f"research_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
|
|
|
mime="application/json"
|
|
|
)
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
main() |