SyedAzlanzar commited on
Commit
91aab29
·
1 Parent(s): a704218

@feat : enhance cover letter generation with markdown formatting; update PDF saving logic

Browse files
app/api/routes.py CHANGED
@@ -6,11 +6,7 @@ from app.services.resume_parser import extract_resume_text
6
  from app.utils.file_utils import generate_unique_filename
7
  from fastapi import UploadFile, File
8
  from app.services.hf_storage_service import HuggingFaceStorageService
9
- import os
10
- from dotenv import load_dotenv
11
-
12
-
13
-
14
 
15
  storage_service = HuggingFaceStorageService()
16
 
@@ -20,13 +16,26 @@ router = APIRouter()
20
  async def generate_cover_letter_api(data: GenerateRequest):
21
  try:
22
  if len(data.job_details) > 2048:
23
- raise HTTPException(status_code=400, detail="Job details are too long")
24
 
25
  resume_text = extract_resume_text(data.resume_path)
26
  letter_text = await generate_cover_letter(data, resume_text)
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  filename = generate_unique_filename()
29
- pdf_path = save_pdf(letter_text, filename)
30
 
31
  return GenerateResponse(
32
  letter=letter_text,
 
6
  from app.utils.file_utils import generate_unique_filename
7
  from fastapi import UploadFile, File
8
  from app.services.hf_storage_service import HuggingFaceStorageService
9
+ from app.services.pdf_creator import build_cover_letter_md, convert_md_to_text
 
 
 
 
10
 
11
  storage_service = HuggingFaceStorageService()
12
 
 
16
  async def generate_cover_letter_api(data: GenerateRequest):
17
  try:
18
  if len(data.job_details) > 2048:
19
+ raise HTTPException(status_code=400, detail="Job detail is too long")
20
 
21
  resume_text = extract_resume_text(data.resume_path)
22
  letter_text = await generate_cover_letter(data, resume_text)
23
+
24
+ md_cover_letter = build_cover_letter_md(
25
+ your_name=data.full_name,
26
+ postal_code=data.postal_code,
27
+ city=data.city,
28
+ email=data.email,
29
+ phone=data.phone_number,
30
+ job_title=data.job_title,
31
+ company_name=data.company_name,
32
+ generated_paragraphs=letter_text
33
+ )
34
+
35
+ text_cover_letter = convert_md_to_text(md_cover_letter)
36
 
37
  filename = generate_unique_filename()
38
+ pdf_path = save_pdf(text_cover_letter, filename)
39
 
40
  return GenerateResponse(
41
  letter=letter_text,
app/models/schema.py CHANGED
@@ -3,6 +3,14 @@ from pydantic import BaseModel
3
  class GenerateRequest(BaseModel):
4
  job_details: str
5
  resume_path: str
 
 
 
 
 
 
 
 
6
 
7
  class GenerateResponse(BaseModel):
8
  letter: str
 
3
  class GenerateRequest(BaseModel):
4
  job_details: str
5
  resume_path: str
6
+ job_title: str
7
+ company_name: str
8
+ email: str
9
+ phone_number: str
10
+ postal_code: str
11
+ city: str
12
+ country: str
13
+ full_name: str
14
 
15
  class GenerateResponse(BaseModel):
16
  letter: str
app/services/generator.py CHANGED
@@ -29,35 +29,30 @@ async def generate_cover_letter(data: GenerateRequest, resume_text: str) -> str:
29
 
30
  altered_job_details = await job_details_alteration(data.job_details)
31
 
 
32
  prompt = f"""
33
- Please generate a professional cover letter in **markdown format** based on the following information:
34
-
35
- **Job Details:**
36
- {altered_job_details}
37
-
38
- **Resume Content:**
39
- {resume_text}
40
-
41
-
42
- **Important:**
43
- - Format the output as proper markdown
44
- - Use appropriate headers (# ## ###)
45
- - Use **bold** for emphasis where appropriate
46
- - Use bullet points or numbered lists where relevant
47
- - Include proper spacing and line breaks
48
- - Start with the applicant's contact information as a header
49
- - Include date and employer information
50
- - Structure it as a professional business letter in markdown format
51
-
52
- Please ensure the cover letter is:
53
- 1. Tailored specifically to the job requirements
54
- 2. Highlights relevant experience from the resume
55
- 3. Professional and engaging tone
56
- 4. Proper markdown formatting throughout
57
- """
58
 
59
  messages = [
60
- {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful AI cover letter bot that generates professional cover letters in markdown format. Always respond with properly formatted markdown."},
61
  {"role": "user", "content": prompt}
62
  ]
63
  # Apply chat template
@@ -123,7 +118,6 @@ async def job_details_alteration(job_details:str) -> str:
123
  - If a section is not found, skip it. """
124
 
125
  messages = [
126
- {"role":"system", "content": "You are a job description cleaner. I will give you a long job description that includes many sections like company intro, perks, and marketing fluff. "},
127
  {"role": "user", "content": prompt}
128
  ]
129
  text = tokenizer.apply_chat_template(
 
29
 
30
  altered_job_details = await job_details_alteration(data.job_details)
31
 
32
+
33
  prompt = f"""
34
+ You are a professional cover letter writer.
35
+
36
+ Generate exactly **3 paragraphs** of a cover letter in **markdown format** based on the following:
37
+
38
+ **Job Title:** {data.job_title}
39
+ **Company Name:** {data.company_name}
40
+ **Job Details:** {altered_job_details}
41
+ **Resume Content:** {resume_text}
42
+
43
+ ### Instructions:
44
+ - Write in a professional and engaging tone.
45
+ - Tailor the content specifically to the job requirements.
46
+ - Highlight relevant experience, skills, and achievements from the resume.
47
+ - Each paragraph should be distinct and flow logically:
48
+ 1. Introduction (state interest and motivation)
49
+ 2. Main body (highlight relevant skills and experience)
50
+ 3. Closing (express enthusiasm and call to action)
51
+ - Return only **3 paragraphs**, no extra text.
52
+ - Do not include contact details, date, or subject line (these will be injected into a template).
53
+ """
 
 
 
 
 
54
 
55
  messages = [
 
56
  {"role": "user", "content": prompt}
57
  ]
58
  # Apply chat template
 
118
  - If a section is not found, skip it. """
119
 
120
  messages = [
 
121
  {"role": "user", "content": prompt}
122
  ]
123
  text = tokenizer.apply_chat_template(
app/services/pdf_creator.py CHANGED
@@ -4,6 +4,7 @@ from datetime import datetime
4
  import io
5
  from app.services.hf_storage_service import HuggingFaceStorageService
6
  import unicodedata
 
7
 
8
  storage_service = HuggingFaceStorageService()
9
 
@@ -54,3 +55,34 @@ def save_pdf(text: str, filename: str) -> str:
54
  # Upload PDF bytes to Hugging Face using your class method
55
  url = storage_service.upload_file_to_hf(file_content=pdf_data, folder="cover-letters", filename=filename)
56
  return url
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import io
5
  from app.services.hf_storage_service import HuggingFaceStorageService
6
  import unicodedata
7
+ import markdown2
8
 
9
  storage_service = HuggingFaceStorageService()
10
 
 
55
  # Upload PDF bytes to Hugging Face using your class method
56
  url = storage_service.upload_file_to_hf(file_content=pdf_data, folder="cover-letters", filename=filename)
57
  return url
58
+
59
+ def build_cover_letter_md(
60
+ your_name, postal_code, city, email, phone,
61
+ job_title, company_name, generated_paragraphs
62
+ ):
63
+ template = f"""
64
+ # {your_name}
65
+
66
+ {postal_code} {city}
67
+ {email} | {phone}
68
+
69
+ ---
70
+
71
+ **Subject:** {job_title}
72
+
73
+ Dear {company_name} Team,
74
+
75
+ {generated_paragraphs}
76
+
77
+ Yours sincerely,
78
+ {your_name}
79
+ """
80
+ return template
81
+
82
+
83
+ def convert_md_to_text(md_text: str) -> str:
84
+ """Convert markdown to plain text for PDF saving."""
85
+ # markdown2 converts to HTML, then strip tags
86
+ html = markdown2.markdown(md_text)
87
+ plain_text = "".join(html.split("<")[0] if "<" in html else html for html in html.split(">"))
88
+ return plain_text