ReconAI / llm_explainer.py
ACA050's picture
Upload 14 files
64e5ee2 verified
import os
import requests
from dotenv import load_dotenv
import logging
load_dotenv()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class LLMExplainer:
def __init__(self):
self.api_key = os.environ.get("MISTRAL_API_KEY")
self.api_url = "https://api.mistral.ai/v1/chat/completions"
self.explanation_cache = {}
def explain_discrepancy(self, row, match_status, books_vendor, gst_vendor, books_amount, gst_amount):
"""
Generates a human-readable explanation for a discrepancy using Mistral AI.
"""
if not self.api_key:
return "API Key not configured. Discrepancy: " + match_status
is_anomaly = row.get('IsAnomaly', False)
if match_status == "Exact Match" and not is_anomaly:
return "Records match perfectly."
cache_key = (match_status, books_vendor, gst_vendor, books_amount, gst_amount, is_anomaly)
if cache_key in self.explanation_cache:
logger.info("Returning cached LLM explanation.")
return self.explanation_cache[cache_key]
prompt = f"""
You are an expert AI financial auditor. Explain the following transaction in 1-2 concise, professional sentences. Provide a recommended action.
Reconciliation Status: {match_status}
Is Flagged as Anomaly: {is_anomaly}
Anomaly Score: {row.get('AnomalyScore', 0.0)}
Invoice ID: {row.get('InvoiceID', 'Unknown')}
Books Vendor: {books_vendor}
GST Vendor: {gst_vendor}
Books Amount: {books_amount}
GST Amount: {gst_amount}
Explanation and Recommendation:
"""
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
data = {
"model": "mistral-tiny", # Using tiny for faster responses, can upgrade to small/medium
"messages": [
{"role": "user", "content": prompt}
],
"temperature": 0.3
}
try:
response = requests.post(self.api_url, headers=headers, json=data, timeout=10)
response.raise_for_status()
result = response.json()
explanation = result['choices'][0]['message']['content'].strip()
self.explanation_cache[cache_key] = explanation
return explanation
except requests.exceptions.RequestException as e:
logger.error(f"Error calling Mistral API: {e}")
return f"API Error ({match_status}). Please check amounts and vendor names manually."
except (KeyError, IndexError) as e:
logger.error(f"Unexpected response format from Mistral API: {e}")
return f"Error parsing AI response ({match_status})."
def generate_explanations_batch(self, discrepancies_df):
"""
Generates explanations for a dataframe of discrepancies.
"""
explanations = []
for _, row in discrepancies_df.iterrows():
status = row.get('MatchStatus', 'Unknown')
b_vendor = row.get('VendorName_books', 'N/A')
g_vendor = row.get('VendorName_gst', 'N/A')
b_amount = row.get('Amount_books', 0)
g_amount = row.get('Amount_gst', 0)
explanation = self.explain_discrepancy(row, status, b_vendor, g_vendor, b_amount, g_amount)
explanations.append(explanation)
return explanations