anu151105 commited on
Commit
a3738dd
·
verified ·
1 Parent(s): 5d5dbc8

Upload 6 files

Browse files
Files changed (6) hide show
  1. Dockerfile +10 -0
  2. agent_memory.py +181 -0
  3. agent_reasoning.py +223 -0
  4. agent_tasks.py +244 -0
  5. app.py +837 -0
  6. requirements.txt +14 -0
Dockerfile ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ CMD ["python", "app.py"]
agent_memory.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ from typing import Dict, List, Any, Optional, Union
3
+
4
+ class MemoryManager:
5
+ """Memory management for the autonomous AI agent
6
+
7
+ This module provides capabilities for:
8
+ 1. Storing and retrieving conversation history
9
+ 2. Managing context windows
10
+ 3. Implementing forgetting mechanisms
11
+ 4. Prioritizing important information
12
+ """
13
+
14
+ def __init__(self, max_history_length: int = 20):
15
+ """Initialize the memory manager
16
+
17
+ Args:
18
+ max_history_length: Maximum number of conversation turns to store
19
+ """
20
+ self.conversation_history = []
21
+ self.max_history_length = max_history_length
22
+ self.important_facts = []
23
+ self.max_facts = 50
24
+ self.session_data = {}
25
+
26
+ def add_message(self, role: str, content: str) -> None:
27
+ """Add a message to the conversation history
28
+
29
+ Args:
30
+ role: The role of the message sender (user or assistant)
31
+ content: The content of the message
32
+ """
33
+ self.conversation_history.append({
34
+ "role": role,
35
+ "content": content,
36
+ "timestamp": time.time()
37
+ })
38
+
39
+ # Trim history if it gets too long
40
+ if len(self.conversation_history) > self.max_history_length * 2:
41
+ self.conversation_history = self.conversation_history[-self.max_history_length*2:]
42
+
43
+ def get_conversation_history(self, max_turns: Optional[int] = None) -> List[Dict[str, Any]]:
44
+ """Get the conversation history
45
+
46
+ Args:
47
+ max_turns: Maximum number of turns to retrieve (None for all)
48
+
49
+ Returns:
50
+ List of conversation messages
51
+ """
52
+ if max_turns is None:
53
+ return self.conversation_history
54
+ else:
55
+ # Calculate the number of messages (2 messages per turn)
56
+ max_messages = max_turns * 2
57
+ return self.conversation_history[-max_messages:]
58
+
59
+ def format_conversation_for_prompt(self, max_turns: Optional[int] = None) -> str:
60
+ """Format the conversation history for inclusion in a prompt
61
+
62
+ Args:
63
+ max_turns: Maximum number of turns to include
64
+
65
+ Returns:
66
+ Formatted conversation string
67
+ """
68
+ history = self.get_conversation_history(max_turns)
69
+ formatted = ""
70
+
71
+ for msg in history:
72
+ formatted += f"{msg['role']}: {msg['content']}\n"
73
+
74
+ return formatted
75
+
76
+ def add_important_fact(self, fact: str, source: str) -> None:
77
+ """Add an important fact to memory
78
+
79
+ Args:
80
+ fact: The important fact to remember
81
+ source: The source of the fact (e.g., user, inference)
82
+ """
83
+ self.important_facts.append({
84
+ "fact": fact,
85
+ "source": source,
86
+ "timestamp": time.time()
87
+ })
88
+
89
+ # Trim facts if they get too numerous
90
+ if len(self.important_facts) > self.max_facts:
91
+ self.important_facts = self.important_facts[-self.max_facts:]
92
+
93
+ def get_important_facts(self) -> List[Dict[str, Any]]:
94
+ """Get the list of important facts
95
+
96
+ Returns:
97
+ List of important facts
98
+ """
99
+ return self.important_facts
100
+
101
+ def format_facts_for_prompt(self) -> str:
102
+ """Format important facts for inclusion in a prompt
103
+
104
+ Returns:
105
+ Formatted facts string
106
+ """
107
+ if not self.important_facts:
108
+ return ""
109
+
110
+ formatted = "Important information I know about the user and context:\n"
111
+
112
+ # Sort facts by timestamp (newest first)
113
+ sorted_facts = sorted(self.important_facts, key=lambda x: x.get('timestamp', 0), reverse=True)
114
+
115
+ # Group facts by source
116
+ user_facts = [fact for fact in sorted_facts if fact.get('source') == 'user']
117
+ inference_facts = [fact for fact in sorted_facts if fact.get('source') == 'inference']
118
+
119
+ # Add user facts first (they're more reliable)
120
+ for i, fact in enumerate(user_facts):
121
+ formatted += f"{i+1}. {fact['fact']} (from user)\n"
122
+
123
+ # Then add inference facts
124
+ start_idx = len(user_facts) + 1
125
+ for i, fact in enumerate(inference_facts):
126
+ formatted += f"{start_idx + i}. {fact['fact']} (inferred)\n"
127
+
128
+ return formatted
129
+
130
+ def store_session_data(self, key: str, value: Any) -> None:
131
+ """Store data for the current session
132
+
133
+ Args:
134
+ key: The key to store the data under
135
+ value: The data to store
136
+ """
137
+ self.session_data[key] = {
138
+ "value": value,
139
+ "timestamp": time.time()
140
+ }
141
+
142
+ def get_session_data(self, key: str) -> Optional[Any]:
143
+ """Retrieve data from the current session
144
+
145
+ Args:
146
+ key: The key to retrieve data for
147
+
148
+ Returns:
149
+ The stored data, or None if not found
150
+ """
151
+ if key in self.session_data:
152
+ return self.session_data[key]["value"]
153
+ else:
154
+ return None
155
+
156
+ def clear_conversation_history(self) -> None:
157
+ """Clear the conversation history"""
158
+ self.conversation_history = []
159
+
160
+ def clear_all_memory(self) -> None:
161
+ """Clear all memory (conversation history, facts, and session data)"""
162
+ self.conversation_history = []
163
+ self.important_facts = []
164
+ self.session_data = {}
165
+
166
+ def get_memory_stats(self) -> Dict[str, Any]:
167
+ """Get statistics about the agent's memory usage
168
+
169
+ Returns:
170
+ Dictionary containing memory statistics
171
+ """
172
+ return {
173
+ "conversation_turns": len(self.conversation_history) // 2,
174
+ "important_facts": len(self.important_facts),
175
+ "session_data_keys": list(self.session_data.keys()),
176
+ "memory_usage": {
177
+ "conversation": len(str(self.conversation_history)),
178
+ "facts": len(str(self.important_facts)),
179
+ "session": len(str(self.session_data))
180
+ }
181
+ }
agent_reasoning.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from typing import Dict, List, Any, Optional, Union
3
+
4
+ class ReasoningEngine:
5
+ """Reasoning engine for the autonomous AI agent
6
+
7
+ This module provides advanced reasoning capabilities including:
8
+ 1. Chain-of-thought reasoning
9
+ 2. Task decomposition
10
+ 3. Self-reflection
11
+ 4. Decision making
12
+ """
13
+
14
+ def __init__(self, model, tokenizer, device="cpu"):
15
+ """Initialize the reasoning engine
16
+
17
+ Args:
18
+ model: The language model to use for reasoning
19
+ tokenizer: The tokenizer for the language model
20
+ device: The device to run the model on (cpu or cuda)
21
+ """
22
+ self.model = model
23
+ self.tokenizer = tokenizer
24
+ self.device = device
25
+
26
+ def generate_text(self, prompt: str, max_length: int = 512, temperature: float = 0.7) -> str:
27
+ """Generate text using the language model
28
+
29
+ Args:
30
+ prompt: The input prompt for the model
31
+ max_length: Maximum length of the generated text
32
+ temperature: Temperature for sampling (higher = more random)
33
+
34
+ Returns:
35
+ Generated text response
36
+ """
37
+ inputs = self.tokenizer(prompt, return_tensors="pt").to(self.device)
38
+
39
+ # Generate response
40
+ with torch.no_grad():
41
+ outputs = self.model.generate(
42
+ inputs["input_ids"],
43
+ max_length=max_length,
44
+ num_return_sequences=1,
45
+ temperature=temperature,
46
+ top_p=0.9,
47
+ do_sample=True
48
+ )
49
+
50
+ # Decode and return the response
51
+ response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
52
+ return response
53
+
54
+ def chain_of_thought(self, query: str) -> Dict[str, str]:
55
+ """Implement chain-of-thought reasoning
56
+
57
+ Args:
58
+ query: User query to reason about
59
+
60
+ Returns:
61
+ Dictionary containing reasoning steps and final answer
62
+ """
63
+ # Construct a prompt that encourages step-by-step reasoning
64
+ reasoning_prompt = f"""I am an autonomous AI agent called ResuRank. I need to think through this step by step to provide the best response.
65
+
66
+ Query: {query}
67
+
68
+ Let me reason through this carefully:
69
+ 1. First, I'll identify the key parts of this query.
70
+ 2. Then, I'll consider what information I need to answer it.
71
+ 3. Next, I'll analyze the implications and context.
72
+ 4. Finally, I'll formulate a comprehensive response.
73
+
74
+ Step-by-step reasoning:
75
+ 1. """
76
+
77
+ # Generate the reasoning steps
78
+ reasoning = self.generate_text(reasoning_prompt, max_length=1024)
79
+
80
+ # Analyze the reasoning for completeness
81
+ if len(reasoning.split('\n')) < 3 or len(reasoning) < 100:
82
+ # If reasoning is too short, try to expand it
83
+ expansion_prompt = f"""My current reasoning is:
84
+ {reasoning}
85
+
86
+ Let me expand on this with more detailed analysis:
87
+ """
88
+ additional_reasoning = self.generate_text(expansion_prompt, max_length=512)
89
+ reasoning = f"{reasoning}\n\n{additional_reasoning}"
90
+
91
+ # Extract the final answer after reasoning
92
+ answer_prompt = f"""Based on my detailed reasoning:
93
+ {reasoning}
94
+
95
+ I will now provide a clear, helpful, and comprehensive response to the query: {query}
96
+
97
+ My final answer is:"""
98
+
99
+ final_answer = self.generate_text(answer_prompt, max_length=768)
100
+
101
+ # Check if the answer addresses the query adequately
102
+ if len(final_answer) < 50:
103
+ # If answer is too short, try to improve it
104
+ improvement_prompt = f"""My current answer is:
105
+ {final_answer}
106
+
107
+ This answer is too brief. Let me provide a more comprehensive response to the query: {query}
108
+
109
+ Improved answer:"""
110
+ final_answer = self.generate_text(improvement_prompt, max_length=768)
111
+
112
+ return {
113
+ "reasoning": reasoning,
114
+ "answer": final_answer
115
+ }
116
+
117
+ def decompose_task(self, task_description: str) -> List[str]:
118
+ """Decompose a complex task into smaller subtasks
119
+
120
+ Args:
121
+ task_description: Description of the task to decompose
122
+
123
+ Returns:
124
+ List of subtask descriptions
125
+ """
126
+ decomposition_prompt = f"""I need to break down this complex task into smaller, manageable subtasks:
127
+
128
+ Task: {task_description}
129
+
130
+ Subtasks:
131
+ 1. """
132
+
133
+ decomposition = self.generate_text(decomposition_prompt, max_length=1024)
134
+
135
+ # Parse the decomposition into a list of subtasks
136
+ subtasks = []
137
+ for line in decomposition.split('\n'):
138
+ line = line.strip()
139
+ if line and (line[0].isdigit() or line.startswith('- ')):
140
+ # Remove numbering or bullet points
141
+ cleaned_line = re.sub(r'^\d+\.\s*|^-\s*', '', line).strip()
142
+ if cleaned_line:
143
+ subtasks.append(cleaned_line)
144
+
145
+ return subtasks
146
+
147
+ def self_reflect(self, action: str, outcome: str) -> Dict[str, str]:
148
+ """Perform self-reflection on an action and its outcome
149
+
150
+ Args:
151
+ action: The action that was taken
152
+ outcome: The outcome of the action
153
+
154
+ Returns:
155
+ Dictionary containing reflection and improvement suggestions
156
+ """
157
+ reflection_prompt = f"""I need to reflect on this action and its outcome:
158
+
159
+ Action: {action}
160
+ Outcome: {outcome}
161
+
162
+ Reflection:
163
+ """
164
+
165
+ reflection = self.generate_text(reflection_prompt, max_length=512)
166
+
167
+ improvement_prompt = f"""Based on my reflection:
168
+ {reflection}
169
+
170
+ How I can improve next time:
171
+ """
172
+
173
+ improvement = self.generate_text(improvement_prompt, max_length=512)
174
+
175
+ return {
176
+ "reflection": reflection,
177
+ "improvement": improvement
178
+ }
179
+
180
+ def make_decision(self, options: List[str], context: str) -> Dict[str, Any]:
181
+ """Make a decision among multiple options
182
+
183
+ Args:
184
+ options: List of options to choose from
185
+ context: Context information for the decision
186
+
187
+ Returns:
188
+ Dictionary containing the chosen option and reasoning
189
+ """
190
+ options_text = "\n".join([f"{i+1}. {option}" for i, option in enumerate(options)])
191
+
192
+ decision_prompt = f"""I need to make a decision based on the following context and options:
193
+
194
+ Context: {context}
195
+
196
+ Options:
197
+ {options_text}
198
+
199
+ Let me analyze each option:
200
+ """
201
+
202
+ analysis = self.generate_text(decision_prompt, max_length=1024)
203
+
204
+ conclusion_prompt = f"""Based on my analysis:
205
+ {analysis}
206
+
207
+ The best option is number:"""
208
+
209
+ conclusion = self.generate_text(conclusion_prompt, max_length=128)
210
+
211
+ # Try to extract the chosen option number
212
+ try:
213
+ option_num = int(re.search(r'\d+', conclusion).group()) - 1
214
+ chosen_option = options[option_num] if 0 <= option_num < len(options) else options[0]
215
+ except (AttributeError, ValueError, IndexError):
216
+ # Default to first option if parsing fails
217
+ chosen_option = options[0]
218
+
219
+ return {
220
+ "chosen_option": chosen_option,
221
+ "analysis": analysis,
222
+ "conclusion": conclusion
223
+ }
agent_tasks.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import time
4
+ import json
5
+ from typing import Dict, List, Any, Optional, Union
6
+
7
+ class TaskExecutor:
8
+ """Task execution engine for the autonomous AI agent
9
+
10
+ This module provides capabilities for executing various tasks including:
11
+ 1. Task planning and execution
12
+ 2. Progress tracking
13
+ 3. Result formatting
14
+ 4. Error handling
15
+ """
16
+
17
+ def __init__(self, reasoning_engine):
18
+ """Initialize the task executor
19
+
20
+ Args:
21
+ reasoning_engine: The reasoning engine to use for task planning
22
+ """
23
+ self.reasoning_engine = reasoning_engine
24
+ self.current_task = None
25
+ self.task_status = "idle"
26
+ self.task_history = []
27
+ self.max_history_length = 10
28
+
29
+ def execute_task(self, task_description: str) -> Dict[str, Any]:
30
+ """Execute a task based on the description
31
+
32
+ Args:
33
+ task_description: Description of the task to execute
34
+
35
+ Returns:
36
+ Dictionary containing task results and status
37
+ """
38
+ self.current_task = task_description
39
+ self.task_status = "in_progress"
40
+ start_time = time.time()
41
+
42
+ try:
43
+ # First, analyze the task to understand its requirements and constraints
44
+ analysis_prompt = f"""I need to analyze this task to understand its requirements and constraints:
45
+
46
+ Task: {task_description}
47
+
48
+ Task Analysis:
49
+ 1. What is the main objective of this task?
50
+ 2. What are the key requirements?
51
+ 3. What constraints or limitations should I be aware of?
52
+ 4. What resources or information do I need to complete this task?
53
+
54
+ Analysis:"""
55
+
56
+ task_analysis = self.reasoning_engine.generate_text(analysis_prompt, max_length=768)
57
+
58
+ # Decompose the task into subtasks with the analysis in mind
59
+ decomposition_prompt = f"""Based on my analysis of the task:
60
+ {task_analysis}
61
+
62
+ I need to break down this task into smaller, manageable subtasks:
63
+
64
+ Task: {task_description}
65
+
66
+ Subtasks:
67
+ 1. """
68
+
69
+ decomposition = self.reasoning_engine.generate_text(decomposition_prompt, max_length=1024)
70
+
71
+ # Parse the decomposition into a list of subtasks
72
+ subtasks = []
73
+ for line in decomposition.split('\n'):
74
+ line = line.strip()
75
+ if line and (line[0].isdigit() or line.startswith('- ')):
76
+ # Remove numbering or bullet points
77
+ cleaned_line = re.sub(r'^\d+\.\s*|^-\s*', '', line).strip()
78
+ if cleaned_line:
79
+ subtasks.append(cleaned_line)
80
+
81
+ # If parsing failed, use the reasoning engine's decompose_task method as fallback
82
+ if not subtasks:
83
+ subtasks = self.reasoning_engine.decompose_task(task_description)
84
+
85
+ # Generate a detailed plan for executing the task
86
+ planning_prompt = f"""I need to create a detailed plan to execute this task:
87
+ {task_description}
88
+
89
+ Task Analysis:
90
+ {task_analysis}
91
+
92
+ The task has been broken down into these subtasks:
93
+ {json.dumps(subtasks, indent=2)}
94
+
95
+ Detailed step-by-step plan (including how to handle potential issues):
96
+ 1. """
97
+
98
+ plan = self.reasoning_engine.generate_text(planning_prompt, max_length=1024)
99
+
100
+ # Track progress of each subtask with more detailed execution
101
+ subtask_results = []
102
+ for i, subtask in enumerate(subtasks):
103
+ # Update status
104
+ self.task_status = f"in_progress ({i+1}/{len(subtasks)})"
105
+
106
+ # Execute the subtask with more context
107
+ subtask_prompt = f"""I am executing this subtask as part of the larger task:
108
+
109
+ Main Task: {task_description}
110
+
111
+ Current Subtask ({i+1}/{len(subtasks)}): {subtask}
112
+
113
+ Previous Results: {json.dumps([r['result'] for r in subtask_results], indent=2) if subtask_results else 'None yet'}
114
+
115
+ I will now execute this subtask carefully and report the detailed results:"""
116
+
117
+ result = self.reasoning_engine.generate_text(subtask_prompt, max_length=768)
118
+
119
+ # Evaluate the quality of the result
120
+ evaluation_prompt = f"""I need to evaluate the quality of my execution of this subtask:
121
+
122
+ Subtask: {subtask}
123
+
124
+ Execution Result: {result}
125
+
126
+ Evaluation (rate from 1-10 and explain):"""
127
+
128
+ evaluation = self.reasoning_engine.generate_text(evaluation_prompt, max_length=256)
129
+
130
+ subtask_results.append({
131
+ "subtask": subtask,
132
+ "result": result,
133
+ "evaluation": evaluation
134
+ })
135
+
136
+ # Compile the final results with synthesis
137
+ compilation_prompt = f"""I have executed all subtasks for the main task:
138
+ {task_description}
139
+
140
+ Here are the results of each subtask:
141
+ {json.dumps(subtask_results, indent=2)}
142
+
143
+ I need to synthesize these results into a coherent final result that addresses the original task completely.
144
+
145
+ Final synthesized result:"""
146
+
147
+ final_result = self.reasoning_engine.generate_text(compilation_prompt, max_length=1024)
148
+
149
+ # Self-reflection on the task execution
150
+ reflection_prompt = f"""I need to reflect on my execution of this task:
151
+
152
+ Task: {task_description}
153
+
154
+ My approach: {plan}
155
+
156
+ Final result: {final_result}
157
+
158
+ Reflection on what went well and what could be improved:"""
159
+
160
+ reflection = self.reasoning_engine.generate_text(reflection_prompt, max_length=512)
161
+
162
+ self.task_status = "completed"
163
+ execution_time = time.time() - start_time
164
+
165
+ # Add to task history
166
+ task_record = {
167
+ "task": task_description,
168
+ "plan": plan,
169
+ "subtasks": subtask_results,
170
+ "result": final_result,
171
+ "reflection": reflection,
172
+ "status": self.task_status,
173
+ "execution_time": execution_time,
174
+ "timestamp": time.time()
175
+ }
176
+
177
+ self.task_history.append(task_record)
178
+
179
+ # Trim history if it gets too long
180
+ if len(self.task_history) > self.max_history_length:
181
+ self.task_history = self.task_history[-self.max_history_length:]
182
+
183
+ return task_record
184
+
185
+ except Exception as e:
186
+ self.task_status = "failed"
187
+ error_message = str(e)
188
+
189
+ # Add failed task to history
190
+ task_record = {
191
+ "task": task_description,
192
+ "status": self.task_status,
193
+ "error": error_message,
194
+ "timestamp": time.time()
195
+ }
196
+
197
+ self.task_history.append(task_record)
198
+
199
+ return task_record
200
+
201
+ def get_task_status(self) -> Dict[str, Any]:
202
+ """Get the current status of task execution
203
+
204
+ Returns:
205
+ Dictionary containing task status information
206
+ """
207
+ return {
208
+ "current_task": self.current_task,
209
+ "status": self.task_status,
210
+ "history_length": len(self.task_history)
211
+ }
212
+
213
+ def get_task_history(self) -> List[Dict[str, Any]]:
214
+ """Get the history of executed tasks
215
+
216
+ Returns:
217
+ List of task records
218
+ """
219
+ return self.task_history
220
+
221
+ def cancel_task(self) -> Dict[str, Any]:
222
+ """Cancel the currently executing task
223
+
224
+ Returns:
225
+ Dictionary containing cancellation status
226
+ """
227
+ if self.task_status == "in_progress":
228
+ self.task_status = "cancelled"
229
+
230
+ # Update the last task record in history
231
+ if self.task_history:
232
+ self.task_history[-1]["status"] = "cancelled"
233
+
234
+ return {
235
+ "task": self.current_task,
236
+ "status": self.task_status,
237
+ "message": "Task cancelled successfully"
238
+ }
239
+ else:
240
+ return {
241
+ "task": self.current_task,
242
+ "status": self.task_status,
243
+ "message": "No task in progress to cancel"
244
+ }
app.py ADDED
@@ -0,0 +1,837 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import torch
4
+ import re
5
+ import time
6
+ from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
7
+ from huggingface_hub import hf_hub_download, snapshot_download
8
+ import json
9
+ from typing import Dict, List, Any, Optional, Union
10
+
11
+ # Import agent modules
12
+ from agent_reasoning import ReasoningEngine
13
+ from agent_tasks import TaskExecutor
14
+ from agent_memory import MemoryManager
15
+
16
+ class ResuRankAgent:
17
+ """Autonomous AI Agent similar to Manus AI
18
+
19
+ This agent can:
20
+ 1. Process user queries and generate responses
21
+ 2. Perform reasoning through chain-of-thought
22
+ 3. Execute tasks based on user instructions
23
+ 4. Maintain conversation context
24
+ """
25
+
26
+ def __init__(self, model_id="distilgpt2", use_cache=True, test_mode=False):
27
+ """Initialize the ResuRank Agent
28
+
29
+ Args:
30
+ model_id: Hugging Face model ID to use for the agent
31
+ use_cache: Whether to use cached models from Hugging Face Hub
32
+ test_mode: Whether to run in test mode with minimal resources
33
+ """
34
+ self.model_id = model_id
35
+ self.test_mode = test_mode
36
+
37
+ # Use CPU for test mode, otherwise check for CUDA
38
+ if test_mode:
39
+ self.device = "cpu"
40
+ print("Running in test mode on CPU with minimal resources")
41
+ else:
42
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
43
+ print(f"Using device: {self.device}")
44
+
45
+ # Load model and tokenizer from Hugging Face Hub
46
+ print(f"Loading model {model_id} from Hugging Face Hub...")
47
+ try:
48
+ # Configure model loading parameters based on mode
49
+ model_kwargs = {
50
+ "torch_dtype": torch.float32, # Use float32 for better compatibility
51
+ }
52
+
53
+ # Check if Accelerate is available for low_cpu_mem_usage and device_map
54
+ try:
55
+ import accelerate
56
+ model_kwargs["low_cpu_mem_usage"] = True
57
+ # Add device map only if not in test mode
58
+ if not test_mode:
59
+ model_kwargs["device_map"] = "auto"
60
+ if self.device == "cuda":
61
+ model_kwargs["torch_dtype"] = torch.float16
62
+ except ImportError:
63
+ print("Accelerate library not found, disabling low_cpu_mem_usage and device_map")
64
+ # Continue without these options
65
+
66
+ # Add cache directory if using cache
67
+ if use_cache:
68
+ model_kwargs["cache_dir"] = "./.cache"
69
+ print("Using cached models if available...")
70
+ self.tokenizer = AutoTokenizer.from_pretrained(model_id, cache_dir="./.cache")
71
+ else:
72
+ print("Downloading models from Hugging Face Hub...")
73
+ self.tokenizer = AutoTokenizer.from_pretrained(model_id)
74
+
75
+ # Load the model with optimized parameters
76
+ self.model = AutoModelForCausalLM.from_pretrained(model_id, **model_kwargs)
77
+
78
+ print(f"Successfully loaded model {model_id}")
79
+ except Exception as e:
80
+ print(f"Error loading model: {str(e)}")
81
+ print("Falling back to smaller model...")
82
+ fallback_model = "distilgpt2" # Use a smaller model as fallback
83
+ self.model_id = fallback_model
84
+
85
+ try:
86
+ # Try loading the fallback model with minimal parameters
87
+ self.tokenizer = AutoTokenizer.from_pretrained(fallback_model, cache_dir="./.cache")
88
+ self.model = AutoModelForCausalLM.from_pretrained(
89
+ fallback_model,
90
+ torch_dtype=torch.float32,
91
+ low_cpu_mem_usage=True,
92
+ cache_dir="./.cache"
93
+ )
94
+ print(f"Successfully loaded fallback model {fallback_model}")
95
+ except Exception as fallback_error:
96
+ print(f"Error loading fallback model: {str(fallback_error)}")
97
+ raise RuntimeError("Failed to load both primary and fallback models")
98
+
99
+ # Initialize agent components
100
+ self.reasoning_engine = ReasoningEngine(self.model, self.tokenizer, self.device)
101
+ self.memory_manager = MemoryManager(max_history_length=20)
102
+ self.task_executor = TaskExecutor(self.reasoning_engine)
103
+
104
+ def process_query(self, query: str, use_reasoning: bool = True) -> Dict[str, Any]:
105
+ """Process a user query and generate a response
106
+
107
+ Args:
108
+ query: User query text
109
+ use_reasoning: Whether to use chain-of-thought reasoning
110
+
111
+ Returns:
112
+ Dictionary containing response and metadata
113
+ """
114
+ # Add query to conversation history
115
+ self.memory_manager.add_message("user", query)
116
+
117
+ start_time = time.time()
118
+
119
+ # Check if this is a task execution request
120
+ is_task_request = self._is_task_request(query)
121
+
122
+ # Process the query with appropriate method
123
+ if is_task_request:
124
+ # Handle as a task execution request
125
+ task_result = self.execute_task(query)
126
+ response = f"I've executed your task. {task_result.get('result', '')}\n\nStatus: {task_result.get('status', 'unknown')}"
127
+ reasoning = task_result.get('plan', '')
128
+ elif use_reasoning:
129
+ # Use chain-of-thought reasoning
130
+ # Enhance with context from memory
131
+ facts = self.memory_manager.format_facts_for_prompt()
132
+ context = self.memory_manager.format_conversation_for_prompt(max_turns=5)
133
+
134
+ # Create an enhanced query with context
135
+ enhanced_query = f"{facts}\n\nRecent conversation:\n{context}\n\nCurrent query: {query}"
136
+
137
+ result = self.reasoning_engine.chain_of_thought(enhanced_query)
138
+ response = result["answer"]
139
+ reasoning = result["reasoning"]
140
+ else:
141
+ # Simple response generation without reasoning
142
+ conversation_prompt = self.memory_manager.format_conversation_for_prompt(max_turns=10)
143
+ facts_prompt = self.memory_manager.format_facts_for_prompt()
144
+
145
+ prompt = f"{facts_prompt}\n\n{conversation_prompt}\nassistant: "
146
+
147
+ response = self.reasoning_engine.generate_text(prompt)
148
+ reasoning = None
149
+
150
+ # Add response to conversation history
151
+ self.memory_manager.add_message("assistant", response)
152
+
153
+ # Extract any important facts from the conversation
154
+ self._extract_facts(query, response)
155
+
156
+ processing_time = time.time() - start_time
157
+
158
+ return {
159
+ "response": response,
160
+ "reasoning": reasoning,
161
+ "processing_time": processing_time,
162
+ "timestamp": time.time()
163
+ }
164
+
165
+ def _is_task_request(self, query: str) -> bool:
166
+ """Determine if a query is a task execution request
167
+
168
+ Args:
169
+ query: The user query
170
+
171
+ Returns:
172
+ True if the query appears to be a task request, False otherwise
173
+ """
174
+ # Keywords that suggest a task execution request
175
+ task_keywords = [
176
+ "execute", "perform", "run", "do", "complete", "finish",
177
+ "task", "job", "work", "action", "operation", "function",
178
+ "can you", "please", "help me", "i need", "i want"
179
+ ]
180
+
181
+ # Check if query contains task-related keywords
182
+ query_lower = query.lower()
183
+ for keyword in task_keywords:
184
+ if keyword in query_lower:
185
+ return True
186
+
187
+ return False
188
+
189
+ def _extract_facts(self, query: str, response: str) -> None:
190
+ """Extract important facts from the conversation
191
+
192
+ Args:
193
+ query: User query
194
+ response: Agent response
195
+ """
196
+ # Extract personal information
197
+ self._extract_personal_info(query)
198
+
199
+ # Extract preferences
200
+ self._extract_preferences(query)
201
+
202
+ # Extract task-related information
203
+ self._extract_task_info(query)
204
+
205
+ # Use the reasoning engine to identify important facts
206
+ self._extract_with_reasoning(query, response)
207
+
208
+ def _extract_personal_info(self, text: str) -> None:
209
+ """Extract personal information from text
210
+
211
+ Args:
212
+ text: Text to extract information from
213
+ """
214
+ text_lower = text.lower()
215
+
216
+ # Extract name
217
+ if "my name is" in text_lower or "i am called" in text_lower or "i'm called" in text_lower:
218
+ name_patterns = [
219
+ r"my name is ([\w\s]+)[.\,]?",
220
+ r"i am called ([\w\s]+)[.\,]?",
221
+ r"i'm called ([\w\s]+)[.\,]?"
222
+ ]
223
+
224
+ for pattern in name_patterns:
225
+ name_match = re.search(pattern, text_lower)
226
+ if name_match:
227
+ name = name_match.group(1).strip()
228
+ self.memory_manager.add_important_fact(f"User's name is {name}", "user")
229
+ break
230
+
231
+ # Extract location
232
+ if "i am from" in text_lower or "i'm from" in text_lower or "i live in" in text_lower:
233
+ location_patterns = [
234
+ r"i am from ([\w\s]+)[.\,]?",
235
+ r"i'm from ([\w\s]+)[.\,]?",
236
+ r"i live in ([\w\s]+)[.\,]?"
237
+ ]
238
+
239
+ for pattern in location_patterns:
240
+ location_match = re.search(pattern, text_lower)
241
+ if location_match:
242
+ location = location_match.group(1).strip()
243
+ self.memory_manager.add_important_fact(f"User is from {location}", "user")
244
+ break
245
+
246
+ # Extract profession/occupation
247
+ if "i work as" in text_lower or "i am a" in text_lower or "i'm a" in text_lower:
248
+ profession_patterns = [
249
+ r"i work as a[n]? ([\w\s]+)[.\,]?",
250
+ r"i am a[n]? ([\w\s]+)[.\,]?",
251
+ r"i'm a[n]? ([\w\s]+)[.\,]?"
252
+ ]
253
+
254
+ for pattern in profession_patterns:
255
+ profession_match = re.search(pattern, text_lower)
256
+ if profession_match:
257
+ profession = profession_match.group(1).strip()
258
+ self.memory_manager.add_important_fact(f"User works as a {profession}", "user")
259
+ break
260
+
261
+ def _extract_preferences(self, text: str) -> None:
262
+ """Extract user preferences from text
263
+
264
+ Args:
265
+ text: Text to extract information from
266
+ """
267
+ text_lower = text.lower()
268
+
269
+ # Extract likes
270
+ if "i like" in text_lower or "i love" in text_lower or "i enjoy" in text_lower:
271
+ like_patterns = [
272
+ r"i like ([\w\s]+)[.\,]?",
273
+ r"i love ([\w\s]+)[.\,]?",
274
+ r"i enjoy ([\w\s]+)[.\,]?"
275
+ ]
276
+
277
+ for pattern in like_patterns:
278
+ like_match = re.search(pattern, text_lower)
279
+ if like_match:
280
+ like = like_match.group(1).strip()
281
+ self.memory_manager.add_important_fact(f"User likes {like}", "user")
282
+ break
283
+
284
+ # Extract dislikes
285
+ if "i don't like" in text_lower or "i hate" in text_lower or "i dislike" in text_lower:
286
+ dislike_patterns = [
287
+ r"i don't like ([\w\s]+)[.\,]?",
288
+ r"i hate ([\w\s]+)[.\,]?",
289
+ r"i dislike ([\w\s]+)[.\,]?"
290
+ ]
291
+
292
+ for pattern in dislike_patterns:
293
+ dislike_match = re.search(pattern, text_lower)
294
+ if dislike_match:
295
+ dislike = dislike_match.group(1).strip()
296
+ self.memory_manager.add_important_fact(f"User dislikes {dislike}", "user")
297
+ break
298
+
299
+ def _extract_task_info(self, text: str) -> None:
300
+ """Extract task-related information from text
301
+
302
+ Args:
303
+ text: Text to extract information from
304
+ """
305
+ text_lower = text.lower()
306
+
307
+ # Extract goals
308
+ if "my goal is" in text_lower or "i want to" in text_lower or "i need to" in text_lower:
309
+ goal_patterns = [
310
+ r"my goal is to ([\w\s]+)[.\,]?",
311
+ r"i want to ([\w\s]+)[.\,]?",
312
+ r"i need to ([\w\s]+)[.\,]?"
313
+ ]
314
+
315
+ for pattern in goal_patterns:
316
+ goal_match = re.search(pattern, text_lower)
317
+ if goal_match:
318
+ goal = goal_match.group(1).strip()
319
+ self.memory_manager.add_important_fact(f"User's goal is to {goal}", "user")
320
+ break
321
+
322
+ def run_test_case(self) -> Dict[str, Any]:
323
+ """Run a test case to demonstrate the agent's capabilities with minimal resources
324
+
325
+ This method is useful for testing the agent on resource-constrained environments
326
+ like Hugging Face Spaces or during development.
327
+
328
+ Returns:
329
+ Dictionary containing test results and performance metrics
330
+ """
331
+ print("Running test case with minimal resources...")
332
+ start_time = time.time()
333
+
334
+ # Simple test query that doesn't require extensive reasoning
335
+ test_query = "What can you help me with?"
336
+
337
+ # Process the query with minimal settings
338
+ test_response = self.process_query(test_query, use_reasoning=False)
339
+
340
+ # Calculate performance metrics
341
+ processing_time = time.time() - start_time
342
+ memory_usage = self._estimate_memory_usage()
343
+
344
+ # Return test results
345
+ return {
346
+ "status": "success",
347
+ "model_id": self.model_id,
348
+ "device": self.device,
349
+ "test_query": test_query,
350
+ "test_response": test_response["response"],
351
+ "processing_time": processing_time,
352
+ "memory_usage_mb": memory_usage,
353
+ "timestamp": time.time()
354
+ }
355
+
356
+ def _estimate_memory_usage(self) -> float:
357
+ """Estimate the memory usage of the model
358
+
359
+ Returns:
360
+ Estimated memory usage in MB
361
+ """
362
+ try:
363
+ import psutil
364
+ process = psutil.Process(os.getpid())
365
+ memory_info = process.memory_info()
366
+ return memory_info.rss / (1024 * 1024) # Convert to MB
367
+ except ImportError:
368
+ return 0.0 # Return 0 if psutil is not available
369
+
370
+ def _extract_with_reasoning(self, query: str, response: str) -> None:
371
+ """Use the reasoning engine to extract important facts
372
+
373
+ Args:
374
+ query: User query
375
+ response: Agent response
376
+ """
377
+ # Only use this for longer queries to avoid unnecessary processing
378
+ if len(query) < 50:
379
+ return
380
+
381
+ extraction_prompt = f"""Extract important facts from this conversation:
382
+
383
+ User: {query}
384
+ Assistant: {response}
385
+
386
+ List of important facts (one per line):
387
+ 1. """
388
+
389
+ try:
390
+ facts_text = self.reasoning_engine.generate_text(extraction_prompt, max_length=256)
391
+
392
+ # Parse the facts
393
+ for line in facts_text.split('\n'):
394
+ line = line.strip()
395
+ if line and (line[0].isdigit() or line.startswith('- ')):
396
+ # Remove numbering or bullet points
397
+ fact = re.sub(r'^\d+\.\s*|^-\s*', '', line).strip()
398
+ if fact and len(fact) > 10: # Only add substantial facts
399
+ self.memory_manager.add_important_fact(fact, "inference")
400
+ except Exception as e:
401
+ print(f"Error extracting facts with reasoning: {str(e)}")
402
+ # Continue without adding facts
403
+
404
+
405
+
406
+ def execute_task(self, task_description: str) -> Dict[str, Any]:
407
+ """Execute a task based on the description
408
+
409
+ Args:
410
+ task_description: Description of the task to execute
411
+
412
+ Returns:
413
+ Dictionary containing task results and status
414
+ """
415
+ return self.task_executor.execute_task(task_description)
416
+
417
+ def get_status(self) -> Dict[str, Any]:
418
+ """Get the current status of the agent
419
+
420
+ Returns:
421
+ Dictionary containing agent status information
422
+ """
423
+ memory_stats = self.memory_manager.get_memory_stats()
424
+ task_status = self.task_executor.get_task_status()
425
+
426
+ return {
427
+ "model_id": self.model_id,
428
+ "device": self.device,
429
+ "conversation_turns": memory_stats["conversation_turns"],
430
+ "important_facts": memory_stats["important_facts"],
431
+ "current_task": task_status["current_task"],
432
+ "task_status": task_status["status"]
433
+ }
434
+
435
+ def clear_conversation(self) -> None:
436
+ """Clear the conversation history"""
437
+ self.memory_manager.clear_conversation_history()
438
+
439
+ def process_document(self, document_text: str, document_type: str = "resume") -> Dict[str, Any]:
440
+ """Process a document (like a resume) and extract information
441
+
442
+ Args:
443
+ document_text: The text content of the document
444
+ document_type: The type of document (e.g., "resume", "job_description")
445
+
446
+ Returns:
447
+ Dictionary containing extracted information and analysis
448
+ """
449
+ self.memory_manager.store_session_data(f"last_{document_type}", document_text)
450
+ start_time = time.time()
451
+
452
+ # Create a prompt for document analysis
453
+ analysis_prompt = f"""I need to analyze this {document_type} document and extract key information:
454
+
455
+ {document_text}
456
+
457
+ Detailed analysis:"""
458
+
459
+ # Generate analysis using reasoning engine
460
+ analysis = self.reasoning_engine.generate_text(analysis_prompt, max_length=1024)
461
+
462
+ # Extract structured information based on document type
463
+ if document_type.lower() == "resume":
464
+ extraction_prompt = f"""Based on this resume:
465
+ {document_text}
466
+
467
+ Extract the following information in a structured format:
468
+ 1. Name:
469
+ 2. Contact Information:
470
+ 3. Education:
471
+ 4. Work Experience:
472
+ 5. Skills:
473
+ 6. Projects:
474
+ 7. Certifications:
475
+ 8. Languages:
476
+ 9. Key Strengths:
477
+ """
478
+ elif document_type.lower() == "job_description":
479
+ extraction_prompt = f"""Based on this job description:
480
+ {document_text}
481
+
482
+ Extract the following information in a structured format:
483
+ 1. Job Title:
484
+ 2. Company:
485
+ 3. Location:
486
+ 4. Required Skills:
487
+ 5. Required Experience:
488
+ 6. Education Requirements:
489
+ 7. Responsibilities:
490
+ 8. Benefits:
491
+ 9. Key Qualifications:
492
+ """
493
+ else:
494
+ extraction_prompt = f"""Extract key information from this document:
495
+ {document_text}
496
+
497
+ Key information:
498
+ 1. """
499
+
500
+ # Generate structured extraction
501
+ structured_info = self.reasoning_engine.generate_text(extraction_prompt, max_length=1024)
502
+
503
+ # Add important facts to memory
504
+ self._extract_document_facts(document_text, document_type, structured_info)
505
+
506
+ processing_time = time.time() - start_time
507
+
508
+ return {
509
+ "document_type": document_type,
510
+ "analysis": analysis,
511
+ "structured_info": structured_info,
512
+ "processing_time": processing_time,
513
+ "timestamp": time.time()
514
+ }
515
+
516
+ def _extract_document_facts(self, document_text: str, document_type: str, structured_info: str) -> None:
517
+ """Extract important facts from a document and add them to memory
518
+
519
+ Args:
520
+ document_text: The text content of the document
521
+ document_type: The type of document
522
+ structured_info: Structured information extracted from the document
523
+ """
524
+ # Extract key facts based on document type
525
+ if document_type.lower() == "resume":
526
+ # Extract name if present
527
+ name_match = re.search(r"Name:\s*([\w\s]+)\n", structured_info)
528
+ if name_match:
529
+ name = name_match.group(1).strip()
530
+ self.memory_manager.add_important_fact(f"Document contains resume for {name}", "document")
531
+
532
+ # Extract skills
533
+ skills_match = re.search(r"Skills:\s*([\w\s,\.\-\+]+)\n", structured_info)
534
+ if skills_match:
535
+ skills = skills_match.group(1).strip()
536
+ self.memory_manager.add_important_fact(f"Resume shows skills in: {skills}", "document")
537
+
538
+ # Extract education
539
+ education_match = re.search(r"Education:\s*([\w\s,\.\-\+]+)\n", structured_info)
540
+ if education_match:
541
+ education = education_match.group(1).strip()
542
+ self.memory_manager.add_important_fact(f"Resume shows education: {education}", "document")
543
+
544
+ elif document_type.lower() == "job_description":
545
+ # Extract job title
546
+ title_match = re.search(r"Job Title:\s*([\w\s]+)\n", structured_info)
547
+ if title_match:
548
+ title = title_match.group(1).strip()
549
+ self.memory_manager.add_important_fact(f"Document contains job description for {title}", "document")
550
+
551
+ # Extract required skills
552
+ skills_match = re.search(r"Required Skills:\s*([\w\s,\.\-\+]+)\n", structured_info)
553
+ if skills_match:
554
+ skills = skills_match.group(1).strip()
555
+ self.memory_manager.add_important_fact(f"Job requires skills in: {skills}", "document")
556
+
557
+ # Add general document fact
558
+ self.memory_manager.add_important_fact(f"Processed a {document_type} document", "system")
559
+
560
+ def rank_resumes(self, job_description: str, resumes: List[str]) -> Dict[str, Any]:
561
+ """Rank multiple resumes against a job description
562
+
563
+ Args:
564
+ job_description: The job description text
565
+ resumes: List of resume texts to rank
566
+
567
+ Returns:
568
+ Dictionary containing rankings and analysis
569
+ """
570
+ start_time = time.time()
571
+
572
+ # Process the job description first
573
+ job_result = self.process_document(job_description, "job_description")
574
+ job_analysis = job_result["structured_info"]
575
+
576
+ # Process each resume
577
+ resume_results = []
578
+ for i, resume in enumerate(resumes):
579
+ result = self.process_document(resume, "resume")
580
+ resume_results.append({
581
+ "index": i,
582
+ "text": resume,
583
+ "analysis": result["structured_info"]
584
+ })
585
+
586
+ # Create a ranking prompt
587
+ ranking_prompt = f"""I need to rank these resumes based on how well they match the job description.
588
+
589
+ Job Description Analysis:
590
+ {job_analysis}
591
+
592
+ Resumes:
593
+ """
594
+
595
+ for i, result in enumerate(resume_results):
596
+ ranking_prompt += f"\nResume {i+1}:\n{result['analysis']}\n"
597
+
598
+ ranking_prompt += "\nRank these resumes from best to worst match for the job, with detailed reasoning for each:"
599
+
600
+ # Generate the ranking analysis
601
+ ranking_analysis = self.reasoning_engine.generate_text(ranking_prompt, max_length=2048)
602
+
603
+ # Generate a numerical scoring for each resume
604
+ scoring_prompt = f"""Based on my analysis of how well these resumes match the job description:
605
+ {ranking_analysis}
606
+
607
+ Assign a numerical score from 0-100 for each resume, where 100 is a perfect match:
608
+
609
+ Resume 1 Score:"""
610
+
611
+ scores_text = self.reasoning_engine.generate_text(scoring_prompt, max_length=512)
612
+
613
+ # Parse scores (simple regex approach)
614
+ scores = []
615
+ for i in range(len(resume_results)):
616
+ score_match = re.search(fr"Resume {i+1} Score:\s*(\d+)", scores_text)
617
+ if score_match:
618
+ scores.append(int(score_match.group(1)))
619
+ else:
620
+ # Default score if parsing fails
621
+ scores.append(50)
622
+
623
+ # Create the final rankings
624
+ rankings = []
625
+ for i, score in enumerate(scores):
626
+ rankings.append({
627
+ "resume_index": i,
628
+ "score": score,
629
+ "resume_text": resumes[i][:100] + "..." # Truncated for readability
630
+ })
631
+
632
+ # Sort by score (descending)
633
+ rankings.sort(key=lambda x: x["score"], reverse=True)
634
+
635
+ processing_time = time.time() - start_time
636
+
637
+ return {
638
+ "rankings": rankings,
639
+ "analysis": ranking_analysis,
640
+ "job_description": job_description,
641
+ "processing_time": processing_time
642
+ }
643
+
644
+ # Create the Gradio interface
645
+ def create_interface(test_mode=False):
646
+ """Create the Gradio interface for the ResuRank AI Agent
647
+
648
+ Args:
649
+ test_mode: Whether to run in test mode with minimal resources
650
+ """
651
+ # Initialize the agent with appropriate settings
652
+ if test_mode:
653
+ agent = ResuRankAgent(model_id="distilgpt2", use_cache=True, test_mode=True)
654
+ # Run a test case to verify functionality
655
+ test_results = agent.run_test_case()
656
+ print(f"Test results: {test_results}")
657
+ else:
658
+ agent = ResuRankAgent(model_id="google/flan-t5-base", use_cache=True)
659
+
660
+ with gr.Blocks(title="ResuRank AI Agent") as interface:
661
+ gr.Markdown("# ResuRank AI Agent")
662
+ gr.Markdown("An autonomous AI agent that can process queries, perform reasoning, and execute tasks.")
663
+
664
+ with gr.Tab("Chat"):
665
+ chatbot = gr.Chatbot(height=400)
666
+ msg = gr.Textbox(label="Your message", placeholder="Ask me anything...")
667
+ with gr.Row():
668
+ submit_btn = gr.Button("Submit")
669
+ clear_btn = gr.Button("Clear")
670
+
671
+ reasoning_checkbox = gr.Checkbox(label="Use reasoning", value=True)
672
+
673
+ if reasoning_checkbox.value:
674
+ reasoning_output = gr.Textbox(label="Reasoning", interactive=False)
675
+ else:
676
+ reasoning_output = gr.Textbox(label="Reasoning", interactive=False, visible=False)
677
+
678
+ def respond(message, chat_history, use_reasoning):
679
+ if not message.strip():
680
+ return chat_history, "", ""
681
+
682
+ # Process the query
683
+ result = agent.process_query(message, use_reasoning=use_reasoning)
684
+
685
+ # Update chat history
686
+ chat_history.append((message, result["response"]))
687
+
688
+ return chat_history, "", result.get("reasoning", "")
689
+
690
+ def clear_chat():
691
+ agent.clear_conversation()
692
+ return [], "", ""
693
+
694
+ # Set up event handlers
695
+ submit_btn.click(respond, [msg, chatbot, reasoning_checkbox], [chatbot, msg, reasoning_output])
696
+ msg.submit(respond, [msg, chatbot, reasoning_checkbox], [chatbot, msg, reasoning_output])
697
+ clear_btn.click(clear_chat, None, [chatbot, msg, reasoning_output])
698
+ reasoning_checkbox.change(lambda x: gr.update(visible=x), reasoning_checkbox, reasoning_output)
699
+
700
+ with gr.Tab("Task Execution"):
701
+ task_input = gr.Textbox(label="Task Description", placeholder="Describe the task to execute...")
702
+ execute_btn = gr.Button("Execute Task")
703
+
704
+ with gr.Row():
705
+ with gr.Column():
706
+ plan_output = gr.Textbox(label="Execution Plan", interactive=False)
707
+ with gr.Column():
708
+ results_output = gr.Textbox(label="Task Results", interactive=False)
709
+
710
+ task_status = gr.Textbox(label="Task Status", value="idle", interactive=False)
711
+
712
+ def execute_task(task_description):
713
+ if not task_description.strip():
714
+ return "No task provided.", "", "idle"
715
+
716
+ # Execute the task
717
+ result = agent.execute_task(task_description)
718
+
719
+ return result.get("plan", ""), result.get("result", ""), result.get("status", "")
720
+
721
+ # Set up event handlers
722
+ execute_btn.click(execute_task, task_input, [plan_output, results_output, task_status])
723
+
724
+ with gr.Tab("Agent Status"):
725
+ status_btn = gr.Button("Refresh Status")
726
+
727
+ with gr.Row():
728
+ with gr.Column():
729
+ model_info = gr.Textbox(label="Model Information", interactive=False)
730
+ with gr.Column():
731
+ conversation_info = gr.Textbox(label="Conversation Information", interactive=False)
732
+
733
+ def update_status():
734
+ status = agent.get_status()
735
+ model_text = f"Model ID: {status['model_id']}\nDevice: {status['device']}"
736
+
737
+ # Handle important_facts which might be an integer count or a list
738
+ important_facts_count = status['important_facts']
739
+ if isinstance(important_facts_count, list):
740
+ important_facts_count = len(important_facts_count)
741
+
742
+ 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']}"
743
+
744
+ return model_text, conversation_text
745
+
746
+ # Set up event handlers
747
+ status_btn.click(update_status, None, [model_info, conversation_info])
748
+
749
+ # Initialize status on load
750
+ model_info.value, conversation_info.value = update_status()
751
+
752
+ with gr.Tab("Document Processing"):
753
+ with gr.Row():
754
+ with gr.Column():
755
+ document_input = gr.Textbox(label="Document Text", placeholder="Paste resume or job description text here...", lines=10)
756
+ document_type = gr.Radio(["resume", "job_description", "other"], label="Document Type", value="resume")
757
+ process_btn = gr.Button("Process Document")
758
+
759
+ with gr.Row():
760
+ with gr.Column():
761
+ analysis_output = gr.Textbox(label="Document Analysis", interactive=False, lines=10)
762
+ with gr.Column():
763
+ structured_output = gr.Textbox(label="Structured Information", interactive=False, lines=10)
764
+
765
+ def process_document(document_text, doc_type):
766
+ if not document_text.strip():
767
+ return "No document provided.", ""
768
+
769
+ # Process the document
770
+ result = agent.process_document(document_text, doc_type)
771
+
772
+ return result.get("analysis", ""), result.get("structured_info", "")
773
+
774
+ # Set up event handlers
775
+ process_btn.click(process_document, [document_input, document_type], [analysis_output, structured_output])
776
+
777
+ with gr.Tab("Resume Ranking"):
778
+ with gr.Row():
779
+ with gr.Column():
780
+ job_description_input = gr.Textbox(label="Job Description", placeholder="Paste job description here...", lines=8)
781
+
782
+ with gr.Row():
783
+ with gr.Column():
784
+ resume1_input = gr.Textbox(label="Resume 1", placeholder="Paste first resume here...", lines=6)
785
+ with gr.Column():
786
+ resume2_input = gr.Textbox(label="Resume 2", placeholder="Paste second resume here...", lines=6)
787
+
788
+ with gr.Row():
789
+ with gr.Column():
790
+ resume3_input = gr.Textbox(label="Resume 3 (Optional)", placeholder="Paste third resume here...", lines=6)
791
+ with gr.Column():
792
+ resume4_input = gr.Textbox(label="Resume 4 (Optional)", placeholder="Paste fourth resume here...", lines=6)
793
+
794
+ rank_btn = gr.Button("Rank Resumes")
795
+
796
+ ranking_output = gr.Textbox(label="Ranking Results", interactive=False, lines=15)
797
+
798
+ def rank_resumes(job_desc, resume1, resume2, resume3, resume4):
799
+ if not job_desc.strip() or not resume1.strip() or not resume2.strip():
800
+ return "Please provide at least a job description and two resumes."
801
+
802
+ # Collect all non-empty resumes
803
+ resumes = [r for r in [resume1, resume2, resume3, resume4] if r.strip()]
804
+
805
+ # Rank the resumes
806
+ result = agent.rank_resumes(job_desc, resumes)
807
+
808
+ # Format the results
809
+ output = "Resume Rankings (Best to Worst Match):\n\n"
810
+
811
+ for i, rank in enumerate(result["rankings"]):
812
+ resume_num = rank["resume_index"] + 1
813
+ score = rank["score"]
814
+ output += f"{i+1}. Resume {resume_num} - Score: {score}/100\n"
815
+
816
+ output += "\nDetailed Analysis:\n" + result["analysis"]
817
+
818
+ return output
819
+
820
+ # Set up event handlers
821
+ rank_btn.click(rank_resumes, [job_description_input, resume1_input, resume2_input, resume3_input, resume4_input], ranking_output)
822
+
823
+ return interface
824
+
825
+ # Launch the interface when run directly
826
+ if __name__ == "__main__":
827
+ import argparse
828
+
829
+ # Parse command line arguments
830
+ parser = argparse.ArgumentParser(description="ResuRank AI Agent")
831
+ parser.add_argument("--test", action="store_true", help="Run in test mode with minimal resources")
832
+ parser.add_argument("--share", action="store_true", help="Share the Gradio interface")
833
+ args = parser.parse_args()
834
+
835
+ # Create and launch the interface
836
+ interface = create_interface(test_mode=args.test)
837
+ interface.launch(share=args.share)
requirements.txt ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ transformers>=4.30.0
2
+ torch>=2.0.0
3
+ fastapi>=0.95.0
4
+ uvicorn>=0.22.0
5
+ python-dotenv>=1.0.0
6
+ pydantic>=2.0.0
7
+ gradio>=3.35.0
8
+ huggingface_hub>=0.16.0
9
+ requests>=2.31.0
10
+ pillow>=9.5.0
11
+ numpy>=1.24.0
12
+ pandas>=2.0.0
13
+ psutil>=5.9.0
14
+ accelerate>=0.26.0