|
|
import os
|
|
|
import sys
|
|
|
import json
|
|
|
import google.generativeai as genai
|
|
|
|
|
|
from intent_detector import detect_intent
|
|
|
from rag_utils import retrieve_profiles, retrieve_jobs
|
|
|
|
|
|
|
|
|
API_KEY = os.getenv("GOOGLE_API_KEY")
|
|
|
if not API_KEY:
|
|
|
raise ValueError("Error: Set GOOGLE_API_KEY environment variable before running.")
|
|
|
genai.configure(api_key=API_KEY)
|
|
|
|
|
|
|
|
|
SYSTEM_PROMPT = """
|
|
|
You are a single AI assistant that can perform any of the following three tasks based on the user's request:
|
|
|
|
|
|
1. ONBOARD – Onboard a work professional by capturing their profile details such as name, role, skills, experience, and location. If information is missing, ask for it. If user already exists (by matching query to stored profiles), tell them they are already onboarded.
|
|
|
|
|
|
2. SEARCH – Search for job opportunities. Given a user's query (e.g., "Remote React developer paying at least $60k"), retrieve relevant job listings and present them in a concise format (title, company, type, salary).
|
|
|
|
|
|
3. POST – Create a structured job opportunity (for clients). Given a client's unstructured description (e.g., "I need someone to build a website for $2000 in 3 weeks"), produce a JSON‐style job post with fields: title, description, skills, budget, timeline.
|
|
|
|
|
|
Respond according to these rules:
|
|
|
- First, identify which of the three tasks the user is requesting (ONBOARD, SEARCH, or POST).
|
|
|
- Then, perform that task. If any details are missing from the user's request, ask a clarifying question.
|
|
|
- Always keep answers concise (2–4 sentences) and in plain English, except for the POST task, where you output a valid JSON object without extra markdown.
|
|
|
|
|
|
Below are three examples (few‐shot) to demonstrate expected behavior:
|
|
|
|
|
|
---
|
|
|
|
|
|
Example 1 – ONBOARD:
|
|
|
User: "I'm a backend developer with 4 years of experience in Python and Django, based in New York."
|
|
|
Assistant: "ONBOARD
|
|
|
|
|
|
Sure! Let me gather your onboarding details:
|
|
|
• Name: [please tell me your full name]
|
|
|
• Role: Backend Developer
|
|
|
• Skills: Python, Django
|
|
|
• Experience: 4 years
|
|
|
• Location: New York
|
|
|
Please confirm your full name so I can complete your profile."
|
|
|
|
|
|
---
|
|
|
|
|
|
Example 2 – SEARCH:
|
|
|
User: "Show me remote React jobs paying at least $50k/year."
|
|
|
Assistant: "SEARCH
|
|
|
|
|
|
Here are three remote React developer roles paying ≥ $50k/year:
|
|
|
1) Senior React Developer at AcmeCorp (Remote, USD 70k–90k)
|
|
|
2) Frontend Engineer at BetaTech (Remote, USD 55k–65k)
|
|
|
3) Fullstack Engineer at GammaWorks (Hybrid, USD 60k–80k)
|
|
|
Let me know if you want more details or a narrower focus."
|
|
|
|
|
|
---
|
|
|
|
|
|
Example 3 – POST:
|
|
|
User: "I need a WordPress expert to build a blog site, budget is around $1000."
|
|
|
Assistant: "POST
|
|
|
|
|
|
{
|
|
|
\"title\": \"WordPress Developer for Blog Site\",
|
|
|
\"description\": \"Create a blog site with a custom theme, SEO optimization, and social sharing features.\",
|
|
|
\"skills\": [\"WordPress\", \"PHP\", \"HTML\", \"CSS\"],
|
|
|
\"budget\": \"$1000\",
|
|
|
\"timeline\": \"3 weeks\"
|
|
|
}"
|
|
|
|
|
|
---
|
|
|
|
|
|
Now, when a new user message arrives, first identify the intent (ONBOARD, SEARCH, or POST), and then follow the instructions above to respond properly.
|
|
|
"""
|
|
|
|
|
|
def chat_loop(model_name="gemini-1.5-flash"):
|
|
|
"""
|
|
|
Main chat loop that handles conversation with context injection based on intent.
|
|
|
"""
|
|
|
print(f"=== Multi-Task Gemini Chatbot (Model: {model_name}) ===")
|
|
|
print("Type your message below. Type 'exit' to quit.\n")
|
|
|
|
|
|
|
|
|
model = genai.GenerativeModel(model_name)
|
|
|
|
|
|
|
|
|
chat = model.start_chat(history=[])
|
|
|
|
|
|
while True:
|
|
|
try:
|
|
|
user_input = input("You: ").strip()
|
|
|
except (KeyboardInterrupt, EOFError):
|
|
|
print("\nExiting. Goodbye!")
|
|
|
break
|
|
|
|
|
|
if not user_input:
|
|
|
continue
|
|
|
if user_input.lower() == "exit":
|
|
|
print("Goodbye!")
|
|
|
break
|
|
|
|
|
|
try:
|
|
|
|
|
|
intent = detect_intent(user_input, model=model_name)
|
|
|
print(f"[Debug] Detected intent: {intent}")
|
|
|
|
|
|
|
|
|
context_block = None
|
|
|
if intent == "SEARCH":
|
|
|
|
|
|
job_results = retrieve_jobs(user_input, top_k=3)
|
|
|
|
|
|
lines = []
|
|
|
for idx, job in enumerate(job_results, start=1):
|
|
|
lines.append(f"{idx}) {job['text']}")
|
|
|
block_text = "\n".join(lines)
|
|
|
context_block = f"Relevant job listings:\n{block_text}"
|
|
|
elif intent == "ONBOARD":
|
|
|
|
|
|
profile_results = retrieve_profiles(user_input, top_k=3)
|
|
|
lines = []
|
|
|
for idx, prof in enumerate(profile_results, start=1):
|
|
|
lines.append(f"{idx}) {prof['text']}")
|
|
|
block_text = "\n".join(lines)
|
|
|
context_block = f"Existing profiles that match your query:\n{block_text}"
|
|
|
|
|
|
|
|
|
|
|
|
full_prompt = SYSTEM_PROMPT
|
|
|
if context_block:
|
|
|
full_prompt += f"\n\nContext Information:\n{context_block}"
|
|
|
print(f"[Debug] Injected context block:\n{context_block}\n")
|
|
|
|
|
|
full_prompt += f"\n\nUser: {user_input}\nAssistant:"
|
|
|
|
|
|
|
|
|
response = model.generate_content(full_prompt)
|
|
|
assistant_reply = response.text
|
|
|
|
|
|
|
|
|
print(f"\nBot: {assistant_reply}\n")
|
|
|
|
|
|
except Exception as e:
|
|
|
print(f"[Error] Gemini API call failed: {e}")
|
|
|
print("Please try again or check your API key and internet connection.\n")
|
|
|
continue
|
|
|
|
|
|
def main():
|
|
|
"""Main function to start the chatbot."""
|
|
|
chosen_model = sys.argv[1] if len(sys.argv) > 1 else "gemini-1.5-flash"
|
|
|
|
|
|
|
|
|
available_models = ["gemini-1.5-flash", "gemini-1.5-pro", "gemini-pro"]
|
|
|
if chosen_model not in available_models:
|
|
|
print(f"Warning: Model '{chosen_model}' may not be available.")
|
|
|
print(f"Available models: {', '.join(available_models)}")
|
|
|
print(f"Using default: gemini-1.5-flash\n")
|
|
|
chosen_model = "gemini-1.5-flash"
|
|
|
|
|
|
chat_loop(model_name=chosen_model)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
main() |