UNPKG

naukri-automation-tool

Version:

Naukri automation tool to fetch, filter, and apply for jobs automatically using gen ai.

429 lines (397 loc) 14.6 kB
const { HarmBlockThreshold, HarmCategory, VertexAI, } = require("@google-cloud/vertexai"); const { GoogleAuth } = require("google-auth-library"); const prompts = require("@inquirer/prompts"); const fs = require("fs"); const path = require("path"); const { localStorage, openFolder, openUrl } = require("./helper"); const aiPrompts = require("./prompts"); let generativeModel = null; // Global variable to store the instance const { GoogleGenerativeAI } = require("@google/generative-ai"); const { documentQA, callFunc, searchSimilarChunks } = require("./vectorSearch"); const { processDocumentEmbeddings } = require("./embeddings"); const { getDataFromFile } = require("./ioUtils"); let questionEmbeddings = null; /** * Captures user configuration through CLI prompts. */ const getGeminiUserConfiguration = async (preferences) => { try { if (!preferences) preferences = {}; const genAiConfig = preferences.genAiConfig || {}; //Get the authenticaton type for gemini // const authType = await prompts.select({ // type: "select", // name: "value", // message: "Select your authentication type:", // choices: [ // { name: "API Key (Recommended)", value: "apiKey" }, // { name: "Service Account", value: "serviceAccount" }, // ], // default: genAiConfig.authType || "apiKey", // }); let authType = "apiKey"; if (authType === "serviceAccount") { // Ensure the apikeys folder exists const apikeysFolderPath = path.join(__dirname, "apikeys"); //check if the file exists and create the file if it does not exist if (!fs.existsSync(apikeysFolderPath)) { fs.mkdirSync(apikeysFolderPath); } // Get the list of files in the folder let files = fs .readdirSync(apikeysFolderPath) .filter((file) => file.endsWith(".json")); // If the folder is empty, prompt the user to add key files while (files.length === 0) { console.log("No key files found in the 'apikeys' folder."); console.log( "Please add your Google Cloud service account key files (.json) to the 'apikeys' folder." ); console.log("Let me guide you step by step."); console.log(`1. Log In: Go to Google Cloud Console and log in. 2. Navigate to IAM & Admin: From the left-hand menu, go to IAM & Admin > Service Accounts. 3. Select Service Account: Find and click on the service account for which you need the key. 4. Manage Keys: Under the "Keys" section, click Add Key > Create new key. 5. Choose Key Type: Select the JSON option and click Create. 6. Copy the Key: Copy and paste the JSON key file into the "apikeys" folder.`); openUrl( `https://console.cloud.google.com/iam-admin/serviceaccounts/create` ); let confirmPrompt = await prompts.confirm({ type: "confirm", name: "value", message: "Were you able to download the api key file?", default: true, }); console.log("7. Paste api key file in apikeys folder"); if (!fs.existsSync(apikeysFolderPath)) { console.log("The 'apiKeys' folder does not exist. Creating it..."); fs.mkdirSync(apikeysFolderPath); } await openFolder(apikeysFolderPath); confirmPrompt = await prompts.confirm({ type: "confirm", name: "value", message: "Have you added the key file(s) to the folder?", default: true, }); console.log(`8. Enable Vertex AI API from API's and Services Section.`); openUrl( `https://console.cloud.google.com/apis/library/aiplatform.googleapis.com` ); confirmPrompt = await prompts.confirm({ type: "confirm", name: "value", message: "Were you able to enable the Vertext AI API?", default: true, }); console.log(`9. Enable Gemini API from API's and Services Section.`); openUrl( `https://console.cloud.google.com/apis/library/generativelanguage.googleapis.com` ); confirmPrompt = await prompts.confirm({ type: "confirm", name: "value", message: "Were you able to enable the Gemini API?", default: true, }); // Refresh the list of files after confirmation files = fs .readdirSync(apikeysFolderPath) .filter((file) => file.endsWith(".json")); } const keyFilePrompt = await prompts.select({ type: "select", name: "value", message: "Select your Google Cloud service account key file:", choices: files.map((file) => ({ name: file, value: file })), }); const keyFile = path.join(apikeysFolderPath, keyFilePrompt); let result = keyFilePrompt.split("-"); result = result.slice(0, result.length - 1).join("-"); const project = await prompts.input({ type: "text", name: "value", message: "Enter your Google Cloud project ID:", default: genAiConfig.project || result, validate: (input) => (input ? true : "Project ID is required."), }); const location = await prompts.input({ type: "text", name: "value", message: "Enter your location (e.g., us-central1):", default: genAiConfig.location || "us-central1", }); preferences.genAiConfig = { authType, project, location, keyFile, }; } else { let apiKey = genAiConfig.apiKey; let choice = "yes"; if (genAiConfig.apiKey) { choice = await prompts.select({ message: `Do you want to use the existing api key? (Current api key: ${genAiConfig.apiKey})`, default: "yes", choices: [ { name: "Yes", value: "yes" }, { name: "No", value: "no" }, ], }); if (choice === "yes") { apiKey = genAiConfig.apiKey; } } if (!genAiConfig.apiKey || choice === "no") { console.log( "Please get the api key from https://aistudio.google.com/app/u/3/apikey and press enter to continue" ); console.log( "Click on create api key button and copy the api key and paste it below, You can paste using ctrl+v or right click and paste." ); await prompts.input({ message: "Press enter to continue, This will open the api key creation page.", }); openUrl(`https://aistudio.google.com/app/u/3/apikey`); apiKey = await prompts.input({ type: "text", name: "value", message: "Enter your Google API Key: ", default: genAiConfig.apiKey ?? "", validate: (input) => input && input.length > 0 ? true : "API Key is required.", }); } preferences.genAiConfig = { authType, apiKey, }; } const textModel = await prompts.select({ name: "value", message: "Enter the text model (e.g., gemini-2.0-flash-001):", default: genAiConfig.textModel || "gemini-2.0-flash-001", choices: [ { name: "gemini-2.0-flash-001 (Recommended)", value: "gemini-2.0-flash-001", }, { name: "gemini-1.5-flash-002", value: "gemini-1.5-flash-002" }, { name: "gemini-1.5-flash-001", value: "gemini-1.5-flash-001" }, { name: "gemini-1.5-pro-002", value: "gemini-1.5-pro-002" }, { name: "gemini-1.5-pro-001", value: "gemini-1.5-pro-001" }, { name: "gemini-1.0-pro-002", value: "gemini-1.0-pro-002" }, { name: "gemini-1.0-pro-001", value: "gemini-1.0-pro-001" }, ], }); preferences.genAiConfig.textModel = textModel; try { console.log("Pinging gemini model ..."); await initializeGeminiModel(preferences.genAiConfig); await pingModel(); preferences.enableGenAi = true; } catch (e) { let choice = await prompts.select({ message: "Would you like to do configuration again or disable Gen AI application?", choices: [ { name: "Do configuration", value: "configure" }, { name: "Disable Gen AI", value: "disable" }, ], }); if (choice === "configure") await getGeminiUserConfiguration(preferences); else preferences.enableGenAi = false; } return { config: preferences.genAiConfig, enableGenAi: preferences.enableGenAi, }; } catch (e) { console.log(e); throw e; } }; /** * Initializes the generative model instance. */ const initializeGeminiModel = async (config) => { if (config.authType == "serviceAccount") { const auth = new GoogleAuth({ scopes: ["https://www.googleapis.com/auth/cloud-platform"], keyFile: config.keyFile, }); const vertexAI = new VertexAI({ project: config.project, location: config.location, googleAuthOptions: auth, }); generativeModel = vertexAI.getGenerativeModel({ model: config.textModel, safetySettings: [ { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE, }, ], generationConfig: { maxOutputTokens: 2048 }, }); return generativeModel; } else { const genAI = new GoogleGenerativeAI(config.apiKey); generativeModel = genAI.getGenerativeModel({ model: config.textModel }); console.log("Generative model initialized successfully."); return generativeModel; } }; /** * Returns the generative model instance. */ const getGeminiModel = async () => { try { if (!generativeModel) { const preferences = localStorage.getItem("preferences"); if (preferences?.genAiConfig === undefined) { const { config, enableGenAi } = await getGeminiUserConfiguration( preferences ); preferences.genAiConfig = config; preferences.enableGenAi = enableGenAi; localStorage.setItem("preferences", preferences); writeToFile(preferences, "preferences"); } await initializeGeminiModel(preferences.genAiConfig); } return generativeModel; } catch (e) { console.log(e); return null; } }; const getModelResponse = async (prompt) => { const model = await getGeminiModel(); if (model == null) return null; const preferences = localStorage.getItem("preferences"); const genAiConfig = preferences.genAiConfig; if (!genAiConfig) { throw new Error("AI configuration not found in preferences."); } let answer; if (genAiConfig.authType === "serviceAccount") { const request = { contents: [{ role: "user", parts: [{ text: prompt }] }], }; const result = await model.generateContent(request); answer = result.response?.candidates?.[0]?.content?.parts?.[0]?.text; } else if (genAiConfig.authType === "apiKey") { const result = await model.generateContent(prompt); answer = result.response?.text(); } else { throw new Error(`Unsupported authType: ${genAiConfig.authType}`); } if (!answer) { throw new Error("No answer generated by the model."); } return answer; }; /** * Example function to check job suitability. */ const checkSuitability = async (job, profile) => { try { const prompt = aiPrompts.jobSuitabilityPrompt( profile.skills, job.description ); let answer = await getModelResponse(prompt); const jsonData = answer?.includes("```json") ? answer.split("```json")[1]?.split("```")[0]?.trim() : answer; if (!jsonData) { throw new Error( `Failed to extract JSON data from the response: ${answer}` ); } const data = JSON.parse(jsonData); if (data.isSuitable == "no" || data.isSuitable == "false") data.isSuitable = false; if (data.isSuitable == "yes" || data.isSuitable == "true") data.isSuitable = true; return data; } catch (e) { console.log( "Error while generating content in checking suitability : " + e ); console.log("Generated content is -> "); console.log(response.candidates[0].content.parts[0].text); throw e; } }; const answerQuestion = async (questions, profileDetails) => { try { if (questionEmbeddings == null) { const questionsData = await getDataFromFile("questions"); if (questionsData && questionsData.length !== 0) { const questionsDataChunks = Object.values(questionsData).map( (question) => { return `Question: ${question.questionName}\nAnswer: ${question.answer}`; } ); questionEmbeddings = await processDocumentEmbeddings( questionsDataChunks ); } } let chunks = []; if(questions && questions.length !== 0 && questionEmbeddings !== null){ chunks = await Promise.all( questions.map(async (question) => { return searchSimilarChunks(question.question, questionEmbeddings); }) ); } const prompt = aiPrompts.answerPrompt(questions, profileDetails, chunks); let answer = await getModelResponse(prompt); const jsonData = answer?.includes("```json") ? answer.split("```json")[1]?.split("```")[0]?.trim() : answer; if (!jsonData) { throw new Error( `Failed to extract JSON data from the response: ${answer}` ); } const data = JSON.parse(jsonData); return data; } catch (e) { console.error("Error while generating Assistant response:", e.message); return null; } }; const pingModel = async (prompt) => { try { let response; const model = await getGeminiModel(); if (model == null) return null; const result = await model.generateContent(prompt ?? "Hello How are you"); console.log(result.response.text()); } catch (e) { console.log("Error while generating Assistant response: "); console.log(e.message); throw e; } }; module.exports = { checkSuitability, answerQuestion, getGeminiUserConfiguration, initializeGeminiModel, getGeminiModel, pingModel, getModelResponse, };