lexguard-backend / src /services /agent4FinancialAnalyst.js
github-actions[bot]
Deploy to Hugging Face
b921752
Raw
History Blame Contribute Delete
3.4 kB
const mongoose = require('mongoose');
const { callLLM } = require('./aiClient');
const Contract = require('../models/Contract');
const Clause = require('../models/Clause');
const SYSTEM_PROMPT = `You are a forensic financial auditor. Your only job is to extract explicit financial obligations, penalties, clawbacks, training bond fees, and hidden costs from the text.
Ignore standard salary or base compensation.
Output a strictly formatted JSON array of objects. Do not include any conversational text or markdown blocks outside the JSON array.
Format:
[
{
"amount": 5000,
"currency": "USD",
"description": "Penalty for early termination of the training bond.",
"clause_id": "optional clause object id if known"
}
]
If no financial obligations are found, return an empty array: []
`;
/**
* Extracts financial obligations from the contract using Agent 4.
* This runs as a sidecar process in the queue.
*/
async function runAgent4FinancialAnalyst(contractId) {
try {
const contract = await Contract.findById(contractId);
if (!contract) throw new Error('Contract not found');
const clauses = await Clause.find({ contractId }).lean();
if (clauses.length === 0) return;
// Send chunks to LLM or whole contract if small enough
const contractText = clauses.map(c => `[ID: ${c._id.toString()}] ${c.rawText}`).join('\n\n');
// Safety limit on text size (e.g., truncate if excessively large for a single pass, or batch it)
const prompt = `Extract financial obligations from the following contract:\n\n${contractText}`;
// Force JSON output
let extractedData = await callLLM({
systemPrompt: SYSTEM_PROMPT,
userContent: prompt,
jsonMode: true
});
if (!Array.isArray(extractedData)) {
// If it returned an object with a wrapper key like { "results": [...] } or similar
if (extractedData.results && Array.isArray(extractedData.results)) {
extractedData = extractedData.results;
} else if (extractedData.financial_obligations && Array.isArray(extractedData.financial_obligations)) {
extractedData = extractedData.financial_obligations;
} else if (extractedData.obligations && Array.isArray(extractedData.obligations)) {
extractedData = extractedData.obligations;
} else {
extractedData = [];
}
}
let totalExposure = 0;
const financial_obligations = extractedData.map(item => {
totalExposure += (Number(item.amount) || 0);
const ob = {
amount: Number(item.amount) || 0,
currency: item.currency || 'INR',
description: item.description || 'Financial Obligation'
};
if (item.clause_id && mongoose.Types.ObjectId.isValid(item.clause_id)) {
ob.clause_id = new mongoose.Types.ObjectId(item.clause_id);
}
return ob;
});
// Save to contract
await Contract.findByIdAndUpdate(contractId, {
financial_obligations,
total_financial_exposure: totalExposure
});
console.log(`[Agent 4] Financial Analyst found ${financial_obligations.length} obligations, total exposure: ${totalExposure}.`);
} catch (err) {
console.error(`[Agent 4] Financial Analyst failed: ${err.message}`);
// Do not throw, as this is a sidecar process and should not kill the job pipeline
}
}
module.exports = {
runAgent4FinancialAnalyst
};