|
|
import os |
|
|
import gradio as gr |
|
|
import torch |
|
|
import re |
|
|
import time |
|
|
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline |
|
|
from huggingface_hub import hf_hub_download, snapshot_download |
|
|
import json |
|
|
from typing import Dict, List, Any, Optional, Union |
|
|
|
|
|
|
|
|
from agent_reasoning import ReasoningEngine |
|
|
from agent_tasks import TaskExecutor |
|
|
from agent_memory import MemoryManager |
|
|
|
|
|
class ResuRankAgent: |
|
|
"""Autonomous AI Agent similar to Manus AI |
|
|
|
|
|
This agent can: |
|
|
1. Process user queries and generate responses |
|
|
2. Perform reasoning through chain-of-thought |
|
|
3. Execute tasks based on user instructions |
|
|
4. Maintain conversation context |
|
|
""" |
|
|
|
|
|
def __init__(self, model_id="distilgpt2", use_cache=True, test_mode=False): |
|
|
"""Initialize the ResuRank Agent |
|
|
|
|
|
Args: |
|
|
model_id: Hugging Face model ID to use for the agent |
|
|
use_cache: Whether to use cached models from Hugging Face Hub |
|
|
test_mode: Whether to run in test mode with minimal resources |
|
|
""" |
|
|
self.model_id = model_id |
|
|
self.test_mode = test_mode |
|
|
|
|
|
|
|
|
if test_mode: |
|
|
self.device = "cpu" |
|
|
print("Running in test mode on CPU with minimal resources") |
|
|
else: |
|
|
self.device = "cuda" if torch.cuda.is_available() else "cpu" |
|
|
print(f"Using device: {self.device}") |
|
|
|
|
|
|
|
|
print(f"Loading model {model_id} from Hugging Face Hub...") |
|
|
try: |
|
|
|
|
|
model_kwargs = { |
|
|
"torch_dtype": torch.float32, |
|
|
} |
|
|
|
|
|
|
|
|
try: |
|
|
import accelerate |
|
|
model_kwargs["low_cpu_mem_usage"] = True |
|
|
|
|
|
if not test_mode: |
|
|
model_kwargs["device_map"] = "auto" |
|
|
if self.device == "cuda": |
|
|
model_kwargs["torch_dtype"] = torch.float16 |
|
|
except ImportError: |
|
|
print("Accelerate library not found, disabling low_cpu_mem_usage and device_map") |
|
|
|
|
|
|
|
|
|
|
|
if use_cache: |
|
|
model_kwargs["cache_dir"] = "./.cache" |
|
|
print("Using cached models if available...") |
|
|
self.tokenizer = AutoTokenizer.from_pretrained(model_id, cache_dir="./.cache") |
|
|
else: |
|
|
print("Downloading models from Hugging Face Hub...") |
|
|
self.tokenizer = AutoTokenizer.from_pretrained(model_id) |
|
|
|
|
|
|
|
|
self.model = AutoModelForCausalLM.from_pretrained(model_id, **model_kwargs) |
|
|
|
|
|
print(f"Successfully loaded model {model_id}") |
|
|
except Exception as e: |
|
|
print(f"Error loading model: {str(e)}") |
|
|
print("Falling back to smaller model...") |
|
|
fallback_model = "distilgpt2" |
|
|
self.model_id = fallback_model |
|
|
|
|
|
try: |
|
|
|
|
|
self.tokenizer = AutoTokenizer.from_pretrained(fallback_model, cache_dir="./.cache") |
|
|
self.model = AutoModelForCausalLM.from_pretrained( |
|
|
fallback_model, |
|
|
torch_dtype=torch.float32, |
|
|
low_cpu_mem_usage=True, |
|
|
cache_dir="./.cache" |
|
|
) |
|
|
print(f"Successfully loaded fallback model {fallback_model}") |
|
|
except Exception as fallback_error: |
|
|
print(f"Error loading fallback model: {str(fallback_error)}") |
|
|
raise RuntimeError("Failed to load both primary and fallback models") |
|
|
|
|
|
|
|
|
self.reasoning_engine = ReasoningEngine(self.model, self.tokenizer, self.device) |
|
|
self.memory_manager = MemoryManager(max_history_length=20) |
|
|
self.task_executor = TaskExecutor(self.reasoning_engine) |
|
|
|
|
|
def process_query(self, query: str, use_reasoning: bool = True) -> Dict[str, Any]: |
|
|
"""Process a user query and generate a response |
|
|
|
|
|
Args: |
|
|
query: User query text |
|
|
use_reasoning: Whether to use chain-of-thought reasoning |
|
|
|
|
|
Returns: |
|
|
Dictionary containing response and metadata |
|
|
""" |
|
|
|
|
|
self.memory_manager.add_message("user", query) |
|
|
|
|
|
start_time = time.time() |
|
|
|
|
|
|
|
|
is_task_request = self._is_task_request(query) |
|
|
|
|
|
|
|
|
if is_task_request: |
|
|
|
|
|
task_result = self.execute_task(query) |
|
|
response = f"I've executed your task. {task_result.get('result', '')}\n\nStatus: {task_result.get('status', 'unknown')}" |
|
|
reasoning = task_result.get('plan', '') |
|
|
elif use_reasoning: |
|
|
|
|
|
|
|
|
facts = self.memory_manager.format_facts_for_prompt() |
|
|
context = self.memory_manager.format_conversation_for_prompt(max_turns=5) |
|
|
|
|
|
|
|
|
enhanced_query = f"{facts}\n\nRecent conversation:\n{context}\n\nCurrent query: {query}" |
|
|
|
|
|
result = self.reasoning_engine.chain_of_thought(enhanced_query) |
|
|
response = result["answer"] |
|
|
reasoning = result["reasoning"] |
|
|
else: |
|
|
|
|
|
conversation_prompt = self.memory_manager.format_conversation_for_prompt(max_turns=10) |
|
|
facts_prompt = self.memory_manager.format_facts_for_prompt() |
|
|
|
|
|
prompt = f"{facts_prompt}\n\n{conversation_prompt}\nassistant: " |
|
|
|
|
|
response = self.reasoning_engine.generate_text(prompt) |
|
|
reasoning = None |
|
|
|
|
|
|
|
|
self.memory_manager.add_message("assistant", response) |
|
|
|
|
|
|
|
|
self._extract_facts(query, response) |
|
|
|
|
|
processing_time = time.time() - start_time |
|
|
|
|
|
return { |
|
|
"response": response, |
|
|
"reasoning": reasoning, |
|
|
"processing_time": processing_time, |
|
|
"timestamp": time.time() |
|
|
} |
|
|
|
|
|
def _is_task_request(self, query: str) -> bool: |
|
|
"""Determine if a query is a task execution request |
|
|
|
|
|
Args: |
|
|
query: The user query |
|
|
|
|
|
Returns: |
|
|
True if the query appears to be a task request, False otherwise |
|
|
""" |
|
|
|
|
|
task_keywords = [ |
|
|
"execute", "perform", "run", "do", "complete", "finish", |
|
|
"task", "job", "work", "action", "operation", "function", |
|
|
"can you", "please", "help me", "i need", "i want" |
|
|
] |
|
|
|
|
|
|
|
|
query_lower = query.lower() |
|
|
for keyword in task_keywords: |
|
|
if keyword in query_lower: |
|
|
return True |
|
|
|
|
|
return False |
|
|
|
|
|
def _extract_facts(self, query: str, response: str) -> None: |
|
|
"""Extract important facts from the conversation |
|
|
|
|
|
Args: |
|
|
query: User query |
|
|
response: Agent response |
|
|
""" |
|
|
|
|
|
self._extract_personal_info(query) |
|
|
|
|
|
|
|
|
self._extract_preferences(query) |
|
|
|
|
|
|
|
|
self._extract_task_info(query) |
|
|
|
|
|
|
|
|
self._extract_with_reasoning(query, response) |
|
|
|
|
|
def _extract_personal_info(self, text: str) -> None: |
|
|
"""Extract personal information from text |
|
|
|
|
|
Args: |
|
|
text: Text to extract information from |
|
|
""" |
|
|
text_lower = text.lower() |
|
|
|
|
|
|
|
|
if "my name is" in text_lower or "i am called" in text_lower or "i'm called" in text_lower: |
|
|
name_patterns = [ |
|
|
r"my name is ([\w\s]+)[.\,]?", |
|
|
r"i am called ([\w\s]+)[.\,]?", |
|
|
r"i'm called ([\w\s]+)[.\,]?" |
|
|
] |
|
|
|
|
|
for pattern in name_patterns: |
|
|
name_match = re.search(pattern, text_lower) |
|
|
if name_match: |
|
|
name = name_match.group(1).strip() |
|
|
self.memory_manager.add_important_fact(f"User's name is {name}", "user") |
|
|
break |
|
|
|
|
|
|
|
|
if "i am from" in text_lower or "i'm from" in text_lower or "i live in" in text_lower: |
|
|
location_patterns = [ |
|
|
r"i am from ([\w\s]+)[.\,]?", |
|
|
r"i'm from ([\w\s]+)[.\,]?", |
|
|
r"i live in ([\w\s]+)[.\,]?" |
|
|
] |
|
|
|
|
|
for pattern in location_patterns: |
|
|
location_match = re.search(pattern, text_lower) |
|
|
if location_match: |
|
|
location = location_match.group(1).strip() |
|
|
self.memory_manager.add_important_fact(f"User is from {location}", "user") |
|
|
break |
|
|
|
|
|
|
|
|
if "i work as" in text_lower or "i am a" in text_lower or "i'm a" in text_lower: |
|
|
profession_patterns = [ |
|
|
r"i work as a[n]? ([\w\s]+)[.\,]?", |
|
|
r"i am a[n]? ([\w\s]+)[.\,]?", |
|
|
r"i'm a[n]? ([\w\s]+)[.\,]?" |
|
|
] |
|
|
|
|
|
for pattern in profession_patterns: |
|
|
profession_match = re.search(pattern, text_lower) |
|
|
if profession_match: |
|
|
profession = profession_match.group(1).strip() |
|
|
self.memory_manager.add_important_fact(f"User works as a {profession}", "user") |
|
|
break |
|
|
|
|
|
def _extract_preferences(self, text: str) -> None: |
|
|
"""Extract user preferences from text |
|
|
|
|
|
Args: |
|
|
text: Text to extract information from |
|
|
""" |
|
|
text_lower = text.lower() |
|
|
|
|
|
|
|
|
if "i like" in text_lower or "i love" in text_lower or "i enjoy" in text_lower: |
|
|
like_patterns = [ |
|
|
r"i like ([\w\s]+)[.\,]?", |
|
|
r"i love ([\w\s]+)[.\,]?", |
|
|
r"i enjoy ([\w\s]+)[.\,]?" |
|
|
] |
|
|
|
|
|
for pattern in like_patterns: |
|
|
like_match = re.search(pattern, text_lower) |
|
|
if like_match: |
|
|
like = like_match.group(1).strip() |
|
|
self.memory_manager.add_important_fact(f"User likes {like}", "user") |
|
|
break |
|
|
|
|
|
|
|
|
if "i don't like" in text_lower or "i hate" in text_lower or "i dislike" in text_lower: |
|
|
dislike_patterns = [ |
|
|
r"i don't like ([\w\s]+)[.\,]?", |
|
|
r"i hate ([\w\s]+)[.\,]?", |
|
|
r"i dislike ([\w\s]+)[.\,]?" |
|
|
] |
|
|
|
|
|
for pattern in dislike_patterns: |
|
|
dislike_match = re.search(pattern, text_lower) |
|
|
if dislike_match: |
|
|
dislike = dislike_match.group(1).strip() |
|
|
self.memory_manager.add_important_fact(f"User dislikes {dislike}", "user") |
|
|
break |
|
|
|
|
|
def _extract_task_info(self, text: str) -> None: |
|
|
"""Extract task-related information from text |
|
|
|
|
|
Args: |
|
|
text: Text to extract information from |
|
|
""" |
|
|
text_lower = text.lower() |
|
|
|
|
|
|
|
|
if "my goal is" in text_lower or "i want to" in text_lower or "i need to" in text_lower: |
|
|
goal_patterns = [ |
|
|
r"my goal is to ([\w\s]+)[.\,]?", |
|
|
r"i want to ([\w\s]+)[.\,]?", |
|
|
r"i need to ([\w\s]+)[.\,]?" |
|
|
] |
|
|
|
|
|
for pattern in goal_patterns: |
|
|
goal_match = re.search(pattern, text_lower) |
|
|
if goal_match: |
|
|
goal = goal_match.group(1).strip() |
|
|
self.memory_manager.add_important_fact(f"User's goal is to {goal}", "user") |
|
|
break |
|
|
|
|
|
def run_test_case(self) -> Dict[str, Any]: |
|
|
"""Run a test case to demonstrate the agent's capabilities with minimal resources |
|
|
|
|
|
This method is useful for testing the agent on resource-constrained environments |
|
|
like Hugging Face Spaces or during development. |
|
|
|
|
|
Returns: |
|
|
Dictionary containing test results and performance metrics |
|
|
""" |
|
|
print("Running test case with minimal resources...") |
|
|
start_time = time.time() |
|
|
|
|
|
|
|
|
test_query = "What can you help me with?" |
|
|
|
|
|
|
|
|
test_response = self.process_query(test_query, use_reasoning=False) |
|
|
|
|
|
|
|
|
processing_time = time.time() - start_time |
|
|
memory_usage = self._estimate_memory_usage() |
|
|
|
|
|
|
|
|
return { |
|
|
"status": "success", |
|
|
"model_id": self.model_id, |
|
|
"device": self.device, |
|
|
"test_query": test_query, |
|
|
"test_response": test_response["response"], |
|
|
"processing_time": processing_time, |
|
|
"memory_usage_mb": memory_usage, |
|
|
"timestamp": time.time() |
|
|
} |
|
|
|
|
|
def _estimate_memory_usage(self) -> float: |
|
|
"""Estimate the memory usage of the model |
|
|
|
|
|
Returns: |
|
|
Estimated memory usage in MB |
|
|
""" |
|
|
try: |
|
|
import psutil |
|
|
process = psutil.Process(os.getpid()) |
|
|
memory_info = process.memory_info() |
|
|
return memory_info.rss / (1024 * 1024) |
|
|
except ImportError: |
|
|
return 0.0 |
|
|
|
|
|
def _extract_with_reasoning(self, query: str, response: str) -> None: |
|
|
"""Use the reasoning engine to extract important facts |
|
|
|
|
|
Args: |
|
|
query: User query |
|
|
response: Agent response |
|
|
""" |
|
|
|
|
|
if len(query) < 50: |
|
|
return |
|
|
|
|
|
extraction_prompt = f"""Extract important facts from this conversation: |
|
|
|
|
|
User: {query} |
|
|
Assistant: {response} |
|
|
|
|
|
List of important facts (one per line): |
|
|
1. """ |
|
|
|
|
|
try: |
|
|
facts_text = self.reasoning_engine.generate_text(extraction_prompt, max_length=256) |
|
|
|
|
|
|
|
|
for line in facts_text.split('\n'): |
|
|
line = line.strip() |
|
|
if line and (line[0].isdigit() or line.startswith('- ')): |
|
|
|
|
|
fact = re.sub(r'^\d+\.\s*|^-\s*', '', line).strip() |
|
|
if fact and len(fact) > 10: |
|
|
self.memory_manager.add_important_fact(fact, "inference") |
|
|
except Exception as e: |
|
|
print(f"Error extracting facts with reasoning: {str(e)}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def execute_task(self, task_description: str) -> Dict[str, Any]: |
|
|
"""Execute a task based on the description |
|
|
|
|
|
Args: |
|
|
task_description: Description of the task to execute |
|
|
|
|
|
Returns: |
|
|
Dictionary containing task results and status |
|
|
""" |
|
|
return self.task_executor.execute_task(task_description) |
|
|
|
|
|
def get_status(self) -> Dict[str, Any]: |
|
|
"""Get the current status of the agent |
|
|
|
|
|
Returns: |
|
|
Dictionary containing agent status information |
|
|
""" |
|
|
memory_stats = self.memory_manager.get_memory_stats() |
|
|
task_status = self.task_executor.get_task_status() |
|
|
|
|
|
return { |
|
|
"model_id": self.model_id, |
|
|
"device": self.device, |
|
|
"conversation_turns": memory_stats["conversation_turns"], |
|
|
"important_facts": memory_stats["important_facts"], |
|
|
"current_task": task_status["current_task"], |
|
|
"task_status": task_status["status"] |
|
|
} |
|
|
|
|
|
def clear_conversation(self) -> None: |
|
|
"""Clear the conversation history""" |
|
|
self.memory_manager.clear_conversation_history() |
|
|
|
|
|
def process_document(self, document_text: str, document_type: str = "resume") -> Dict[str, Any]: |
|
|
"""Process a document (like a resume) and extract information |
|
|
|
|
|
Args: |
|
|
document_text: The text content of the document |
|
|
document_type: The type of document (e.g., "resume", "job_description") |
|
|
|
|
|
Returns: |
|
|
Dictionary containing extracted information and analysis |
|
|
""" |
|
|
self.memory_manager.store_session_data(f"last_{document_type}", document_text) |
|
|
start_time = time.time() |
|
|
|
|
|
|
|
|
analysis_prompt = f"""I need to analyze this {document_type} document and extract key information: |
|
|
|
|
|
{document_text} |
|
|
|
|
|
Detailed analysis:""" |
|
|
|
|
|
|
|
|
analysis = self.reasoning_engine.generate_text(analysis_prompt, max_length=1024) |
|
|
|
|
|
|
|
|
if document_type.lower() == "resume": |
|
|
extraction_prompt = f"""Based on this resume: |
|
|
{document_text} |
|
|
|
|
|
Extract the following information in a structured format: |
|
|
1. Name: |
|
|
2. Contact Information: |
|
|
3. Education: |
|
|
4. Work Experience: |
|
|
5. Skills: |
|
|
6. Projects: |
|
|
7. Certifications: |
|
|
8. Languages: |
|
|
9. Key Strengths: |
|
|
""" |
|
|
elif document_type.lower() == "job_description": |
|
|
extraction_prompt = f"""Based on this job description: |
|
|
{document_text} |
|
|
|
|
|
Extract the following information in a structured format: |
|
|
1. Job Title: |
|
|
2. Company: |
|
|
3. Location: |
|
|
4. Required Skills: |
|
|
5. Required Experience: |
|
|
6. Education Requirements: |
|
|
7. Responsibilities: |
|
|
8. Benefits: |
|
|
9. Key Qualifications: |
|
|
""" |
|
|
else: |
|
|
extraction_prompt = f"""Extract key information from this document: |
|
|
{document_text} |
|
|
|
|
|
Key information: |
|
|
1. """ |
|
|
|
|
|
|
|
|
structured_info = self.reasoning_engine.generate_text(extraction_prompt, max_length=1024) |
|
|
|
|
|
|
|
|
self._extract_document_facts(document_text, document_type, structured_info) |
|
|
|
|
|
processing_time = time.time() - start_time |
|
|
|
|
|
return { |
|
|
"document_type": document_type, |
|
|
"analysis": analysis, |
|
|
"structured_info": structured_info, |
|
|
"processing_time": processing_time, |
|
|
"timestamp": time.time() |
|
|
} |
|
|
|
|
|
def _extract_document_facts(self, document_text: str, document_type: str, structured_info: str) -> None: |
|
|
"""Extract important facts from a document and add them to memory |
|
|
|
|
|
Args: |
|
|
document_text: The text content of the document |
|
|
document_type: The type of document |
|
|
structured_info: Structured information extracted from the document |
|
|
""" |
|
|
|
|
|
if document_type.lower() == "resume": |
|
|
|
|
|
name_match = re.search(r"Name:\s*([\w\s]+)\n", structured_info) |
|
|
if name_match: |
|
|
name = name_match.group(1).strip() |
|
|
self.memory_manager.add_important_fact(f"Document contains resume for {name}", "document") |
|
|
|
|
|
|
|
|
skills_match = re.search(r"Skills:\s*([\w\s,\.\-\+]+)\n", structured_info) |
|
|
if skills_match: |
|
|
skills = skills_match.group(1).strip() |
|
|
self.memory_manager.add_important_fact(f"Resume shows skills in: {skills}", "document") |
|
|
|
|
|
|
|
|
education_match = re.search(r"Education:\s*([\w\s,\.\-\+]+)\n", structured_info) |
|
|
if education_match: |
|
|
education = education_match.group(1).strip() |
|
|
self.memory_manager.add_important_fact(f"Resume shows education: {education}", "document") |
|
|
|
|
|
elif document_type.lower() == "job_description": |
|
|
|
|
|
title_match = re.search(r"Job Title:\s*([\w\s]+)\n", structured_info) |
|
|
if title_match: |
|
|
title = title_match.group(1).strip() |
|
|
self.memory_manager.add_important_fact(f"Document contains job description for {title}", "document") |
|
|
|
|
|
|
|
|
skills_match = re.search(r"Required Skills:\s*([\w\s,\.\-\+]+)\n", structured_info) |
|
|
if skills_match: |
|
|
skills = skills_match.group(1).strip() |
|
|
self.memory_manager.add_important_fact(f"Job requires skills in: {skills}", "document") |
|
|
|
|
|
|
|
|
self.memory_manager.add_important_fact(f"Processed a {document_type} document", "system") |
|
|
|
|
|
def rank_resumes(self, job_description: str, resumes: List[str]) -> Dict[str, Any]: |
|
|
"""Rank multiple resumes against a job description |
|
|
|
|
|
Args: |
|
|
job_description: The job description text |
|
|
resumes: List of resume texts to rank |
|
|
|
|
|
Returns: |
|
|
Dictionary containing rankings and analysis |
|
|
""" |
|
|
start_time = time.time() |
|
|
|
|
|
|
|
|
job_result = self.process_document(job_description, "job_description") |
|
|
job_analysis = job_result["structured_info"] |
|
|
|
|
|
|
|
|
resume_results = [] |
|
|
for i, resume in enumerate(resumes): |
|
|
result = self.process_document(resume, "resume") |
|
|
resume_results.append({ |
|
|
"index": i, |
|
|
"text": resume, |
|
|
"analysis": result["structured_info"] |
|
|
}) |
|
|
|
|
|
|
|
|
ranking_prompt = f"""I need to rank these resumes based on how well they match the job description. |
|
|
|
|
|
Job Description Analysis: |
|
|
{job_analysis} |
|
|
|
|
|
Resumes: |
|
|
""" |
|
|
|
|
|
for i, result in enumerate(resume_results): |
|
|
ranking_prompt += f"\nResume {i+1}:\n{result['analysis']}\n" |
|
|
|
|
|
ranking_prompt += "\nRank these resumes from best to worst match for the job, with detailed reasoning for each:" |
|
|
|
|
|
|
|
|
ranking_analysis = self.reasoning_engine.generate_text(ranking_prompt, max_length=2048) |
|
|
|
|
|
|
|
|
scoring_prompt = f"""Based on my analysis of how well these resumes match the job description: |
|
|
{ranking_analysis} |
|
|
|
|
|
Assign a numerical score from 0-100 for each resume, where 100 is a perfect match: |
|
|
|
|
|
Resume 1 Score:""" |
|
|
|
|
|
scores_text = self.reasoning_engine.generate_text(scoring_prompt, max_length=512) |
|
|
|
|
|
|
|
|
scores = [] |
|
|
for i in range(len(resume_results)): |
|
|
score_match = re.search(fr"Resume {i+1} Score:\s*(\d+)", scores_text) |
|
|
if score_match: |
|
|
scores.append(int(score_match.group(1))) |
|
|
else: |
|
|
|
|
|
scores.append(50) |
|
|
|
|
|
|
|
|
rankings = [] |
|
|
for i, score in enumerate(scores): |
|
|
rankings.append({ |
|
|
"resume_index": i, |
|
|
"score": score, |
|
|
"resume_text": resumes[i][:100] + "..." |
|
|
}) |
|
|
|
|
|
|
|
|
rankings.sort(key=lambda x: x["score"], reverse=True) |
|
|
|
|
|
processing_time = time.time() - start_time |
|
|
|
|
|
return { |
|
|
"rankings": rankings, |
|
|
"analysis": ranking_analysis, |
|
|
"job_description": job_description, |
|
|
"processing_time": processing_time |
|
|
} |
|
|
|
|
|
|
|
|
def create_interface(test_mode=False): |
|
|
"""Create the Gradio interface for the ResuRank AI Agent |
|
|
|
|
|
Args: |
|
|
test_mode: Whether to run in test mode with minimal resources |
|
|
""" |
|
|
|
|
|
if test_mode: |
|
|
agent = ResuRankAgent(model_id="distilgpt2", use_cache=True, test_mode=True) |
|
|
|
|
|
test_results = agent.run_test_case() |
|
|
print(f"Test results: {test_results}") |
|
|
else: |
|
|
agent = ResuRankAgent(model_id="google/flan-t5-base", use_cache=True) |
|
|
|
|
|
with gr.Blocks(title="ResuRank AI Agent") as interface: |
|
|
gr.Markdown("# ResuRank AI Agent") |
|
|
gr.Markdown("An autonomous AI agent that can process queries, perform reasoning, and execute tasks.") |
|
|
|
|
|
with gr.Tab("Chat"): |
|
|
chatbot = gr.Chatbot(height=400) |
|
|
msg = gr.Textbox(label="Your message", placeholder="Ask me anything...") |
|
|
with gr.Row(): |
|
|
submit_btn = gr.Button("Submit") |
|
|
clear_btn = gr.Button("Clear") |
|
|
|
|
|
reasoning_checkbox = gr.Checkbox(label="Use reasoning", value=True) |
|
|
|
|
|
if reasoning_checkbox.value: |
|
|
reasoning_output = gr.Textbox(label="Reasoning", interactive=False) |
|
|
else: |
|
|
reasoning_output = gr.Textbox(label="Reasoning", interactive=False, visible=False) |
|
|
|
|
|
def respond(message, chat_history, use_reasoning): |
|
|
if not message.strip(): |
|
|
return chat_history, "", "" |
|
|
|
|
|
|
|
|
result = agent.process_query(message, use_reasoning=use_reasoning) |
|
|
|
|
|
|
|
|
chat_history.append((message, result["response"])) |
|
|
|
|
|
return chat_history, "", result.get("reasoning", "") |
|
|
|
|
|
def clear_chat(): |
|
|
agent.clear_conversation() |
|
|
return [], "", "" |
|
|
|
|
|
|
|
|
submit_btn.click(respond, [msg, chatbot, reasoning_checkbox], [chatbot, msg, reasoning_output]) |
|
|
msg.submit(respond, [msg, chatbot, reasoning_checkbox], [chatbot, msg, reasoning_output]) |
|
|
clear_btn.click(clear_chat, None, [chatbot, msg, reasoning_output]) |
|
|
reasoning_checkbox.change(lambda x: gr.update(visible=x), reasoning_checkbox, reasoning_output) |
|
|
|
|
|
with gr.Tab("Task Execution"): |
|
|
task_input = gr.Textbox(label="Task Description", placeholder="Describe the task to execute...") |
|
|
execute_btn = gr.Button("Execute Task") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
plan_output = gr.Textbox(label="Execution Plan", interactive=False) |
|
|
with gr.Column(): |
|
|
results_output = gr.Textbox(label="Task Results", interactive=False) |
|
|
|
|
|
task_status = gr.Textbox(label="Task Status", value="idle", interactive=False) |
|
|
|
|
|
def execute_task(task_description): |
|
|
if not task_description.strip(): |
|
|
return "No task provided.", "", "idle" |
|
|
|
|
|
|
|
|
result = agent.execute_task(task_description) |
|
|
|
|
|
return result.get("plan", ""), result.get("result", ""), result.get("status", "") |
|
|
|
|
|
|
|
|
execute_btn.click(execute_task, task_input, [plan_output, results_output, task_status]) |
|
|
|
|
|
with gr.Tab("Agent Status"): |
|
|
status_btn = gr.Button("Refresh Status") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
model_info = gr.Textbox(label="Model Information", interactive=False) |
|
|
with gr.Column(): |
|
|
conversation_info = gr.Textbox(label="Conversation Information", interactive=False) |
|
|
|
|
|
def update_status(): |
|
|
status = agent.get_status() |
|
|
model_text = f"Model ID: {status['model_id']}\nDevice: {status['device']}" |
|
|
|
|
|
|
|
|
important_facts_count = status['important_facts'] |
|
|
if isinstance(important_facts_count, list): |
|
|
important_facts_count = len(important_facts_count) |
|
|
|
|
|
conversation_text = f"Conversation Length: {status['conversation_turns']} turns\nImportant Facts: {important_facts_count}\nCurrent Task: {status['current_task'] or 'None'}\nTask Status: {status['task_status']}" |
|
|
|
|
|
return model_text, conversation_text |
|
|
|
|
|
|
|
|
status_btn.click(update_status, None, [model_info, conversation_info]) |
|
|
|
|
|
|
|
|
model_info.value, conversation_info.value = update_status() |
|
|
|
|
|
with gr.Tab("Document Processing"): |
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
document_input = gr.Textbox(label="Document Text", placeholder="Paste resume or job description text here...", lines=10) |
|
|
document_type = gr.Radio(["resume", "job_description", "other"], label="Document Type", value="resume") |
|
|
process_btn = gr.Button("Process Document") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
analysis_output = gr.Textbox(label="Document Analysis", interactive=False, lines=10) |
|
|
with gr.Column(): |
|
|
structured_output = gr.Textbox(label="Structured Information", interactive=False, lines=10) |
|
|
|
|
|
def process_document(document_text, doc_type): |
|
|
if not document_text.strip(): |
|
|
return "No document provided.", "" |
|
|
|
|
|
|
|
|
result = agent.process_document(document_text, doc_type) |
|
|
|
|
|
return result.get("analysis", ""), result.get("structured_info", "") |
|
|
|
|
|
|
|
|
process_btn.click(process_document, [document_input, document_type], [analysis_output, structured_output]) |
|
|
|
|
|
with gr.Tab("Resume Ranking"): |
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
job_description_input = gr.Textbox(label="Job Description", placeholder="Paste job description here...", lines=8) |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
resume1_input = gr.Textbox(label="Resume 1", placeholder="Paste first resume here...", lines=6) |
|
|
with gr.Column(): |
|
|
resume2_input = gr.Textbox(label="Resume 2", placeholder="Paste second resume here...", lines=6) |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
resume3_input = gr.Textbox(label="Resume 3 (Optional)", placeholder="Paste third resume here...", lines=6) |
|
|
with gr.Column(): |
|
|
resume4_input = gr.Textbox(label="Resume 4 (Optional)", placeholder="Paste fourth resume here...", lines=6) |
|
|
|
|
|
rank_btn = gr.Button("Rank Resumes") |
|
|
|
|
|
ranking_output = gr.Textbox(label="Ranking Results", interactive=False, lines=15) |
|
|
|
|
|
def rank_resumes(job_desc, resume1, resume2, resume3, resume4): |
|
|
if not job_desc.strip() or not resume1.strip() or not resume2.strip(): |
|
|
return "Please provide at least a job description and two resumes." |
|
|
|
|
|
|
|
|
resumes = [r for r in [resume1, resume2, resume3, resume4] if r.strip()] |
|
|
|
|
|
|
|
|
result = agent.rank_resumes(job_desc, resumes) |
|
|
|
|
|
|
|
|
output = "Resume Rankings (Best to Worst Match):\n\n" |
|
|
|
|
|
for i, rank in enumerate(result["rankings"]): |
|
|
resume_num = rank["resume_index"] + 1 |
|
|
score = rank["score"] |
|
|
output += f"{i+1}. Resume {resume_num} - Score: {score}/100\n" |
|
|
|
|
|
output += "\nDetailed Analysis:\n" + result["analysis"] |
|
|
|
|
|
return output |
|
|
|
|
|
|
|
|
rank_btn.click(rank_resumes, [job_description_input, resume1_input, resume2_input, resume3_input, resume4_input], ranking_output) |
|
|
|
|
|
return interface |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
import argparse |
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description="ResuRank AI Agent") |
|
|
parser.add_argument("--test", action="store_true", help="Run in test mode with minimal resources") |
|
|
parser.add_argument("--share", action="store_true", help="Share the Gradio interface") |
|
|
args = parser.parse_args() |
|
|
|
|
|
|
|
|
interface = create_interface(test_mode=args.test) |
|
|
interface.launch(share=args.share) |