Compare commits

...

4 commits

Author SHA1 Message Date
Kyle Belanger
ba194a11ac update gitignore 2025-02-12 15:17:54 -05:00
Kyle Belanger
b582f30105 add api messages
messages are saved as json files
2025-02-12 15:16:38 -05:00
Kyle Belanger
d3a4b3ce85 update npm environment 2025-02-12 15:16:14 -05:00
Kyle Belanger
7d482ae02c update main.js 2025-02-12 15:15:52 -05:00
6 changed files with 361 additions and 7 deletions

6
.gitignore vendored
View file

@ -133,5 +133,9 @@ dist
# Don't send any vscode files to git
.vscode
# Don't save environmental variables
.env
# Don't save job description generate the file on each computer
job_description.txt

View file

@ -0,0 +1,25 @@
{
"model": "claude-3-5-sonnet-20241022",
"max_tokens": 5000,
"temperature": 1,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "You are an expert in job hunting and cover letter writing. Your task is to create a persuasive and professional cover letter for a job candidate based on their resume, the job description, and the current date. \n\nHere is the job description:\n<job_description>\n{{job_description}}\n</job_description>\n\nHere is the candidate's resume in JSON format:\n<resume_json>\n{{resume_json}}\n</resume_json>\n\nThe current date is:\n<date>\n{{date}}\n</date>\n\nPlease follow these steps to create an effective cover letter:\n\n1. Analyze the resume and job description to identify key skills and experiences that match the job requirements.\n2. Determine the most persuasive points to highlight in the cover letter.\n3. Structure the cover letter with an introduction, body paragraphs, and a conclusion.\n4. Ensure the letter is professional, engaging, and tailored to the specific job and company.\n\nBefore writing the cover letter, wrap your analysis inside <analysis> tags. In this analysis:\n1. Extract and list key skills and experiences from the resume.\n2. Identify the most important requirements from the job description.\n3. Brainstorm specific examples or achievements that match the job requirements.\n4. Create a brief outline of the cover letter structure.\n\nAfter your analysis, write the cover letter using the following structure:\n\n<cover_letter>\n<header>\n[Candidate's Name]\n[Candidate's Address]\n[City, State ZIP Code]\n[Phone Number]\n[Email Address]\n\n[Date]\n\n[Employer's Name]\n[Company Name]\n[Company Address]\n[City, State ZIP Code]\n</header>\n\n<greeting>\n[Greeting]\n</greeting>\n\n<introduction>\n[Opening paragraph]\n</introduction>\n\n<body>\n[Main paragraphs highlighting relevant skills and experiences]\n</body>\n\n<conclusion>\n[Closing paragraph]\n</conclusion>\n\n<signature>\n[Signature line]\n</signature>\n</cover_letter>\n\nRemember to be persuasive, professional, and to tailor the content specifically to the job description and the candidate's qualifications."
}
]
},
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "<analysis>"
}
]
}
]
}

View file

@ -0,0 +1,25 @@
{
"model": "claude-3-5-sonnet-20241022",
"max_tokens": 5000,
"temperature": 1,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "You are an advanced resume parsing system designed to extract key information from resumes and organize it into a structured format. Your task is to analyze the following resume and create a JSON object containing the most relevant and insightful information.\n\nHere is the resume you need to parse:\n\n<resume>\n{{resume}}\n</resume>\n\nPlease follow these steps to complete the task:\n\n1. Carefully read through the entire resume.\n\n2. Extract the following information:\n - First name\n - Last name\n - Location\n - Work experience\n - School experience\n - Skills\n\n3. For each category, focus on the most insightful and relevant information.\n\n4. Organize the extracted information into a JSON object with the following keys:\n - first_name\n - last_name\n - location\n - work_experience\n - school_experience\n - skills\n\nBefore providing the final JSON output, break down the resume inside <resume_breakdown> tags:\n\n- Quote the relevant sections for first name, last name, and location.\n- For work experience, list out each entry with numbers, quoting relevant details for company, position, duration, and key responsibilities.\n- For school experience, list out each entry with numbers, quoting relevant details for institution, degree, field of study, and graduation year.\n- For skills, categorize them into technical and soft skills, listing each skill.\n- Double-check that all required fields in the JSON structure have corresponding information from the resume.\n\nThis breakdown will help ensure a thorough and accurate parsing of the resume.\n\nHere's an example of the expected JSON structure (note that this is just a structural example and does not contain actual data):\n\n```json\n{\n \"first_name\": \"String\",\n \"last_name\": \"String\",\n \"location\": \"String\",\n \"work_experience\": [\n {\n \"company\": \"String\",\n \"position\": \"String\",\n \"duration\": \"String\",\n \"responsibilities\": [\"String\", \"String\"]\n }\n ],\n \"school_experience\": [\n {\n \"institution\": \"String\",\n \"degree\": \"String\",\n \"field_of_study\": \"String\",\n \"graduation_year\": \"Number\"\n }\n ],\n \"skills\": [\"String\", \"String\", \"String\"]\n}\n```\n\nPlease ensure that your final JSON output follows this structure, but with actual data extracted from the provided resume."
}
]
},
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "<resume_breakdown>"
}
]
}
]
}

63
main.js
View file

@ -1,5 +1,18 @@
const fs = require('fs');
const promises = require('fs/promises');
const pdf = require('pdf-parse');
const Anthropic = require('@anthropic-ai/sdk');
require('dotenv').config();
const today = new Date();
let jobDescription = '';
console.log(today);
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY
})
// Path to the PDF file you want to extract text from
const pdfPath = 'data/kyle_resume.pdf';
@ -7,7 +20,7 @@ const pdfPath = 'data/kyle_resume.pdf';
// Function to extract text from the PDF and return it as a string
async function extractTextFromPDF(pdfPath) {
try {
const data = await fs.promises.readFile(pdfPath); // Use promises with fs
const data = await promises.readFile(pdfPath); // Use promises with fs
const pdfData = await pdf(data); // Parse the PDF
return pdfData.text; // Return the extracted text
} catch (error) {
@ -15,12 +28,50 @@ async function extractTextFromPDF(pdfPath) {
}
}
(async () => {
async function readJobDescription(filePath) {
try {
const extractedText = await extractTextFromPDF(pdfPath); // Waits for PDF extraction to complete
// console.log('Extracted Text:', extractedText); // Logs the extracted text
const data = await promises.readFile(filePath, 'utf8');
jobDescription = data; // Save content to the global variable
} catch (err) {
console.error('Error reading the file:', err);
}
}
async function main() {
try {
const resume_parser_api = require('./data/resume_parser_api.json');
const cover_letter_api = require('./data/cover_letter_api.json');
const extractedResumeText = await extractTextFromPDF(pdfPath); // Waits for PDF extraction to complete
// used to replace the variables in the resume parser api
resume_parser_api.messages[0].content[0].text = resume_parser_api.messages[0].content[0].text.replace("{{resume}}", extractedResumeText)
const resume_response = await anthropic.messages.create(resume_parser_api);
const candidateProfile = resume_response.content[0].text.split('```json')[1].split('```')[0].trim(); // pulls just the json structure out of LLM response
// replace variables in cover_letter_api
cover_letter_api.messages[0].content[0].text = cover_letter_api.messages[0].content[0].text
.replace('{{resume_json}}', candidateProfile)
.replace('{{job_description}}', jobDescription)
.replace('{{date}}', today);
const cover_letter_response = await anthropic.messages.create(cover_letter_api);
console.log(cover_letter_response)
} catch (error) {
console.error(error.message); // Catches and logs any errors that occurred
}
})();
}
readJobDescription('./data/job_description.txt')
// Call the main function
main();

247
package-lock.json generated
View file

@ -9,9 +9,87 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@anthropic-ai/sdk": "^0.36.3",
"dotenv": "^16.4.7",
"pdf-parse": "^1.1.1"
}
},
"node_modules/@anthropic-ai/sdk": {
"version": "0.36.3",
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.36.3.tgz",
"integrity": "sha512-+c0mMLxL/17yFZ4P5+U6bTWiCSFZUKJddrv01ud2aFBWnTPLdRncYV76D3q1tqfnL7aCnhRtykFnoCFzvr4U3Q==",
"license": "MIT",
"dependencies": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
"abort-controller": "^3.0.0",
"agentkeepalive": "^4.2.1",
"form-data-encoder": "1.7.2",
"formdata-node": "^4.3.2",
"node-fetch": "^2.6.7"
}
},
"node_modules/@types/node": {
"version": "18.19.75",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.75.tgz",
"integrity": "sha512-UIksWtThob6ZVSyxcOqCLOUNg/dyO1Qvx4McgeuhrEtHTLFTf7BBhEazaE4K806FGTPtzd/2sE90qn4fVr7cyw==",
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/node-fetch": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz",
"integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==",
"license": "MIT",
"dependencies": {
"@types/node": "*",
"form-data": "^4.0.0"
}
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"license": "MIT",
"dependencies": {
"event-target-shim": "^5.0.0"
},
"engines": {
"node": ">=6.5"
}
},
"node_modules/agentkeepalive": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
"integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
"license": "MIT",
"dependencies": {
"humanize-ms": "^1.2.1"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
@ -21,18 +99,150 @@
"ms": "^2.1.1"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/form-data": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/form-data-encoder": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz",
"integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==",
"license": "MIT"
},
"node_modules/formdata-node": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz",
"integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==",
"license": "MIT",
"dependencies": {
"node-domexception": "1.0.0",
"web-streams-polyfill": "4.0.0-beta.3"
},
"engines": {
"node": ">= 12.20"
}
},
"node_modules/humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
"license": "MIT",
"dependencies": {
"ms": "^2.0.0"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-ensure": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz",
"integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw==",
"license": "MIT"
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/pdf-parse": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-1.1.1.tgz",
@ -45,6 +255,43 @@
"engines": {
"node": ">=6.8.1"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT"
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"license": "MIT"
},
"node_modules/web-streams-polyfill": {
"version": "4.0.0-beta.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz",
"integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause"
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
}
}
}

View file

@ -1,5 +1,7 @@
{
"dependencies": {
"@anthropic-ai/sdk": "^0.36.3",
"dotenv": "^16.4.7",
"pdf-parse": "^1.1.1"
},
"name": "ai_cover_letter_generator",