feat: add new role 'bot'

This commit is contained in:
2025-09-09 19:26:52 +05:30
parent 3090b10192
commit 3e5169b454
5 changed files with 4 additions and 268 deletions

View File

@@ -1,142 +0,0 @@
import OpenAI from "openai";
import fsp from "fs/promises";
import { PdfReader } from "pdfreader";
const openaiClient = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
async function queryChatGPT(prompt: string) {
let res = await openaiClient.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content: prompt,
},
],
response_format: { type: "json_object" },
});
return JSON.parse(res.choices[0].message.content);
}
async function parseBufferAsync(buffer: Buffer): Promise<string> {
let parsedData = "";
return new Promise((resolve, reject) => {
new PdfReader().parseBuffer(buffer, (error, item) => {
if (error) return reject(error);
if (!item) return resolve(parsedData);
if (item.text) parsedData += item.text;
});
});
}
export default async function generateNote(
folderPath: string
): Promise<string> {
let energyData = {};
let checklistData = {};
let energyDataFlag = false;
let checklistDataFlag = false;
let energyFilePath = "";
let checklistFilePath = "";
const files = await fsp.readdir(folderPath);
for (const file of files) {
const input = await fsp.readFile(folderPath + `/${file}`);
const pdfData = await parseBufferAsync(input);
if (
Object.keys(checklistData).length == 0 &&
pdfData.toLowerCase().includes("general contractors")
) {
const prompt = `
Extract the following fields and contractor info from the given data and return the response in JSON.
Fields: ["Site Address", "Lot Number", "Estimated Construction Cost", "Total Square Footage", "A/C Square Footage", "Number of Bedrooms", "Number of Bathrooms", "Construction Type", "Number of Stories", "Height", "General Contractors License", "Mechanical Contractors License", "Electrical Contractors License", "Plumbing Contractors License", "Roofing Contractors License", "Concrete/Mason Contractors License", "Confirm the following"]
Data: ${pdfData}
`;
checklistDataFlag = true;
checklistFilePath = file;
checklistData = await queryChatGPT(prompt);
}
if (
Object.keys(energyData).length == 0 &&
pdfData.toLowerCase().includes("energy efficiency")
) {
const prompt = `
Extract the following fields form the given data and return the response in JSON.
Fields: [Project Name, address, county]
Data: ${pdfData}
`;
energyDataFlag = true;
energyFilePath = file;
energyData = await queryChatGPT(prompt);
}
}
const addressCheck = {};
if (checklistDataFlag && Object.keys(checklistData).length > 0) {
for (const file of files) {
if (file == checklistFilePath) continue;
const input = await fsp.readFile(folderPath + `${file}`);
const pdfData = await parseBufferAsync(input);
const prompt = `
Check if the address in the data given below matches this ${checklistData["Site Address"]}. Return the response in JSON with schema {match: Boolean}.
Data: ${pdfData}
`;
const { match } = await queryChatGPT(prompt);
addressCheck[file] = match;
}
}
let note = "";
if (!energyDataFlag) {
note += "Energy Efficiency document not found.\n";
}
if (!checklistDataFlag) {
note += `Checklist document not found.\n\n`;
}
let filesList = "";
for (const file in addressCheck) {
if (!addressCheck[file]) {
filesList += file.split("/").pop() + "\n";
}
}
if (filesList != "") {
note +=
"Below files don't have address or the address doesn't match with the address in checklist\n\n";
note += filesList;
note += "\n";
}
if (energyDataFlag || checklistDataFlag) {
note += "\nExtracted Data:\n";
}
if (Object.keys(energyData).length > 0) {
note += `\n${JSON.stringify(energyData, null, 2)}\n`;
}
if (Object.keys(checklistData).length > 0) {
note += `\n${JSON.stringify(checklistData, null, 2)}\n`;
}
return note;
}

View File

@@ -1,26 +0,0 @@
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { validate } from "./validate";
export async function validateRoutes(fastify: FastifyInstance) {
fastify.post(
"/:rtsId",
{
schema: {
params: {
type: "object",
properties: { rtsId: { type: "string" } },
},
},
},
async (req: FastifyRequest, res: FastifyReply) => {
const { rtsId } = req.params as { rtsId: string };
try {
validate(rtsId, req.user.tenantId);
return res.code(200).send();
} catch (err) {
return err;
}
}
);
}

View File

@@ -1,85 +0,0 @@
import fsp from "fs/promises";
import axios from "axios";
import { rtsModel } from "../rts/rts.schema";
import { getChildren } from "../file/file.service";
import { getFileUrlS3 } from "../utils/s3";
import generateNote from "./ai";
import { createNote, createNoteBot } from "../note/note.service";
async function downloadFile(url: string, downloadPath: string) {
try {
const res = await axios({
url: url,
method: "GET",
responseType: "stream",
});
await fsp.writeFile(downloadPath, res.data);
} catch (err) {
console.log(err);
}
}
async function downloadFileTree(
basePath: string,
recId: string,
tenantId: string
) {
const rts = await rtsModel.findOne({ pid: recId });
async function downloadFolder(folderId: string, path: string) {
const items = await getChildren(folderId, tenantId);
for (const item of items) {
if (item.mimeType == "folder") {
const newPath = path + `/${item.name}`;
await fsp.mkdir(newPath);
await downloadFolder(item.pid, newPath);
} else {
await downloadFile(
await getFileUrlS3(item.pid, null, false),
path + `/${item.name}`
);
}
}
}
await downloadFolder(recId, basePath);
}
async function deleteFolder(path: string) {
await fsp.rm(path, { recursive: true, force: true });
}
export async function validate(recId: string, tenantId: string) {
const basePath = process.env.BASE_PATH || "/root/tmp/quickerPermit";
const folderPath = basePath + `/${recId}`;
try {
await fsp.mkdir(folderPath);
await downloadFileTree(folderPath, recId, tenantId);
const files = await fsp.readdir(folderPath);
const notes: { folder: string; note: string }[] = [];
for (const file of files) {
const filePath = folderPath + `/${file}`;
const stats = await fsp.stat(filePath);
if (stats.isDirectory()) {
const note = await generateNote(filePath);
notes.push({ folder: file, note });
}
}
let finalNote = "";
for (const note of notes) {
finalNote += `${note.folder}\n`;
finalNote += `${note.note}\n\n`;
}
await createNoteBot(finalNote, recId, tenantId);
} catch (err) {
console.log(err);
} finally {
deleteFolder(folderPath);
}
}

View File

@@ -80,21 +80,6 @@ export async function createNote(
return newNote.populate({ path: "createdBy", select: "pid name avatar" });
}
export async function createNoteBot(
content: string,
resourceId: string,
tenantId: string
) {
const newNote = await noteModel.create({
tenantId: tenantId,
pid: generateId(),
resourceId: resourceId,
content: content,
createdAt: new Date(),
createdBy: "6762acd606db9d07307a302d",
});
}
export async function updateNote(
input: CreateNoteInput,
resourceId: string,

View File

@@ -163,6 +163,10 @@ export const rules: Record<
users: ["__v"],
},
},
bot: {
claims: ["file:download", "note:write"],
hiddenFields: {},
},
};
export const roles = Object.keys(rules) as [