UNPKG

naukri-ninja

Version:

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

311 lines (280 loc) 10.1 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 } = require("./utils/helper"); const { openFolder, openUrl } = require("./utils/cmdUtils"); const { GoogleGenerativeAI } = require("@google/generative-ai"); const { textModelMenu, keyFileMenu, getConfirmation } = require("./utils/prompts"); const spinner = require('./utils/spinniesUtils'); const { writeToFile } = require("./utils/ioUtils"); 2 let generativeModel = 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 selectAuthTypeMenu(); 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 getConfirmation( "Were you able to download the api key file?" ); 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 getConfirmation( "Have you added the key file(s) to the folder?" ); 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 getConfirmation( "Were you able to enable the Vertext AI API?" ); 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 getConfirmation( "Were you able to enable the Gemini API?" ); // Refresh the list of files after confirmation files = fs .readdirSync(apikeysFolderPath) .filter((file) => file.endsWith(".json")); } const keyFilePrompt = await keyFileMenu(files); 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 getConfirmation( `Do you want to use the existing api key? (Current api key: ${genAiConfig.apiKey})` ); if (choice) { apiKey = genAiConfig.apiKey; } } if (!genAiConfig.apiKey || choice === false) { 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 textModelMenu(genAiConfig.textModel); preferences.genAiConfig.textModel = textModel; writeToFile(preferences, "preferences"); localStorage.setItem("preferences", preferences); try { await initializeGeminiModel(preferences.genAiConfig); await pingModel(); preferences.enableGenAi = true; } catch (e) { console.log(e.message); console.debug(e); let choice = await getConfirmation( "Would you like to do configuration again? (Select no to disable Gen AI application)" ); if (choice) 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 }); spinner.update("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) => { try { spinner.start("Getting model response..."); 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."); } spinner.stop(); return answer; } catch (e) { debugger; spinner.fail(`this is the error ${e.message}\n\n\n`); throw e; } finally { spinner.stop(); } }; const pingModel = async (prompt) => { try { spinner.start("Pinging model..."); const result = await getModelResponse(prompt ?? "Hello How are you"); spinner.succeed("Model pinged successfully"); } catch (e) { spinner.fail(`Error while pinging model: ${e.message}`); throw e; } }; module.exports = { getGeminiUserConfiguration, initializeGeminiModel, getGeminiModel, getModelResponse, pingModel, };