ak0601 commited on
Commit
2ab7b79
·
verified ·
1 Parent(s): c98daca

Update app_hug.py

Browse files
Files changed (1) hide show
  1. app_hug.py +544 -0
app_hug.py CHANGED
@@ -1,3 +1,495 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from fastapi import FastAPI, HTTPException, BackgroundTasks
2
  from pydantic import BaseModel, EmailStr, Field
3
  from typing import Optional, Tuple
@@ -88,6 +580,13 @@ class MessageResponse(BaseModel):
88
  email_subject: Optional[str] = None
89
  error: Optional[str] = None
90
 
 
 
 
 
 
 
 
91
  # ------------------------------------------
92
  # Gmail Helper Functions
93
  # ------------------------------------------
@@ -339,10 +838,55 @@ async def root():
339
  "/generate-message",
340
  "/refine-message",
341
  "/authenticate",
 
342
  "/docs"
 
343
  ]
344
  }
345
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  @app.post("/generate-message", response_model=MessageResponse)
347
  async def generate_message(request: GenerateMessageRequest, background_tasks: BackgroundTasks):
348
  try:
 
1
+ # from fastapi import FastAPI, HTTPException, BackgroundTasks
2
+ # from pydantic import BaseModel, EmailStr, Field
3
+ # from typing import Optional, Tuple
4
+ # from enum import Enum
5
+ # import os
6
+ # import base64
7
+ # import pickle
8
+ # import pandas as pd
9
+ # from dotenv import load_dotenv
10
+ # from langchain_openai import ChatOpenAI
11
+ # from langchain.schema import HumanMessage, SystemMessage
12
+ # from email.mime.text import MIMEText
13
+ # from google.auth.transport.requests import Request
14
+ # from google.oauth2.credentials import Credentials
15
+ # from google_auth_oauthlib.flow import InstalledAppFlow
16
+ # from googleapiclient.discovery import build
17
+ # from googleapiclient.errors import HttpError
18
+ # from datetime import datetime
19
+ # import json
20
+
21
+ # # Load environment variables (not needed on Hugging Face, but harmless)
22
+ # load_dotenv()
23
+
24
+ # # ------------------------------------------
25
+ # # Helper: Write GOOGLE_CREDENTIALS_JSON to file if needed
26
+ # # ------------------------------------------
27
+ # def ensure_credentials_file():
28
+ # credentials_env = os.getenv("GOOGLE_CREDENTIALS_JSON")
29
+ # credentials_path = "credentials_SYNAPSE.json"
30
+ # if not os.path.exists(credentials_path):
31
+ # if not credentials_env:
32
+ # raise Exception("GOOGLE_CREDENTIALS_JSON not found in environment variables.")
33
+ # try:
34
+ # parsed_json = json.loads(credentials_env)
35
+ # except json.JSONDecodeError:
36
+ # raise Exception("Invalid JSON in GOOGLE_CREDENTIALS_JSON")
37
+ # with open(credentials_path, "w") as f:
38
+ # json.dump(parsed_json, f, indent=2)
39
+ # return credentials_path
40
+
41
+ # # ------------------------------------------
42
+ # # FastAPI app
43
+ # # ------------------------------------------
44
+ # app = FastAPI(title="Recruitment Message Generator API", version="1.0.0")
45
+
46
+ # SCOPES = ["https://www.googleapis.com/auth/gmail.send"]
47
+ # openai_api_key = os.getenv("OPENAI_API_KEY")
48
+
49
+ # # ------------------------------------------
50
+ # # Enums and Models
51
+ # # ------------------------------------------
52
+ # class MessageType(str, Enum):
53
+ # OUTREACH = "outreach"
54
+ # INTRODUCTORY = "introductory"
55
+ # FOLLOWUP = "followup"
56
+
57
+ # class GenerateMessageRequest(BaseModel):
58
+ # job_evaluation: str
59
+ # sender_email: EmailStr
60
+ # reply_to_email: Optional[EmailStr] = Field(None, description="Recruiter's email for reply-to header")
61
+ # recipient_email: EmailStr
62
+ # candidate_name: str
63
+ # current_role: str
64
+ # current_company: str
65
+ # company_name: str
66
+ # role: str
67
+ # recruiter_name: str
68
+ # organisation: str
69
+ # message_type: MessageType
70
+ # send_email: bool = False
71
+
72
+ # class FeedbackRequest(BaseModel):
73
+ # message: str
74
+ # feedback: str
75
+
76
+ # class AuthenticateRequest(BaseModel):
77
+ # email: EmailStr
78
+
79
+ # class AuthenticateResponse(BaseModel):
80
+ # success: bool
81
+ # message: str
82
+ # error: Optional[str] = None
83
+
84
+ # class MessageResponse(BaseModel):
85
+ # success: bool
86
+ # message: str
87
+ # email_sent: bool = False
88
+ # email_subject: Optional[str] = None
89
+ # error: Optional[str] = None
90
+
91
+ # # ------------------------------------------
92
+ # # Gmail Helper Functions
93
+ # # ------------------------------------------
94
+ # def get_token_file_path(email: str) -> str:
95
+ # tokens_dir = "gmail_tokens"
96
+ # if not os.path.exists(tokens_dir):
97
+ # os.makedirs(tokens_dir)
98
+ # safe_email = email.replace("@", "_at_").replace(".", "_dot_")
99
+ # return os.path.join(tokens_dir, f"token_{safe_email}.pickle")
100
+
101
+ # def check_user_token_exists(email: str) -> bool:
102
+ # token_file = get_token_file_path(email)
103
+ # return os.path.exists(token_file)
104
+
105
+ # def load_user_credentials(email: str):
106
+ # token_file = get_token_file_path(email)
107
+ # if os.path.exists(token_file):
108
+ # try:
109
+ # with open(token_file, 'rb') as token:
110
+ # creds = pickle.load(token)
111
+ # return creds
112
+ # except Exception:
113
+ # if os.path.exists(token_file):
114
+ # os.remove(token_file)
115
+ # return None
116
+
117
+ # def save_user_credentials(email: str, creds):
118
+ # token_file = get_token_file_path(email)
119
+ # with open(token_file, 'wb') as token:
120
+ # pickle.dump(creds, token)
121
+
122
+ # def create_new_credentials(email: str):
123
+ # credentials_path = ensure_credentials_file()
124
+ # flow = InstalledAppFlow.from_client_secrets_file(
125
+ # credentials_path, SCOPES
126
+ # )
127
+ # creds = flow.run_local_server(port=0)
128
+ # save_user_credentials(email, creds)
129
+ # return creds
130
+
131
+ # def authenticate_gmail(email: str, create_if_missing: bool = False):
132
+ # creds = load_user_credentials(email)
133
+ # if creds:
134
+ # if creds.expired and creds.refresh_token:
135
+ # try:
136
+ # creds.refresh(Request())
137
+ # save_user_credentials(email, creds)
138
+ # except Exception:
139
+ # if create_if_missing:
140
+ # try:
141
+ # creds = create_new_credentials(email)
142
+ # except:
143
+ # return None
144
+ # else:
145
+ # return None
146
+ # elif not creds.valid:
147
+ # creds = None
148
+ # if not creds:
149
+ # if create_if_missing:
150
+ # try:
151
+ # creds = create_new_credentials(email)
152
+ # except:
153
+ # return None
154
+ # else:
155
+ # return None
156
+ # try:
157
+ # service = build("gmail", "v1", credentials=creds)
158
+ # return service
159
+ # except Exception:
160
+ # return None
161
+
162
+ # def create_email_message(sender: str, to: str, subject: str, message_text: str, reply_to: Optional[str] = None):
163
+ # message = MIMEText(message_text)
164
+ # message["to"] = to
165
+ # message["from"] = sender
166
+ # message["subject"] = subject
167
+ # if reply_to:
168
+ # message["reply-to"] = reply_to
169
+ # message["Cc"] = reply_to
170
+ # raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
171
+ # return {"raw": raw_message}
172
+
173
+ # def send_gmail_message(service, user_id: str, message: dict):
174
+ # try:
175
+ # result = service.users().messages().send(userId=user_id, body=message).execute()
176
+ # return result is not None
177
+ # except HttpError:
178
+ # return False
179
+
180
+ # # ------------------------------------------
181
+ # # LLM (OpenAI) Message Generation Helpers
182
+ # # ------------------------------------------
183
+ # def refine_message_with_feedback(
184
+ # original_message: str,
185
+ # feedback: str,
186
+ # ) -> Tuple[str, str]:
187
+ # api_key = os.getenv("OPENAI_API_KEY")
188
+ # llm = ChatOpenAI(
189
+ # model="gpt-4o-mini",
190
+ # temperature=0.7,
191
+ # max_tokens=600,
192
+ # openai_api_key=api_key
193
+ # )
194
+ # prompt = f"""
195
+ # Please refine the following recruitment message based on the provided feedback:
196
+
197
+ # ORIGINAL MESSAGE:
198
+ # {original_message}
199
+
200
+ # FEEDBACK:
201
+ # {feedback}
202
+
203
+ # Please provide your response in the following format:
204
+ # SUBJECT: [Your subject line here]
205
+
206
+ # BODY:
207
+ # [Your refined email body content here]
208
+ # Keep the same tone and intent as the original message, but incorporate the feedback to improve it.
209
+ # """
210
+ # try:
211
+ # messages = [
212
+ # SystemMessage(content="You are a professional recruitment message writer. Refine the given message based on feedback while maintaining professionalism and the original intent."),
213
+ # HumanMessage(content=prompt)
214
+ # ]
215
+ # response = llm.invoke(messages)
216
+ # content = response.content.strip()
217
+ # subject_line = ""
218
+ # body_content = ""
219
+ # lines = content.split('\n')
220
+ # body_found = False
221
+ # body_lines = []
222
+ # for line in lines:
223
+ # if line.strip().startswith('SUBJECT:'):
224
+ # subject_line = line.replace('SUBJECT:', '').strip()
225
+ # elif line.strip().startswith('BODY:'):
226
+ # body_found = True
227
+ # elif body_found and line.strip():
228
+ # body_lines.append(line)
229
+ # body_content = '\n'.join(body_lines).strip()
230
+ # if not subject_line:
231
+ # subject_line = "Recruitment Opportunity - Updated"
232
+ # if not body_content:
233
+ # body_content = content
234
+ # return subject_line, body_content
235
+ # except Exception as e:
236
+ # raise HTTPException(status_code=500, detail=f"Error refining message: {str(e)}")
237
+
238
+ # def generate_recruitment_message_with_subject(
239
+ # msg_type: str,
240
+ # company: str,
241
+ # role_title: str,
242
+ # recruiter: str,
243
+ # org: str,
244
+ # candidate: str,
245
+ # current_pos: str,
246
+ # evaluation: str,
247
+ # feedback: Optional[str] = None
248
+ # ) -> Tuple[str, str]:
249
+ # api_key = os.getenv("OPENAI_API_KEY")
250
+ # llm = ChatOpenAI(
251
+ # model="gpt-4o-mini",
252
+ # temperature=0.7,
253
+ # max_tokens=600,
254
+ # openai_api_key=api_key
255
+ # )
256
+ # base_prompt = f"""
257
+ # Generate a professional recruitment {msg_type} with the following details:
258
+ # - Company hiring: {company}
259
+ # - Role: {role_title}
260
+ # - Recruiter: {recruiter} from {org}
261
+ # - Candidate: {candidate}
262
+ # - Candidate's current position: {current_pos}
263
+ # - Evaluation: {evaluation}
264
+ # """
265
+ # if msg_type == "outreach":
266
+ # prompt = base_prompt + """
267
+ # Create an initial outreach message that:
268
+ # - Introduces the recruiter and organization
269
+ # - Mentions the specific role and company
270
+ # - Expresses interest in discussing the opportunity
271
+ # - Keeps it short and to the point.
272
+ # - Do not include any placeholders like [Candidate Name] or [Role Title] in the email.
273
+ # """
274
+ # elif msg_type == "introductory":
275
+ # prompt = base_prompt + """
276
+ # Create an introductory message that:
277
+ # - Thanks the candidate for their initial response
278
+ # - Provides more details about the role and company
279
+ # - Explains why this opportunity aligns with their background
280
+ # - Suggests next steps (like a call or meeting)
281
+ # - Maintains a warm, professional tone
282
+ # """
283
+ # else: # followup
284
+ # prompt = base_prompt + """
285
+ # Create a follow-up message that:
286
+ # - References previous communication
287
+ # - Reiterates interest in the candidate
288
+ # - Addresses any potential concerns
289
+ # - Provides additional compelling reasons to consider the role
290
+ # - Includes a clear call to action
291
+ # """
292
+ # if feedback:
293
+ # prompt += f"\n\nPlease modify the message based on this feedback: {feedback}"
294
+ # prompt += """
295
+
296
+ # Please provide your response in the following format:
297
+ # SUBJECT: [Your subject line here]
298
+
299
+ # BODY:
300
+ # [Your email body content here]
301
+ # """
302
+ # try:
303
+ # messages = [
304
+ # SystemMessage(content="You are a professional recruitment message writer. Generate both an email subject line and body content. Follow the exact format requested."),
305
+ # HumanMessage(content=prompt)
306
+ # ]
307
+ # response = llm.invoke(messages)
308
+ # content = response.content.strip()
309
+ # subject_line = ""
310
+ # body_content = ""
311
+ # lines = content.split('\n')
312
+ # body_found = False
313
+ # body_lines = []
314
+ # for line in lines:
315
+ # if line.strip().startswith('SUBJECT:'):
316
+ # subject_line = line.replace('SUBJECT:', '').strip()
317
+ # elif line.strip().startswith('BODY:'):
318
+ # body_found = True
319
+ # elif body_found and line.strip():
320
+ # body_lines.append(line)
321
+ # body_content = '\n'.join(body_lines).strip()
322
+ # if not subject_line:
323
+ # subject_line = f"Opportunity at {company} - {role_title}"
324
+ # if not body_content:
325
+ # body_content = content
326
+ # return subject_line, body_content
327
+ # except Exception as e:
328
+ # raise HTTPException(status_code=500, detail=f"Error generating message: {str(e)}")
329
+
330
+ # # ------------------------------------------
331
+ # # FastAPI Endpoints
332
+ # # ------------------------------------------
333
+ # @app.get("/")
334
+ # async def root():
335
+ # return {
336
+ # "message": "Recruitment Message Generator API",
337
+ # "version": "1.0.0",
338
+ # "endpoints": [
339
+ # "/generate-message",
340
+ # "/refine-message",
341
+ # "/authenticate",
342
+ # "/docs"
343
+ # ]
344
+ # }
345
+
346
+ # @app.post("/generate-message", response_model=MessageResponse)
347
+ # async def generate_message(request: GenerateMessageRequest, background_tasks: BackgroundTasks):
348
+ # try:
349
+ # current_position = f"{request.current_role} at {request.current_company}"
350
+ # email_subject, generated_message = generate_recruitment_message_with_subject(
351
+ # msg_type=request.message_type.value.replace('followup', 'follow-up'),
352
+ # company=request.company_name,
353
+ # role_title=request.role,
354
+ # recruiter=request.recruiter_name,
355
+ # org=request.organisation,
356
+ # candidate=request.candidate_name,
357
+ # current_pos=current_position,
358
+ # evaluation=request.job_evaluation
359
+ # )
360
+ # email_sent = False
361
+ # if request.send_email:
362
+ # registered_users = []
363
+ # if os.path.exists("registered_users.csv"):
364
+ # df = pd.read_csv("registered_users.csv")
365
+ # registered_users = df['email'].tolist() if 'email' in df.columns else []
366
+ # if request.sender_email.lower() not in [user.lower() for user in registered_users]:
367
+ # return MessageResponse(
368
+ # success=True,
369
+ # message=generated_message,
370
+ # email_sent=False,
371
+ # email_subject=email_subject,
372
+ # error="Email not sent: Sender email is not registered"
373
+ # )
374
+ # service = authenticate_gmail(request.sender_email)
375
+ # if service:
376
+ # email_message = create_email_message(
377
+ # sender=request.sender_email,
378
+ # to=request.recipient_email,
379
+ # subject=email_subject,
380
+ # message_text=generated_message,
381
+ # reply_to=request.reply_to_email
382
+ # )
383
+ # email_sent = send_gmail_message(service, "me", email_message)
384
+ # if not email_sent:
385
+ # return MessageResponse(
386
+ # success=True,
387
+ # message=generated_message,
388
+ # email_sent=False,
389
+ # email_subject=email_subject,
390
+ # error="Email not sent: Failed to send via Gmail API"
391
+ # )
392
+ # else:
393
+ # return MessageResponse(
394
+ # success=True,
395
+ # message=generated_message,
396
+ # email_sent=False,
397
+ # email_subject=email_subject,
398
+ # error="Email not sent: Gmail authentication failed"
399
+ # )
400
+ # return MessageResponse(
401
+ # success=True,
402
+ # message=generated_message,
403
+ # email_sent=email_sent,
404
+ # email_subject=email_subject
405
+ # )
406
+ # except Exception as e:
407
+ # return MessageResponse(
408
+ # success=False,
409
+ # message="",
410
+ # error=str(e)
411
+ # )
412
+
413
+ # @app.post("/refine-message", response_model=MessageResponse)
414
+ # async def refine_message(request: FeedbackRequest):
415
+ # try:
416
+ # email_subject, refined_message = refine_message_with_feedback(
417
+ # original_message=request.message,
418
+ # feedback=request.feedback
419
+ # )
420
+ # return MessageResponse(
421
+ # success=True,
422
+ # message=refined_message,
423
+ # email_sent=False,
424
+ # email_subject=email_subject
425
+ # )
426
+ # except Exception as e:
427
+ # return MessageResponse(
428
+ # success=False,
429
+ # message="",
430
+ # error=str(e)
431
+ # )
432
+
433
+ # @app.post("/authenticate", response_model=AuthenticateResponse)
434
+ # async def authenticate_user(request: AuthenticateRequest):
435
+ # try:
436
+ # if check_user_token_exists(request.email):
437
+ # service = authenticate_gmail(request.email, create_if_missing=False)
438
+ # if service:
439
+ # return AuthenticateResponse(
440
+ # success=True,
441
+ # message="User already authenticated and token is valid"
442
+ # )
443
+ # else:
444
+ # service = authenticate_gmail(request.email, create_if_missing=True)
445
+ # if service:
446
+ # return AuthenticateResponse(
447
+ # success=True,
448
+ # message="Token refreshed successfully"
449
+ # )
450
+ # else:
451
+ # return AuthenticateResponse(
452
+ # success=False,
453
+ # message="Failed to refresh token",
454
+ # error="Could not refresh existing token. Please check credentials.json"
455
+ # )
456
+ # else:
457
+ # try:
458
+ # creds = create_new_credentials(request.email)
459
+ # if creds:
460
+ # return AuthenticateResponse(
461
+ # success=True,
462
+ # message="Authentication successful. Token created and saved."
463
+ # )
464
+ # else:
465
+ # return AuthenticateResponse(
466
+ # success=False,
467
+ # message="Authentication failed",
468
+ # error="Failed to create credentials"
469
+ # )
470
+ # except Exception as e:
471
+ # return AuthenticateResponse(
472
+ # success=False,
473
+ # message="Authentication failed",
474
+ # error=str(e)
475
+ # )
476
+ # except Exception as e:
477
+ # return AuthenticateResponse(
478
+ # success=False,
479
+ # message="Authentication error",
480
+ # error=str(e)
481
+ # )
482
+
483
+ # @app.get("/health")
484
+ # async def health_check():
485
+ # return {"status": "healthy", "timestamp": datetime.utcnow().isoformat()}
486
+
487
+ # if __name__ == "__main__":
488
+ # import uvicorn
489
+ # uvicorn.run(app, host="0.0.0.0", port=8000)
490
+
491
+
492
+
493
  from fastapi import FastAPI, HTTPException, BackgroundTasks
