Spaces:
Sleeping
Sleeping
| 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 | |
| }; | |