Merge branch 'main' into update-docs

This commit is contained in:
Shubham Saboo 2024-12-25 22:12:07 -06:00 committed by GitHub
commit d0785ccfc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 1363 additions and 6 deletions

View file

@ -37,12 +37,14 @@ A curated collection of awesome LLM apps built with RAG and AI agents. This repo
- [💼 AI Customer Support Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_customer_support_agent)
- [📈 AI Investment Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_investment_agent)
- [👨‍⚖️ AI Legal Agent Team](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_legal_agent_team)
- [💼 AI Recruitment Agent Team](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_recruitment_agent_team)
- [👨‍💼 AI Services Agency](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_services_agency)
- [🏋️‍♂️ AI Health & Fitness Planner Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_health_fitness_agent)
- [📈 AI Startup Trend Analysis Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_startup_trend_analysis_agent)
- [🗞️ AI Journalist Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_journalist_agent)
- [💲 AI Finance Agent Team](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_finance_agent_team)
- [💰 AI Personal Finance Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_personal_finance_agent)
- [🩻 AI Medical Scan Diagnosis Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_medical_imaging_agent)
- [🛫 AI Travel Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_travel_agent)
- [🎬 AI Movie Production Agent](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/ai_movie_production_agent)
- [📰 Multi-Agent AI Researcher](https://github.com/Shubhamsaboo/awesome-llm-apps/tree/main/ai_agent_tutorials/multi_agent_researcher)

View file

@ -2,7 +2,7 @@
from textwrap import dedent
from phi.assistant import Assistant
from phi.tools.serpapi_tools import SerpApiTools
from phi.tools.newspaper_toolkit import NewspaperToolkit
from phi.tools.newspaper4k import Newspaper4k as NewspaperToolkit
import streamlit as st
from phi.llm.openai import OpenAIChat

View file

@ -2,5 +2,5 @@ streamlit
phidata
openai
google-search-results
newspaper3k
newspaper4k
lxml_html_clean

View file

@ -1,5 +1,6 @@
phidata==2.5.33
streamlit==1.40.2
qdrant-client==1.12.1
openai
openai
pypdf
duckduckgo-search

View file

@ -0,0 +1,66 @@
# 🩻 Medical Imaging Diagnosis Agent
A Medical Imaging Diagnosis Agent build on phidata powered by Gemini 2.0 Flash Experimental that provides AI-assisted analysis of medical images of various scans. The agent acts as a medical imaging diagnosis expert to analyze various types of medical images and videos, providing detailed diagnostic insights and explanations.
## Features
- **Comprehensive Image Analysis**
- Image Type Identification (X-ray, MRI, CT scan, ultrasound)
- Anatomical Region Detection
- Key Findings and Observations
- Potential Abnormalities Detection
- Image Quality Assessment
- Research and Reference
## How to Run
1. **Setup Environment**
```bash
# Clone the repository
git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git
cd ai_agent_tutorials/ai_medical_imaging_agent
# Install dependencies
pip install -r requirements.txt
```
2. **Configure API Keys**
- Get Google API key from [Google AI Studio](https://aistudio.google.com)
3. **Run the Application**
```bash
streamlit run ai_medical_imaging.py
```
## Analysis Components
- **Image Type and Region**
- Identifies imaging modality
- Specifies anatomical region
- **Key Findings**
- Systematic listing of observations
- Detailed appearance descriptions
- Abnormality highlighting
- **Diagnostic Assessment**
- Potential diagnoses ranking
- Differential diagnoses
- Severity assessment
- **Patient-Friendly Explanations**
- Simplified terminology
- Detailed first-principles explanations
- Visual reference points
## Notes
- Uses Gemini 2.0 Flash for analysis
- Requires stable internet connection
- API usage costs apply
- For educational and development purposes only
- Not a replacement for professional medical diagnosis
## Disclaimer
This tool is for educational and informational purposes only. All analyses should be reviewed by qualified healthcare professionals. Do not make medical decisions based solely on this analysis.

View file

@ -0,0 +1,157 @@
import os
from PIL import Image
from phi.agent import Agent
from phi.model.google import Gemini
import streamlit as st
from phi.tools.duckduckgo import DuckDuckGo
if "GOOGLE_API_KEY" not in st.session_state:
st.session_state.GOOGLE_API_KEY = None
with st.sidebar:
st.title(" Configuration")
if not st.session_state.GOOGLE_API_KEY:
api_key = st.text_input(
"Enter your Google API Key:",
type="password"
)
st.caption(
"Get your API key from [Google AI Studio]"
"(https://aistudio.google.com/apikey) 🔑"
)
if api_key:
st.session_state.GOOGLE_API_KEY = api_key
st.success("API Key saved!")
st.rerun()
else:
st.success("API Key is configured")
if st.button("🔄 Reset API Key"):
st.session_state.GOOGLE_API_KEY = None
st.rerun()
st.info(
"This tool provides AI-powered analysis of medical imaging data using "
"advanced computer vision and radiological expertise."
)
st.warning(
"⚠DISCLAIMER: This tool is for educational and informational purposes only. "
"All analyses should be reviewed by qualified healthcare professionals. "
"Do not make medical decisions based solely on this analysis."
)
medical_agent = Agent(
model=Gemini(
api_key=st.session_state.GOOGLE_API_KEY,
id="gemini-2.0-flash-exp"
),
tools=[DuckDuckGo()],
markdown=True
) if st.session_state.GOOGLE_API_KEY else None
if not medical_agent:
st.warning("Please configure your API key in the sidebar to continue")
# Medical Analysis Query
query = """
You are a highly skilled medical imaging expert with extensive knowledge in radiology and diagnostic imaging. Analyze the patient's medical image and structure your response as follows:
### 1. Image Type & Region
- Specify imaging modality (X-ray/MRI/CT/Ultrasound/etc.)
- Identify the patient's anatomical region and positioning
- Comment on image quality and technical adequacy
### 2. Key Findings
- List primary observations systematically
- Note any abnormalities in the patient's imaging with precise descriptions
- Include measurements and densities where relevant
- Describe location, size, shape, and characteristics
- Rate severity: Normal/Mild/Moderate/Severe
### 3. Diagnostic Assessment
- Provide primary diagnosis with confidence level
- List differential diagnoses in order of likelihood
- Support each diagnosis with observed evidence from the patient's imaging
- Note any critical or urgent findings
### 4. Patient-Friendly Explanation
- Explain the findings in simple, clear language that the patient can understand
- Avoid medical jargon or provide clear definitions
- Include visual analogies if helpful
- Address common patient concerns related to these findings
### 5. Research Context
IMPORTANT: Use the DuckDuckGo search tool to:
- Find recent medical literature about similar cases
- Search for standard treatment protocols
- Provide a list of relevant medical links of them too
- Research any relevant technological advances
- Include 2-3 key references to support your analysis
Format your response using clear markdown headers and bullet points. Be concise yet thorough.
"""
st.title("🏥 Medical Imaging Diagnosis Agent")
st.write("Upload a medical image for professional analysis")
# Create containers for better organization
upload_container = st.container()
image_container = st.container()
analysis_container = st.container()
with upload_container:
uploaded_file = st.file_uploader(
"Upload Medical Image",
type=["jpg", "jpeg", "png", "dicom"],
help="Supported formats: JPG, JPEG, PNG, DICOM"
)
if uploaded_file is not None:
with image_container:
# Center the image using columns
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
image = Image.open(uploaded_file)
# Calculate aspect ratio for resizing
width, height = image.size
aspect_ratio = width / height
new_width = 500
new_height = int(new_width / aspect_ratio)
resized_image = image.resize((new_width, new_height))
st.image(
resized_image,
caption="Uploaded Medical Image",
use_container_width=True
)
analyze_button = st.button(
"🔍 Analyze Image",
type="primary",
use_container_width=True
)
with analysis_container:
if analyze_button:
image_path = "temp_medical_image.png"
with open(image_path, "wb") as f:
f.write(uploaded_file.getbuffer())
with st.spinner("🔄 Analyzing image... Please wait."):
try:
response = medical_agent.run(query, images=[image_path])
st.markdown("### 📋 Analysis Results")
st.markdown("---")
st.markdown(response.content)
st.markdown("---")
st.caption(
"Note: This analysis is generated by AI and should be reviewed by "
"a qualified healthcare professional."
)
except Exception as e:
st.error(f"Analysis error: {e}")
finally:
if os.path.exists(image_path):
os.remove(image_path)
else:
st.info("👆 Please upload a medical image to begin analysis")

View file

@ -0,0 +1,5 @@
streamlit==1.40.2
phidata==2.7.3
Pillow==10.0.0
duckduckgo-search==6.4.1
google-generativeai==0.8.3

View file

@ -0,0 +1,101 @@
# 💼 AI Recruitment Agent Team
A Streamlit application that simulates a full-service recruitment team using multiple AI agents to automate and streamline the hiring process. Each agent represents a different recruitment specialist role - from resume analysis and candidate evaluation to interview scheduling and communication - working together to provide comprehensive hiring solutions. The system combines the expertise of technical recruiters, HR coordinators, and scheduling specialists into a cohesive automated workflow.
## Features
#### Specialized AI Agents
- Technical Recruiter Agent: Analyzes resumes and evaluates technical skills
- Communication Agent: Handles professional email correspondence
- Scheduling Coordinator Agent: Manages interview scheduling and coordination
- Each agent has specific expertise and collaborates for comprehensive recruitment
#### End-to-End Recruitment Process
- Automated resume screening and analysis
- Role-specific technical evaluation
- Professional candidate communication
- Automated interview scheduling
- Integrated feedback system
## Important Things to do before running the application
- Create/Use a new Gmail account for the recruiter
- Enable 2-Step Verification and generate an App Password for the Gmail account
- The App Password is a 16 digit code (use without spaces) that should be generated here - [Google App Password](https://support.google.com/accounts/answer/185833?hl=en) Please go through the steps to generate the password - it will of the format - 'afec wejf awoj fwrv' (remove the spaces and enter it in the streamlit app)
- Create/ Use a Zoom account and go to the Zoom App Marketplace to get the API credentials :
[Zoom Marketplace](https://marketplace.zoom.us)
- Go to Developer Dashboard and create a new app - Select Server to Server OAuth and get the credentials, You see 3 credentials - Client ID, Client Secret and Account ID
- After that, you need to add a few scopes to the app - so that the zoom link of the candidate is sent and created through the mail.
- The Scopes are meeting:write:invite_links:admin, meeting:write:meeting:admin, meeting:write:meeting:master, meeting:write:invite_links:master, meeting:write:open_app:admin, user:read:email:admin, user:read:list_users:admin, billing:read:user_entitlement:admin, dashboard:read:list_meeting_participants:admin [last 3 are optional]
## How to Run
1. **Setup Environment**
```bash
# Clone the repository
git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git
cd ai_agent_tutorials/ai_recruitment_agent_team
# Install dependencies
pip install -r requirements.txt
```
2. **Configure API Keys**
- OpenAI API key for GPT-4o access
- Zoom API credentials (Account ID, Client ID, Client Secret)
- Email App Password of Recruiter's Email
3. **Run the Application**
```bash
streamlit run ai_recruitment_agent_team.py
```
## System Components
- **Resume Analyzer Agent**
- Skills matching algorithm
- Experience verification
- Technical assessment
- Selection decision making
- **Email Communication Agent**
- Professional email drafting
- Automated notifications
- Feedback communication
- Follow-up management
- **Interview Scheduler Agent**
- Zoom meeting coordination
- Calendar management
- Timezone handling
- Reminder system
- **Candidate Experience**
- Simple upload interface
- Real-time feedback
- Clear communication
- Streamlined process
## Technical Stack
- **Framework**: Phidata
- **Model**: OpenAI GPT-4o
- **Integration**: Zoom API, EmailTools Tool from Phidata
- **PDF Processing**: PyPDF2
- **Time Management**: pytz
- **State Management**: Streamlit Session State
## Disclaimer
This tool is designed to assist in the recruitment process but should not completely replace human judgment in hiring decisions. All automated decisions should be reviewed by human recruiters for final approval.
## Future Enhancements
- Integration with ATS systems
- Advanced candidate scoring
- Video interview capabilities
- Skills assessment integration
- Multi-language support

View file

@ -0,0 +1,521 @@
from typing import Literal, Tuple, Dict, Optional
import os
import time
import json
import requests
import PyPDF2
from datetime import datetime, timedelta
import pytz
import streamlit as st
from phi.agent import Agent
from phi.model.openai import OpenAIChat
from phi.tools.email import EmailTools
from phi.tools.zoom import ZoomTool
from phi.utils.log import logger
from streamlit_pdf_viewer import pdf_viewer
class CustomZoomTool(ZoomTool):
def __init__(self, *, account_id: Optional[str] = None, client_id: Optional[str] = None, client_secret: Optional[str] = None, name: str = "zoom_tool"):
super().__init__(account_id=account_id, client_id=client_id, client_secret=client_secret, name=name)
self.token_url = "https://zoom.us/oauth/token"
self.access_token = None
self.token_expires_at = 0
def get_access_token(self) -> str:
if self.access_token and time.time() < self.token_expires_at:
return str(self.access_token)
headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {"grant_type": "account_credentials", "account_id": self.account_id}
try:
response = requests.post(self.token_url, headers=headers, data=data, auth=(self.client_id, self.client_secret))
response.raise_for_status()
token_info = response.json()
self.access_token = token_info["access_token"]
expires_in = token_info["expires_in"]
self.token_expires_at = time.time() + expires_in - 60
self._set_parent_token(str(self.access_token))
return str(self.access_token)
except requests.RequestException as e:
logger.error(f"Error fetching access token: {e}")
return ""
def _set_parent_token(self, token: str) -> None:
"""Helper method to set the token in the parent ZoomTool class"""
if token:
self._ZoomTool__access_token = token
# Role requirements as a constant dictionary
ROLE_REQUIREMENTS: Dict[str, str] = {
"ai_ml_engineer": """
Required Skills:
- Python, PyTorch/TensorFlow
- Machine Learning algorithms and frameworks
- Deep Learning and Neural Networks
- Data preprocessing and analysis
- MLOps and model deployment
- RAG, LLM, Finetuning and Prompt Engineering
""",
"frontend_engineer": """
Required Skills:
- React/Vue.js/Angular
- HTML5, CSS3, JavaScript/TypeScript
- Responsive design
- State management
- Frontend testing
""",
"backend_engineer": """
Required Skills:
- Python/Java/Node.js
- REST APIs
- Database design and management
- System architecture
- Cloud services (AWS/GCP/Azure)
- Kubernetes, Docker, CI/CD
"""
}
def init_session_state() -> None:
"""Initialize only necessary session state variables."""
defaults = {
'candidate_email': "", 'openai_api_key': "", 'resume_text': "", 'analysis_complete': False,
'is_selected': False, 'zoom_account_id': "", 'zoom_client_id': "", 'zoom_client_secret': "",
'email_sender': "", 'email_passkey': "", 'company_name': "", 'current_pdf': None
}
for key, value in defaults.items():
if key not in st.session_state:
st.session_state[key] = value
def create_resume_analyzer() -> Agent:
"""Creates and returns a resume analysis agent."""
if not st.session_state.openai_api_key:
st.error("Please enter your OpenAI API key first.")
return None
return Agent(
model=OpenAIChat(
id="gpt-4o",
api_key=st.session_state.openai_api_key
),
description="You are an expert technical recruiter who analyzes resumes.",
instructions=[
"Analyze the resume against the provided job requirements",
"Be lenient with AI/ML candidates who show strong potential",
"Consider project experience as valid experience",
"Value hands-on experience with key technologies",
"Return a JSON response with selection decision and feedback"
],
markdown=True
)
def create_email_agent() -> Agent:
return Agent(
model=OpenAIChat(
id="gpt-4o",
api_key=st.session_state.openai_api_key
),
tools=[EmailTools(
receiver_email=st.session_state.candidate_email,
sender_email=st.session_state.email_sender,
sender_name=st.session_state.company_name,
sender_passkey=st.session_state.email_passkey
)],
description="You are a professional recruitment coordinator handling email communications.",
instructions=[
"Draft and send professional recruitment emails",
"Act like a human writing an email and use all lowercase letters",
"Maintain a friendly yet professional tone",
"Always end emails with exactly: 'best,\nthe ai recruiting team'",
"Never include the sender's or receiver's name in the signature",
f"The name of the company is '{st.session_state.company_name}'"
],
markdown=True,
show_tool_calls=True
)
def create_scheduler_agent() -> Agent:
zoom_tools = CustomZoomTool(
account_id=st.session_state.zoom_account_id,
client_id=st.session_state.zoom_client_id,
client_secret=st.session_state.zoom_client_secret
)
return Agent(
name="Interview Scheduler",
model=OpenAIChat(
id="gpt-4o",
api_key=st.session_state.openai_api_key
),
tools=[zoom_tools],
description="You are an interview scheduling coordinator.",
instructions=[
"You are an expert at scheduling technical interviews using Zoom.",
"Schedule interviews during business hours (9 AM - 5 PM EST)",
"Create meetings with proper titles and descriptions",
"Ensure all meeting details are included in responses",
"Use ISO 8601 format for dates",
"Handle scheduling errors gracefully"
],
markdown=True,
show_tool_calls=True
)
def extract_text_from_pdf(pdf_file) -> str:
try:
pdf_reader = PyPDF2.PdfReader(pdf_file)
text = ""
for page in pdf_reader.pages:
text += page.extract_text()
return text
except Exception as e:
st.error(f"Error extracting PDF text: {str(e)}")
return ""
def analyze_resume(
resume_text: str,
role: Literal["ai_ml_engineer", "frontend_engineer", "backend_engineer"],
analyzer: Agent
) -> Tuple[bool, str]:
try:
response = analyzer.run(
f"""Please analyze this resume against the following requirements and provide your response in valid JSON format:
Role Requirements:
{ROLE_REQUIREMENTS[role]}
Resume Text:
{resume_text}
Your response must be a valid JSON object like this:
{{
"selected": true/false,
"feedback": "Detailed feedback explaining the decision",
"matching_skills": ["skill1", "skill2"],
"missing_skills": ["skill3", "skill4"],
"experience_level": "junior/mid/senior"
}}
Evaluation criteria:
1. Match at least 70% of required skills
2. Consider both theoretical knowledge and practical experience
3. Value project experience and real-world applications
4. Consider transferable skills from similar technologies
5. Look for evidence of continuous learning and adaptability
Important: Return ONLY the JSON object without any markdown formatting or backticks.
"""
)
assistant_message = next((msg.content for msg in response.messages if msg.role == 'assistant'), None)
if not assistant_message:
raise ValueError("No assistant message found in response.")
result = json.loads(assistant_message.strip())
if not isinstance(result, dict) or not all(k in result for k in ["selected", "feedback"]):
raise ValueError("Invalid response format")
return result["selected"], result["feedback"]
except (json.JSONDecodeError, ValueError) as e:
st.error(f"Error processing response: {str(e)}")
return False, f"Error analyzing resume: {str(e)}"
def send_selection_email(email_agent: Agent, to_email: str, role: str) -> None:
email_agent.run(
f"""
Send an email to {to_email} regarding their selection for the {role} position.
The email should:
1. Congratulate them on being selected
2. Explain the next steps in the process
3. Mention that they will receive interview details shortly
4. The name of the company is 'AI Recruiting Team'
"""
)
def send_rejection_email(email_agent: Agent, to_email: str, role: str, feedback: str) -> None:
"""
Send a rejection email with constructive feedback.
"""
email_agent.run(
f"""
Send an email to {to_email} regarding their application for the {role} position.
Use this specific style:
1. use all lowercase letters
2. be empathetic and human
3. mention specific feedback from: {feedback}
4. encourage them to upskill and try again
5. suggest some learning resources based on missing skills
6. end the email with exactly:
best,
the ai recruiting team
Do not include any names in the signature.
The tone should be like a human writing a quick but thoughtful email.
"""
)
def schedule_interview(scheduler: Agent, candidate_email: str, email_agent: Agent, role: str) -> None:
"""
Schedule interviews during business hours (9 AM - 5 PM IST).
"""
try:
# Get current time in IST
ist_tz = pytz.timezone('Asia/Kolkata')
current_time_ist = datetime.now(ist_tz)
tomorrow_ist = current_time_ist + timedelta(days=1)
interview_time = tomorrow_ist.replace(hour=11, minute=0, second=0, microsecond=0)
formatted_time = interview_time.strftime('%Y-%m-%dT%H:%M:%S')
meeting_response = scheduler.run(
f"""Schedule a 60-minute technical interview with these specifications:
- Title: '{role} Technical Interview'
- Date: {formatted_time}
- Timezone: IST (India Standard Time)
- Attendee: {candidate_email}
Important Notes:
- The meeting must be between 9 AM - 5 PM IST
- Use IST (UTC+5:30) timezone for all communications
- Include timezone information in the meeting details
"""
)
email_agent.run(
f"""Send an interview confirmation email with these details:
- Role: {role} position
- Meeting Details: {meeting_response}
Important:
- Clearly specify that the time is in IST (India Standard Time)
- Ask the candidate to join 5 minutes early
- Include timezone conversion link if possible
- Ask him to be confident and not so nervous and prepare well for the interview
"""
)
st.success("Interview scheduled successfully! Check your email for details.")
except Exception as e:
logger.error(f"Error scheduling interview: {str(e)}")
st.error("Unable to schedule interview. Please try again.")
def main() -> None:
st.title("AI Recruitment System")
init_session_state()
with st.sidebar:
st.header("Configuration")
# OpenAI Configuration
st.subheader("OpenAI Settings")
api_key = st.text_input("OpenAI API Key", type="password", value=st.session_state.openai_api_key, help="Get your API key from platform.openai.com")
if api_key: st.session_state.openai_api_key = api_key
st.subheader("Zoom Settings")
zoom_account_id = st.text_input("Zoom Account ID", type="password", value=st.session_state.zoom_account_id)
zoom_client_id = st.text_input("Zoom Client ID", type="password", value=st.session_state.zoom_client_id)
zoom_client_secret = st.text_input("Zoom Client Secret", type="password", value=st.session_state.zoom_client_secret)
st.subheader("Email Settings")
email_sender = st.text_input("Sender Email", value=st.session_state.email_sender, help="Email address to send from")
email_passkey = st.text_input("Email App Password", type="password", value=st.session_state.email_passkey, help="App-specific password for email")
company_name = st.text_input("Company Name", value=st.session_state.company_name, help="Name to use in email communications")
if zoom_account_id: st.session_state.zoom_account_id = zoom_account_id
if zoom_client_id: st.session_state.zoom_client_id = zoom_client_id
if zoom_client_secret: st.session_state.zoom_client_secret = zoom_client_secret
if email_sender: st.session_state.email_sender = email_sender
if email_passkey: st.session_state.email_passkey = email_passkey
if company_name: st.session_state.company_name = company_name
required_configs = {'OpenAI API Key': st.session_state.openai_api_key, 'Zoom Account ID': st.session_state.zoom_account_id,
'Zoom Client ID': st.session_state.zoom_client_id, 'Zoom Client Secret': st.session_state.zoom_client_secret,
'Email Sender': st.session_state.email_sender, 'Email Password': st.session_state.email_passkey,
'Company Name': st.session_state.company_name}
missing_configs = [k for k, v in required_configs.items() if not v]
if missing_configs:
st.warning(f"Please configure the following in the sidebar: {', '.join(missing_configs)}")
return
if not st.session_state.openai_api_key:
st.warning("Please enter your OpenAI API key in the sidebar to continue.")
return
role = st.selectbox("Select the role you're applying for:", ["ai_ml_engineer", "frontend_engineer", "backend_engineer"])
with st.expander("View Required Skills", expanded=True): st.markdown(ROLE_REQUIREMENTS[role])
# Add a "New Application" button before the resume upload
if st.button("📝 New Application"):
# Clear only the application-related states
keys_to_clear = ['resume_text', 'analysis_complete', 'is_selected', 'candidate_email', 'current_pdf']
for key in keys_to_clear:
if key in st.session_state:
st.session_state[key] = None if key == 'current_pdf' else ""
st.rerun()
resume_file = st.file_uploader("Upload your resume (PDF)", type=["pdf"], key="resume_uploader")
if resume_file is not None and resume_file != st.session_state.get('current_pdf'):
st.session_state.current_pdf = resume_file
st.session_state.resume_text = ""
st.session_state.analysis_complete = False
st.session_state.is_selected = False
st.rerun()
if resume_file:
st.subheader("Uploaded Resume")
col1, col2 = st.columns([4, 1])
with col1:
import tempfile, os
with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as tmp_file:
tmp_file.write(resume_file.read())
tmp_file_path = tmp_file.name
resume_file.seek(0)
try: pdf_viewer(tmp_file_path)
finally: os.unlink(tmp_file_path)
with col2:
st.download_button(label="📥 Download", data=resume_file, file_name=resume_file.name, mime="application/pdf")
# Process the resume text
if not st.session_state.resume_text:
with st.spinner("Processing your resume..."):
resume_text = extract_text_from_pdf(resume_file)
if resume_text:
st.session_state.resume_text = resume_text
st.success("Resume processed successfully!")
else:
st.error("Could not process the PDF. Please try again.")
# Email input with session state
email = st.text_input(
"Candidate's email address",
value=st.session_state.candidate_email,
key="email_input"
)
st.session_state.candidate_email = email
# Analysis and next steps
if st.session_state.resume_text and email and not st.session_state.analysis_complete:
if st.button("Analyze Resume"):
with st.spinner("Analyzing your resume..."):
resume_analyzer = create_resume_analyzer()
email_agent = create_email_agent() # Create email agent here
if resume_analyzer and email_agent:
print("DEBUG: Starting resume analysis")
is_selected, feedback = analyze_resume(
st.session_state.resume_text,
role,
resume_analyzer
)
print(f"DEBUG: Analysis complete - Selected: {is_selected}, Feedback: {feedback}")
if is_selected:
st.success("Congratulations! Your skills match our requirements.")
st.session_state.analysis_complete = True
st.session_state.is_selected = True
st.rerun()
else:
st.warning("Unfortunately, your skills don't match our requirements.")
st.write(f"Feedback: {feedback}")
# Send rejection email
with st.spinner("Sending feedback email..."):
try:
send_rejection_email(
email_agent=email_agent,
to_email=email,
role=role,
feedback=feedback
)
st.info("We've sent you an email with detailed feedback.")
except Exception as e:
logger.error(f"Error sending rejection email: {e}")
st.error("Could not send feedback email. Please try again.")
if st.session_state.get('analysis_complete') and st.session_state.get('is_selected', False):
st.success("Congratulations! Your skills match our requirements.")
st.info("Click 'Proceed with Application' to continue with the interview process.")
if st.button("Proceed with Application", key="proceed_button"):
print("DEBUG: Proceed button clicked") # Debug
with st.spinner("🔄 Processing your application..."):
try:
print("DEBUG: Creating email agent") # Debug
email_agent = create_email_agent()
print(f"DEBUG: Email agent created: {email_agent}") # Debug
print("DEBUG: Creating scheduler agent") # Debug
scheduler_agent = create_scheduler_agent()
print(f"DEBUG: Scheduler agent created: {scheduler_agent}") # Debug
# 3. Send selection email
with st.status("📧 Sending confirmation email...", expanded=True) as status:
print(f"DEBUG: Attempting to send email to {st.session_state.candidate_email}") # Debug
send_selection_email(
email_agent,
st.session_state.candidate_email,
role
)
print("DEBUG: Email sent successfully") # Debug
status.update(label="✅ Confirmation email sent!")
# 4. Schedule interview
with st.status("📅 Scheduling interview...", expanded=True) as status:
print("DEBUG: Attempting to schedule interview") # Debug
schedule_interview(
scheduler_agent,
st.session_state.candidate_email,
email_agent,
role
)
print("DEBUG: Interview scheduled successfully") # Debug
status.update(label="✅ Interview scheduled!")
print("DEBUG: All processes completed successfully") # Debug
st.success("""
🎉 Application Successfully Processed!
Please check your email for:
1. Selection confirmation
2. Interview details with Zoom link 🔗
Next steps:
1. Review the role requirements
2. Prepare for your technical interview
3. Join the interview 5 minutes early
""")
except Exception as e:
print(f"DEBUG: Error occurred: {str(e)}") # Debug
print(f"DEBUG: Error type: {type(e)}") # Debug
import traceback
print(f"DEBUG: Full traceback: {traceback.format_exc()}") # Debug
st.error(f"An error occurred: {str(e)}")
st.error("Please try again or contact support.")
# Reset button
if st.sidebar.button("Reset Application"):
for key in st.session_state.keys():
if key != 'openai_api_key':
del st.session_state[key]
st.rerun()
if __name__ == "__main__":
main()

View file

@ -0,0 +1,12 @@
# Core dependencies
phidata==2.7.3
streamlit==1.40.2
PyPDF2==3.0.1
streamlit-pdf-viewer==0.0.19
requests==2.32.3
pytz==2023.4
typing-extensions>=4.9.0
# Optional but recommended
black>=24.1.1 # for code formatting
python-dateutil>=2.8.2 # for date parsing

View file

@ -0,0 +1,33 @@
from phi.agent import Agent
from phi.model.google import Gemini
from phi.tools.duckduckgo import DuckDuckGo
from google.generativeai import upload_file, get_file
import time
# 1. Initialize the Multimodal Agent
agent = Agent(model=Gemini(id="gemini-2.0-flash-exp"), tools=[DuckDuckGo()], markdown=True)
# 2. Image Input
image_url = "https://example.com/sample_image.jpg"
# 3. Audio Input
audio_file = "sample_audio.mp3"
# 4. Video Input
video_file = upload_file("sample_video.mp4")
while video_file.state.name == "PROCESSING":
time.sleep(2)
video_file = get_file(video_file.name)
# 5. Multimodal Query
query = """
Combine insights from the inputs:
1. **Image**: Describe the scene and its significance.
2. **Audio**: Extract key messages that relate to the visual.
3. **Video**: Look at the video input and provide insights that connect with the image and audio context.
4. **Web Search**: Find the latest updates or events linking all these topics.
Summarize the overall theme or story these inputs convey.
"""
# 6. Multimodal Agent generates unified response
agent.print_response(query, images=[image_url], audio=audio_file, videos=[video_file], stream=True)

View file

@ -16,7 +16,7 @@ A Streamlit application that combines video analysis and web search capabilities
```bash
git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git
cd awesome-llm-apps/ai_agent_tutorials/multimodal_ai_agent
cd ai_agent_tutorials/multimodal_ai_agent
```
2. Install the required dependencies:
@ -36,4 +36,4 @@ GOOGLE_API_KEY=your_api_key_here
5. Run the Streamlit App
```bash
streamlit run multimodal_agent.py
```
```

View file

@ -0,0 +1,62 @@
import streamlit as st
from phi.agent import Agent
from phi.model.google import Gemini
import tempfile
import os
def main():
# Set up the reasoning agent
agent = Agent(
model=Gemini(id="gemini-2.0-flash-thinking-exp-1219"),
markdown=True
)
# Streamlit app title
st.title("Multimodal Reasoning AI Agent 🧠")
# Instruction
st.write(
"Upload an image and provide a reasoning-based task for the AI Agent. "
"The AI Agent will analyze the image and respond based on your input."
)
# File uploader for image
uploaded_file = st.file_uploader("Upload Image", type=["jpg", "jpeg", "png"])
if uploaded_file is not None:
try:
# Save uploaded file to temporary file
with tempfile.NamedTemporaryFile(delete=False, suffix='.jpg') as tmp_file:
tmp_file.write(uploaded_file.getvalue())
temp_path = tmp_file.name
# Display the uploaded image
st.image(uploaded_file, caption="Uploaded Image", use_container_width=True)
# Input for dynamic task
task_input = st.text_area(
"Enter your task/question for the AI Agent:"
)
# Button to process the image and task
if st.button("Analyze Image") and task_input:
with st.spinner("AI is thinking... 🤖"):
try:
# Call the agent with the dynamic task and image path
response = agent.run(task_input, images=[temp_path])
# Display the response from the model
st.markdown("### AI Response:")
st.markdown(response.content)
except Exception as e:
st.error(f"An error occurred during analysis: {str(e)}")
finally:
# Clean up temp file
if os.path.exists(temp_path):
os.unlink(temp_path)
except Exception as e:
st.error(f"An error occurred while processing the image: {str(e)}")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,64 @@
# RAG Agent with Cohere ⌘R
A RAG Agentic system built with Cohere's new model Command-r7b-12-2024, Qdrant for vector storage, Langchain for RAG and LangGraph for orchestration. This application allows users to upload documents, ask questions about them, and get AI-powered responses with fallback to web search when needed.
## Features
- **Document Processing**
- PDF document upload and processing
- Automatic text chunking and embedding
- Vector storage in Qdrant cloud
- **Intelligent Querying**
- RAG-based document retrieval
- Similarity search with threshold filtering
- Automatic fallback to web search when no relevant documents found
- Source attribution for answers
- **Advanced Capabilities**
- DuckDuckGo web search integration
- LangGraph agent for web research
- Context-aware response generation
- Long answer summarization
- **Model Specific Features**
- Command-r7b-12-2024 model for Chat and RAG
- cohere embed-english-v3.0 model for embeddings
- create_react_agent function from langgraph
- DuckDuckGoSearchRun tool for web search
## Prerequisites
### 1. Cohere API Key
1. Go to [Cohere Platform](https://dashboard.cohere.ai/api-keys)
2. Sign up or log in to your account
3. Navigate to API Keys section
4. Create a new API key
### 2. Qdrant Cloud Setup
1. Visit [Qdrant Cloud](https://cloud.qdrant.io/)
2. Create an account or sign in
3. Create a new cluster
4. Get your credentials:
- Qdrant API Key: Found in API Keys section
- Qdrant URL: Your cluster URL (format: `https://xxx-xxx.aws.cloud.qdrant.io`)
## How to Run
1. Clone the repository:
```bash
git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git
cd rag_tutorials/rag_agent_cohere
```
2. Install dependencies:
```bash
pip install -r requirements.txt
```
```bash
streamlit run rag_agent_cohere.py
```

View file

@ -0,0 +1,319 @@
import os
import streamlit as st
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_cohere import CohereEmbeddings, ChatCohere
from langchain_qdrant import QdrantVectorStore
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains import create_retrieval_chain
from langchain import hub
import tempfile
from langgraph.prebuilt import create_react_agent
from langchain_community.tools import DuckDuckGoSearchRun
from typing import TypedDict, List
from langchain_core.language_models import BaseLanguageModel
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from time import sleep
from tenacity import retry, wait_exponential, stop_after_attempt
def init_session_state():
if 'api_keys_submitted' not in st.session_state:
st.session_state.api_keys_submitted = False
if 'chat_history' not in st.session_state:
st.session_state.chat_history = []
if 'vectorstore' not in st.session_state:
st.session_state.vectorstore = None
if 'qdrant_api_key' not in st.session_state:
st.session_state.qdrant_api_key = ""
if 'qdrant_url' not in st.session_state:
st.session_state.qdrant_url = ""
def sidebar_api_form():
with st.sidebar:
st.header("API Credentials")
if st.session_state.api_keys_submitted:
st.success("API credentials verified")
if st.button("Reset Credentials"):
st.session_state.clear()
st.rerun()
return True
with st.form("api_credentials"):
cohere_key = st.text_input("Cohere API Key", type="password")
qdrant_key = st.text_input("Qdrant API Key", type="password", help="Enter your Qdrant API key")
qdrant_url = st.text_input("Qdrant URL",
placeholder="https://xyz-example.eu-central.aws.cloud.qdrant.io:6333",
help="Enter your Qdrant instance URL")
if st.form_submit_button("Submit Credentials"):
try:
client = QdrantClient(url=qdrant_url, api_key=qdrant_key, timeout=60)
client.get_collections()
st.session_state.cohere_api_key = cohere_key
st.session_state.qdrant_api_key = qdrant_key
st.session_state.qdrant_url = qdrant_url
st.session_state.api_keys_submitted = True
st.success("Credentials verified!")
st.rerun()
except Exception as e:
st.error(f"Qdrant connection failed: {str(e)}")
return False
def init_qdrant() -> QdrantClient:
if not st.session_state.get("qdrant_api_key"):
raise ValueError("Qdrant API key not provided")
if not st.session_state.get("qdrant_url"):
raise ValueError("Qdrant URL not provided")
return QdrantClient(url=st.session_state.qdrant_url,
api_key=st.session_state.qdrant_api_key,
timeout=60)
init_session_state()
if not sidebar_api_form():
st.info("Please enter your API credentials in the sidebar to continue.")
st.stop()
embedding = CohereEmbeddings(model="embed-english-v3.0",
cohere_api_key=st.session_state.cohere_api_key)
chat_model = ChatCohere(model="command-r7b-12-2024",
temperature=0.1,
max_tokens=512,
verbose=True,
cohere_api_key=st.session_state.cohere_api_key)
client = init_qdrant()
def process_document(file):
try:
with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as tmp_file:
tmp_file.write(file.getvalue())
tmp_path = tmp_file.name
loader = PyPDFLoader(tmp_path)
documents = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
texts = text_splitter.split_documents(documents)
os.unlink(tmp_path)
return texts
except Exception as e:
st.error(f"Error processing document: {e}")
return []
COLLECTION_NAME = "cohere_rag"
def create_vector_stores(texts):
"""Create and populate vector store with documents."""
try:
try:
client.create_collection(collection_name=COLLECTION_NAME,
vectors_config=VectorParams(size=1024,
distance=Distance.COSINE))
st.success(f"Created new collection: {COLLECTION_NAME}")
except Exception as e:
if "already exists" not in str(e).lower():
raise e
vector_store = QdrantVectorStore(client=client,
collection_name=COLLECTION_NAME,
embedding=embedding)
with st.spinner('Storing documents in Qdrant...'):
vector_store.add_documents(texts)
st.success("Documents successfully stored in Qdrant!")
return vector_store
except Exception as e:
st.error(f"Error in vector store creation: {str(e)}")
return None
# Define the state schema using TypedDict
class AgentState(TypedDict):
"""State schema for the agent."""
messages: List[HumanMessage | AIMessage | SystemMessage]
is_last_step: bool
class RateLimitedDuckDuckGo(DuckDuckGoSearchRun):
@retry(wait=wait_exponential(multiplier=1, min=4, max=10),
stop=stop_after_attempt(3))
def run(self, query: str) -> str:
"""Run search with rate limiting."""
try:
sleep(2) # Add delay between requests
return super().run(query)
except Exception as e:
if "Ratelimit" in str(e):
sleep(5) # Longer delay on rate limit
return super().run(query)
raise e
def create_fallback_agent(chat_model: BaseLanguageModel):
"""Create a LangGraph agent for web research."""
def web_research(query: str) -> str:
"""Web search with result formatting."""
try:
search = DuckDuckGoSearchRun(num_results=5)
results = search.run(query)
return results
except Exception as e:
return f"Search failed: {str(e)}. Providing answer based on general knowledge."
tools = [web_research]
agent = create_react_agent(model=chat_model,
tools=tools,
debug=False)
return agent
def process_query(vectorstore, query) -> tuple[str, list]:
"""Process a query using RAG with fallback to web search."""
try:
retriever = vectorstore.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={
"k": 10,
"score_threshold": 0.7
}
)
relevant_docs = retriever.get_relevant_documents(query)
if relevant_docs:
retrieval_qa_prompt = hub.pull("langchain-ai/retrieval-qa-chat")
combine_docs_chain = create_stuff_documents_chain(chat_model, retrieval_qa_prompt)
retrieval_chain = create_retrieval_chain(retriever, combine_docs_chain)
response = retrieval_chain.invoke({"input": query})
return response['answer'], relevant_docs
else:
st.info("No relevant documents found. Searching web...")
fallback_agent = create_fallback_agent(chat_model)
with st.spinner('Researching...'):
agent_input = {
"messages": [
HumanMessage(content=f"""Please thoroughly research the question: '{query}' and provide a detailed and comprehensive response. Make sure to gather the latest information from credible sources. Minimum 400 words.""")
],
"is_last_step": False
}
config = {"recursion_limit": 100}
try:
response = fallback_agent.invoke(agent_input, config=config)
if isinstance(response, dict) and "messages" in response:
last_message = response["messages"][-1]
answer = last_message.content if hasattr(last_message, 'content') else str(last_message)
return f"""Web Search Result:
{answer}
""", []
except Exception as agent_error:
fallback_response = chat_model.invoke(f"Please provide a general answer to: {query}").content
return f"Web search unavailable. General response: {fallback_response}", []
except Exception as e:
st.error(f"Error: {str(e)}")
return "I encountered an error. Please try rephrasing your question.", []
def post_process(answer, sources):
"""Post-process the answer and format sources."""
answer = answer.strip()
# Summarize long answers
if len(answer) > 500:
summary_prompt = f"Summarize the following answer in 2-3 sentences: {answer}"
summary = chat_model.invoke(summary_prompt).content
answer = f"{summary}\n\nFull Answer: {answer}"
formatted_sources = []
for i, source in enumerate(sources, 1):
formatted_source = f"{i}. {source.page_content[:200]}..."
formatted_sources.append(formatted_source)
return answer, formatted_sources
st.title("RAG Agent with Cohere ⌘R")
uploaded_file = st.file_uploader("Choose a PDF or Image File", type=["pdf", "jpg", "jpeg"])
if uploaded_file is not None and 'processed_file' not in st.session_state:
with st.spinner('Processing file... This may take a while for images.'):
texts = process_document(uploaded_file)
vectorstore = create_vector_stores(texts)
if vectorstore:
st.session_state.vectorstore = vectorstore
st.session_state.processed_file = True
st.success('File uploaded and processed successfully!')
else:
st.error('Failed to process file. Please try again.')
for message in st.session_state.chat_history:
with st.chat_message(message["role"]):
st.markdown(message["content"])
if query := st.chat_input("Ask a question about the document:"):
st.session_state.chat_history.append({"role": "user", "content": query})
with st.chat_message("user"):
st.markdown(query)
if st.session_state.vectorstore:
with st.chat_message("assistant"):
try:
answer, sources = process_query(st.session_state.vectorstore, query)
st.markdown(answer)
if sources:
with st.expander("Sources"):
for source in sources:
st.markdown(f"- {source.page_content[:200]}...")
st.session_state.chat_history.append({
"role": "assistant",
"content": answer
})
except Exception as e:
st.error(f"Error: {str(e)}")
st.info("Please try asking your question again.")
else:
st.error("Please upload a document first.")
with st.sidebar:
st.divider()
col1, col2 = st.columns(2)
with col1:
if st.button('Clear Chat History'):
st.session_state.chat_history = []
st.rerun()
with col2:
if st.button('Clear All Data'):
try:
collections = client.get_collections().collections
collection_names = [col.name for col in collections]
if COLLECTION_NAME in collection_names:
client.delete_collection(COLLECTION_NAME)
if f"{COLLECTION_NAME}_compressed" in collection_names:
client.delete_collection(f"{COLLECTION_NAME}_compressed")
st.session_state.vectorstore = None
st.session_state.chat_history = []
st.success("All data cleared successfully!")
st.rerun()
except Exception as e:
st.error(f"Error clearing data: {str(e)}")

View file

@ -0,0 +1,14 @@
langchain==0.3.12
langchain-community==0.3.12
langchain-core==0.3.25
langchain-cohere==0.3.2
langchain-qdrant==0.2.0
cohere==5.11.4
qdrant-client==1.12.1
duckduckgo-search==6.4.1
streamlit==1.40.2
tenacity==9.0.0
typing-extensions==4.12.2
pydantic==2.9.2
pydantic-core==2.23.4
langgraph==0.2.53