494
  from pydantic import BaseModel, EmailStr, Field
495
  from typing import Optional, Tuple
 
580
  email_subject: Optional[str] = None
581
  error: Optional[str] = None
582
 
583
+ class SendMessageRequest(BaseModel):
584
+ subject: str
585
+ email_body: str
586
+ sender_email: EmailStr
587
+ recipient_email: EmailStr
588
+ reply_to_email: Optional[EmailStr] = None
589
+
590
  # ------------------------------------------
591
  # Gmail Helper Functions
592
  # ------------------------------------------
 
838
  "/generate-message",
839
  "/refine-message",
840
  "/authenticate",
841
+ "/send-message",
842
  "/docs"
843
+
844
  ]
845
  }
846
 
847
+ @app.post("/send-message", response_model=MessageResponse)
848
+ async def send_message(request: SendMessageRequest):
849
+ try:
850
+ # Authenticate sender
851
+ service = authenticate_gmail(request.sender_email)
852
+ if not service:
853
+ return MessageResponse(
854
+ success=False,
855
+ message="",
856
+ error="Gmail authentication failed"
857
+ )
858
+ # Create the email
859
+ email_message = create_email_message(
860
+ sender=request.sender_email,
861
+ to=request.recipient_email,
862
+ subject=request.subject,
863
+ message_text=request.email_body,
864
+ reply_to=request.reply_to_email
865
+ )
866
+ # Send the email
867
+ email_sent = send_gmail_message(service, "me", email_message)
868
+ if email_sent:
869
+ return MessageResponse(
870
+ success=True,
871
+ message="Email sent successfully.",
872
+ email_sent=True,
873
+ email_subject=request.subject
874
+ )
875
+ else:
876
+ return MessageResponse(
877
+ success=False,
878
+ message="",
879
+ email_sent=False,
880
+ email_subject=request.subject,
881
+ error="Failed to send via Gmail API"
882
+ )
883
+ except Exception as e:
884
+ return MessageResponse(
885
+ success=False,
886
+ message="",
887
+ error=str(e)
888
+ )
889
+
890
  @app.post("/generate-message", response_model=MessageResponse)
891
  async def generate_message(request: GenerateMessageRequest, background_tasks: BackgroundTasks):
892
  try: