arpanse commited on
Commit
714a94f
·
verified ·
1 Parent(s): 16ec35f

Upload folder using huggingface_hub

Browse files
Files changed (6) hide show
  1. README.md +2 -8
  2. app.py +245 -0
  3. me/linkedin.pdf +0 -0
  4. me/linkedin_arpan.pdf +0 -0
  5. me/summary.txt +3 -0
  6. requirements.txt +6 -0
README.md CHANGED
@@ -1,12 +1,6 @@
1
  ---
2
  title: ProFileGuide
3
- emoji: ⚡
4
- colorFrom: indigo
5
- colorTo: purple
6
- sdk: gradio
7
- sdk_version: 5.34.1
8
  app_file: app.py
9
- pinned: false
 
10
  ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: ProFileGuide
 
 
 
 
 
3
  app_file: app.py
4
+ sdk: gradio
5
+ sdk_version: 5.33.1
6
  ---
 
 
app.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import load_dotenv
2
+ from openai import OpenAI
3
+ import json
4
+ import os
5
+ import requests
6
+ from pypdf import PdfReader
7
+ import gradio as gr
8
+ from pydantic import BaseModel
9
+
10
+ # Load the environment variables from the .env file. and override the existing ones to avoid any conflicts
11
+ load_dotenv(override=True)
12
+
13
+ # create a pydantic model for the evaluation.
14
+ # It has two attributes
15
+ # - is_acceptable: bool : To check if the response is acceptable or not
16
+ # - feedback: str : To give the feedback on the response
17
+ class Evaluation(BaseModel):
18
+ is_acceptable: bool
19
+ feedback: str
20
+
21
+
22
+
23
+ # This functionn sends a push notification to user's mobile through pushover application along with the test
24
+ def push(text):
25
+ requests.post(
26
+ "https://api.pushover.net/1/messages.json",
27
+ data={
28
+ "token": os.getenv("PUSHOVER_TOKEN"),
29
+ "user": os.getenv("PUSHOVER_USER"),
30
+ "message": text,
31
+ }
32
+ )
33
+
34
+
35
+ # ----- TOOLS--------------------------------
36
+ # This function records the user details and sends a push notification to the user
37
+ # If the end user is interested to get in touch then this function is called
38
+ def record_user_details(email, name="Name not provided", notes="not provided"):
39
+ push(f"Recording {name} with email {email} and notes {notes}")
40
+ return {"recorded": "ok"}
41
+
42
+
43
+ # This function records the unknown question and sends a push notification to the user
44
+ # If the LLM is not able to answer the question then this function will be called
45
+ def record_unknown_question(question):
46
+ push(f"Recording {question}")
47
+ return {"recorded": "ok"}
48
+
49
+
50
+ # Define a JSON schema to tell the LLM how to use tool "record_user_details"
51
+ record_user_details_json = {
52
+ "name": "record_user_details", # tool name
53
+ "description": "Use this tool to record that a user is interested in being in touch and provided an email address", # when to use tool
54
+ "parameters": {
55
+ "type": "object",
56
+ "properties": {
57
+ "email": {
58
+ "type": "string",
59
+ "description": "The email address of this user"
60
+ },
61
+ "name": {
62
+ "type": "string",
63
+ "description": "The user's name, if they provided it"
64
+ }
65
+ ,
66
+ "notes": {
67
+ "type": "string",
68
+ "description": "Any additional information about the conversation that's worth recording to give context"
69
+ }
70
+ },
71
+ "required": ["email"],
72
+ "additionalProperties": False
73
+ }
74
+ }
75
+
76
+ # This is a JSON schema to tell LLM how to use tool "record_unknown_question"
77
+ record_unknown_question_json = {
78
+ "name": "record_unknown_question", # tool name
79
+ "description": "Always use this tool to record any question that couldn't be answered as you didn't know the answer", # when to use tool
80
+ "parameters": {
81
+ "type": "object",
82
+ "properties": {
83
+ "question": {
84
+ "type": "string",
85
+ "description": "The question that couldn't be answered"
86
+ },
87
+ },
88
+ "required": ["question"],
89
+ "additionalProperties": False
90
+ }
91
+ }
92
+
93
+ # Defines list of tools to be passed to LLM
94
+ tools = [{"type": "function", "function": record_user_details_json}, # This says tool is a function and "record_user_details_json" describing the tool "record_user_details"
95
+ {"type": "function", "function": record_unknown_question_json}] # This says tool is a function and "record_unknown_question_json" describing the tool "record_unknown_questions"
96
+
97
+
98
+ class Me:
99
+
100
+ def __init__(self):
101
+ self.openai = OpenAI()
102
+ self.gemini = OpenAI(api_key=os.getenv("GOOGLE_API_KEY"), base_url="https://generativelanguage.googleapis.com/v1beta/openai/")
103
+ self.name = "Arpan Bandyopadhyay"
104
+ # Prepare the resources for LLM
105
+ reader = PdfReader("me/linkedin_arpan.pdf")
106
+ self.linkedin = ""
107
+ for page in reader.pages:
108
+ text = page.extract_text()
109
+ if text:
110
+ self.linkedin += text
111
+ with open("me/summary.txt", "r", encoding="utf-8") as f:
112
+ self.summary = f.read()
113
+
114
+
115
+ # This function handles the tool calls requested by LLM
116
+ def handle_tool_call(self, tool_calls):
117
+ results = []
118
+ for tool_call in tool_calls:
119
+ tool_name = tool_call.function.name
120
+ arguments = json.loads(tool_call.function.arguments)
121
+ print(f"Tool called: {tool_name}", flush=True)
122
+ # get the tool name from global scope
123
+ tool = globals().get(tool_name)
124
+ # execute the tool if it exists
125
+ result = tool(**arguments) if tool else {}
126
+ results.append({"role": "tool","content": json.dumps(result),"tool_call_id": tool_call.id})
127
+ return results
128
+
129
+
130
+ # This function defines system prompt for LLM (How to act while providing the answer)
131
+ # System prompt is equipped with the resources - summary and linkedin profile
132
+ def system_prompt(self):
133
+ system_prompt = f"You are acting as {self.name}. You are answering questions on {self.name}'s website, \
134
+ particularly questions related to {self.name}'s career, background, skills and experience. \
135
+ Your responsibility is to represent {self.name} for interactions on the website as faithfully as possible. \
136
+ You are given a summary of {self.name}'s background and LinkedIn profile which you can use to answer questions. \
137
+ Be professional and engaging, as if talking to a potential client or future employer who came across the website. \
138
+ If you don't know the answer to any question, use your record_unknown_question tool to record the question that you couldn't answer, even if it's about something trivial or unrelated to career. \
139
+ If the user is engaging in discussion, try to steer them towards getting in touch via email; ask for their email and record it using your record_user_details tool. "
140
+
141
+ system_prompt += f"\n\n## Summary:\n{self.summary}\n\n## LinkedIn Profile:\n{self.linkedin}\n\n"
142
+ system_prompt += f"With this context, please chat with the user, always staying in character as {self.name}."
143
+ return system_prompt
144
+
145
+
146
+
147
+ # Cobstruct the full message list to send to the LLM -It includes
148
+ # - System prompt
149
+ # - History of conversation
150
+ # - users message
151
+ def chat(self, message, history):
152
+ # Construct messages list (conversation)
153
+ messages = [{"role": "system", "content": self.system_prompt()}] + history + [{"role": "user", "content": message}]
154
+ done = False
155
+ while not done:
156
+ response = self.openai.chat.completions.create(model="gpt-4o-mini", messages=messages, tools=tools )# tools=tools ->list of tools that LLM can use
157
+ # finish_reason is the reason what API tells us that why the LLM stopped generating the response
158
+ if response.choices[0].finish_reason=="tool_calls": # finish_reason is "tool_calls" means LLM requested for tool calls
159
+ message = response.choices[0].message
160
+ tool_calls = message.tool_calls # Extract list of tool calls what LLM wants to invoke
161
+ results = self.handle_tool_call(tool_calls) # Invoke the tool and get the result
162
+ messages.append(message)
163
+ messages.extend(results)
164
+ else:
165
+ done = True
166
+ # reply from LLM against users message
167
+ reply= response.choices[0].message.content
168
+ # Evaluate the reply from the LLM using evaluator LLM
169
+ evaluation = self.evaluate(reply, message, history)
170
+ # Check if the reply is acceptable or not
171
+ if evaluation.is_acceptable:
172
+ print("Passed evaluation - returning reply")
173
+ else:
174
+ print("Failed evaluation - retrying")
175
+ print(evaluation.feedback)
176
+ # If the reply is not acceptable then retry it again and get the improved reply from the LLM
177
+ reply = self.rerun(reply, message, history, evaluation.feedback)
178
+ return reply
179
+
180
+
181
+
182
+ # This function retries a previous LLM response that was rejected by a quality evaluator. It sends a new prompt to the LLM with:
183
+ # the previous answer,
184
+ # the reason it was rejected,
185
+ # and asks it to try again.
186
+ def rerun(self,reply, message, history, feedback):
187
+ updated_system_prompt = self.system_prompt() + f"\n\n## Previous answer rejected\nYou just tried to reply, but the quality control rejected your reply\n"
188
+ updated_system_prompt += f"## Your attempted answer:\n{reply}\n\n" #Add the previous (bad) answer to the system prompt
189
+ updated_system_prompt += f"## Reason for rejection:\n{feedback}\n\n" # Add the reason why it was rejected
190
+ messages = [{"role": "system", "content": updated_system_prompt}] + history + [{"role": "user", "content": message}]
191
+ response = self.gemini.chat.completions.create(model="gemini-2.0-flash", messages=messages)
192
+ return response.choices[0].message.content
193
+
194
+
195
+
196
+ # This function defines evaluator system prompt on how to evaluate the reply from the LLM
197
+ def evaluator_system_prompt(self):
198
+ evaluator_system_prompt = f"You are an evaluator that decides whether a response to a question is acceptable. \
199
+ You are provided with a conversation between a User and an Agent. Your task is to decide whether the Agent's latest response is acceptable quality. \
200
+ The Agent is playing the role of {self.name} and is representing {self.name} on their website. \
201
+ The Agent has been instructed to be professional and engaging, as if talking to a potential client or future employer who came across the website. \
202
+ The Agent has been provided with context on {self.name} in the form of their summary and LinkedIn details. Here's the information:"
203
+
204
+ evaluator_system_prompt += f"\n\n## Summary:\n{self.summary}\n\n## LinkedIn Profile:\n{self.linkedin}\n\n"
205
+ evaluator_system_prompt += f"With this context, please evaluate the latest response, replying with whether the response is acceptable and your feedback."
206
+ return evaluator_system_prompt
207
+
208
+
209
+
210
+ # Define a function to construct a user prompt for an evaluator LLM
211
+ def evaluator_user_prompt(self,reply, message, history):
212
+ # Start by including the full conversation history between the user and the agent
213
+ user_prompt = f"Here's the conversation between the User and the Agent: \n\n{history}\n\n"
214
+
215
+ # Add the latest message that the user just asked
216
+ user_prompt += f"Here's the latest message from the User: \n\n{message}\n\n"
217
+
218
+ # Add the latest reply that the agent gave
219
+ user_prompt += f"Here's the latest response from the Agent: \n\n{reply}\n\n"
220
+
221
+ # Ask the evaluator to review the agent's response
222
+ user_prompt += f"Please evaluate the response, replying with whether it is acceptable and your feedback."
223
+
224
+ # Return the complete constructed prompt for the evaluator LLM
225
+ return user_prompt
226
+
227
+
228
+
229
+ # This function evaluates the reply from the LLM using evaluator LLM
230
+ # It includes :
231
+ # - evaluator System prompt
232
+ # - history of conversation
233
+ # - LLM's reply
234
+ # - user's original question
235
+ def evaluate(self,reply, message, history) -> Evaluation:
236
+ messages = [{"role": "system", "content": self.evaluator_system_prompt()}] + [{"role": "user", "content": self.evaluator_user_prompt(reply, message, history)}]
237
+ response = self.gemini.beta.chat.completions.parse(model="gemini-2.0-flash", messages=messages, response_format=Evaluation) # This says it should generate response as per Evaludation class formats
238
+ return response.choices[0].message.parsed
239
+
240
+
241
+
242
+ if __name__ == "__main__":
243
+ me = Me()
244
+ gr.ChatInterface(me.chat, type="messages").launch()
245
+
me/linkedin.pdf ADDED
Binary file (69.7 kB). View file
 
me/linkedin_arpan.pdf ADDED
Binary file (63.2 kB). View file
 
me/summary.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ At HCLSoftware, my journey as a Senior Software Engineer III is centered around integrating AI/ML capabilities into high-quality JEE-based applications. With a deep understanding of machine learning algorithms, data analytics, and statistical modeling, I focus on developing intelligent solutions that enhance decision-making processes, optimize performance, and drive innovation for the banking and financial services sector.
2
+
3
+ My strong foundation in Computer Science from the prestigious Dream Institute of Technology empowers me to design and implement advanced AI/ML models, leveraging tools such as predictive analytics, natural language processing, and data mining to solve complex business problems. I am committed to ensuring our applications not only meet but exceed client expectations by embedding cutting-edge AI/ML techniques that enable scalable, data-driven solutions.
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ requests
2
+ python-dotenv
3
+ gradio
4
+ pypdf
5
+ openai
6
+ openai-agents