LAST_DATE / claimbotics_engine.py
PranavRaut0902's picture
Upload 11 files
ed6cb1b verified
Raw
History Blame Contribute Delete
23.2 kB
import os
import re
import json
import torch
import pandas as pd
from sentence_transformers import SentenceTransformer, util
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
import os, json, datetime
def load_training_data(data_folder):
data = []
missing_code = 0
missing_query = 0
for file in os.listdir(data_folder):
if file.endswith(".json"):
path = os.path.join(data_folder, file)
with open(path, "r", encoding="utf-8") as f:
try:
entries = json.load(f)
for e in entries:
query_text = e.get("english") or e.get("English") or e.get("query") or e.get("Query")
if not query_text:
missing_query += 1
continue
code_text = e.get("pandas_code") or e.get("Pandas_Code") or e.get("code")
if not code_text:
missing_code += 1
continue
data.append({"english": query_text, "pandas_code": code_text})
except Exception as e:
print(f" Skipped {file}: {e}")
print(f" Loaded {len(data)} valid query–code pairs from {data_folder}")
print(f" Skipped {missing_code} missing-code and {missing_query} missing-query entries.")
return data
class EnhancedRetriever:
def __init__(self, data):
self.model = SentenceTransformer("all-MiniLM-L6-v2")
valid_data = [item for item in data if (item.get("pandas_code") and (item.get("english") or item.get("query")))]
if not valid_data:
raise ValueError("No valid query–code pairs found in dataset!")
self.queries = [item.get("english") or item.get("query") for item in valid_data]
self.codes = [item["pandas_code"] for item in valid_data]
print(f" Using {len(valid_data)} valid items for retrieval.")
print(" Encoding queries for retrieval...")
self.query_embeddings = self.model.encode(self.queries, convert_to_tensor=True)
def retrieve_best_match(self, user_query, top_k=3):
user_emb = self.model.encode(user_query, convert_to_tensor=True)
similarity = util.pytorch_cos_sim(user_emb, self.query_embeddings)[0]
top_results = torch.topk(similarity, k=top_k)
results = []
for i in range(top_k):
results.append({
"query": self.queries[top_results.indices[i]],
"pandas_code": self.codes[top_results.indices[i]],
"similarity": float(top_results.values[i])
})
return results
class Generator:
def __init__(self, model_dir="model"):
try:
self.tokenizer = AutoTokenizer.from_pretrained(model_dir)
self.model = AutoModelForSeq2SeqLM.from_pretrained(model_dir)
print(" Base model loaded successfully.")
except Exception as e:
print(f" Error loading base model: {e}")
self.tokenizer = None
self.model = None
def generate(self, query):
print("\n================= GENERATE DEBUG =================")
print("Incoming Query :", query)
try:
prompt = query
print("Prompt Created:", prompt)
print("Tokenizer Loaded:", self.tokenizer is not None)
try:
inputs = self.tokenizer(prompt, return_tensors="pt")
except Exception as e:
print(" TOKENIZER ERROR:", e)
return f"[Tokenizer Error] {str(e)}"
print("Model Loaded:", self.model is not None)
try:
outputs = self.model.generate(**inputs, max_length=128)
print("Model Generation Successful")
except Exception as e:
print(" MODEL ERROR:", e)
return f"[Model Error] {str(e)}"
try:
decoded = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
print("Decoding Successful →", decoded)
except Exception as e:
print(" DECODING ERROR:", e)
return f"[Decode Error] {str(e)}"
return decoded
except Exception as e:
print(" UNEXPECTED ERROR:", e)
return f"[Unexpected Error] {str(e)}"
def normalize_name(name):
if not isinstance(name, str):
return name
return re.sub(r'[^a-z0-9]', '', name.lower())
def extract_column_names(text):
words = re.findall(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b', text)
stopwords = {
'show','display','find','get','the','and','or','where','what','how','many','much','list','give','me','all','with','for','bottom','top','average','mean','sum','median','count','minimum','maximum','highest','lowest'
}
cols = [w for w in words if w.lower() not in stopwords and len(w) > 2]
return [normalize_name(c) for c in cols]
def extract_values(text):
quoted = re.findall(r"'([^']*)'", text)
numbers = re.findall(r'\b\d+\b', text)
return quoted + numbers
def enhanced_adaptation(user_query, code, original_retrieved_query):
query_columns = extract_column_names(user_query)
original_columns = extract_column_names(original_retrieved_query)
query_values = extract_values(user_query)
original_values = extract_values(original_retrieved_query)
new_code = code
for orig_col, new_col in zip(original_columns, query_columns):
if orig_col and new_col and normalize_name(orig_col) != normalize_name(new_col):
for pattern in [rf"'{orig_col}'", rf'\"{orig_col}\"', rf"\\b{orig_col}\\b"]:
new_code = re.sub(pattern, new_col, new_code, flags=re.IGNORECASE)
for orig_val, new_val in zip(original_values, query_values):
if orig_val and new_val and orig_val != new_val:
new_code = re.sub(rf"'{re.escape(orig_val)}'", f"'{new_val}'", new_code)
new_code = re.sub(rf'\"{re.escape(orig_val)}\"', f'\"{new_val}\"', new_code)
new_code = re.sub(rf"\\b{re.escape(orig_val)}\\b", new_val, new_code)
return new_code
def adapt_operations_based_on_query(query, code):
q = query.lower()
c = code
if any(word in q for word in ["average", "mean", "avg"]):
c = re.sub(r"\.(sum|min|max|count)\(\)", ".mean()", c)
elif any(word in q for word in ["total", "sum", "add", "together"]):
c = re.sub(r"\.(mean|min|max|count)\(\)", ".sum()", c)
return c
def select_best_template(retrieved_results, user_query):
best_score = -1
best_result = retrieved_results[0]
for result in retrieved_results:
score = result["similarity"]
if score > best_score:
best_score = score
best_result = result
return best_result
def post_process_code(code, user_query):
code = re.sub(r'\.\.', '.', code)
return code
def detect_vague_query(user_query):
q = user_query.lower()
intent_keywords = [
"amount", "sum", "total", "average", "mean", "minimum", "maximum",
"count", "number", "list", "show", "display", "details", "records","how many","what is"
]
data_keywords = [
"claim", "bill", "policy", "approved", "rejected", "tariff", "package", "department", "provider"
]
has_intent = any(word in q for word in intent_keywords)
has_data = any(word in q for word in data_keywords)
if has_data and not has_intent:
return True
return False
class RobustHybridText2Code:
def __init__(self, data_folder="data", model_dir="model/my_finetuned_model"):
self.data = load_training_data(data_folder)
self.retriever = EnhancedRetriever(self.data)
self.generator = MultiModelGenerator(
{
"modelA": "model/my_finetuned_model",
"modelB": "model/second_model_folder"
}
)
# self.generator = Generator(model_dir)
all_cols = set()
for item in self.data:
code = item.get("pandas_code", "")
cols = re.findall(r"df\[['\"]([^'\"]+)['\"]\]", code)
all_cols.update(cols)
self.col_map = {normalize_name(c): c for c in all_cols}
def get_code(self, user_query):
retrieved_results = self.retriever.retrieve_best_match(user_query, top_k=3)
best = select_best_template(retrieved_results, user_query)
print(f" [Best Match Similarity: {best['similarity']:.2f}]")
print(f" Original Query: {best['query']}")
if best["similarity"] > 0.95:
print(" High similarity — using code directly.")
code = best["pandas_code"]
elif best["similarity"] >= 0.90:
print(" Moderate similarity — adapting code.")
code = enhanced_adaptation(user_query, best["pandas_code"], best["query"])
else:
print(" Low similarity — generating new code.")
#code = self.generator.generate(user_query)
generated, used_model = self.generator.generate(user_query)
print(f"[INFO] Model used for this query → {used_model}")
code = generated
return post_process_code(code, user_query)
# class MultiDatasetHybridText2Code:
# def __init__(self):
# print(" Initializing Multi-Dataset Hybrid System with CSV Execution and Clarification Memory...\n")
# self.datasets = {
# "bill": {"folder": "data/bill_data", "csv_path": "data/bill_data/bill_dataset.csv"},
# "status": {"folder": "data/status_data", "csv_path": "data/status_data/dataset.csv"},
# "history": {"folder": "data/history_data", "csv_path": "data/history_data/status_history_dataset.csv"}
# }
# self.models = {}
# for name, meta in self.datasets.items():
# print(f" Loading dataset: {name}")
# self.models[name] = RobustHybridText2Code(
# data_folder=meta["folder"],
# model_dir="model/my_finetuned_model" )
# if os.path.exists(meta["csv_path"]):
# df = pd.read_csv(meta["csv_path"])
# self.datasets[name]["df"] = df
# print(f" Loaded {len(df)} records for {name} dataset.")
# else:
# print(f" CSV not found for {name}: {meta['csv_path']}")
# print("\n Models and CSVs loaded successfully!\n")
# self.routing_model = SentenceTransformer("all-MiniLM-L6-v2")
# self.dataset_embeddings = {}
# for name in self.models:
# all_queries = [e.get("english") or e.get("query") for e in self.models[name].data]
# mean_emb = self.routing_model.encode(all_queries, convert_to_tensor=True).mean(dim=0)
# self.dataset_embeddings[name] = mean_emb
# def detect_dataset(self, user_query):
# q = user_query.lower().strip()
# if any(word in q for word in ["bill", "billing", "bill details", "bill status","invoice"]):
# print(" Rule-based routing: 'bill' detected → Using BILL dataset.")
# return "bill"
# elif any(word in q for word in ["claim history", "history", "previous claims", "old claims"]):
# print(" Rule-based routing: 'claim history' detected → Using HISTORY dataset.")
# if "history" not in self.datasets:
# print(" History dataset not found — falling back to default routing.")
# else:
# return "history"
# else:
# return "status"
# user_emb = self.routing_model.encode(user_query, convert_to_tensor=True)
# sims = {name: float(util.pytorch_cos_sim(user_emb, emb)) for name, emb in self.dataset_embeddings.items()}
# best_match = max(sims, key=sims.get)
# print(f" Dataset routing via embeddings: {sims}")
# print(f"Fallback selected: {best_match}")
# return best_match
# def execute_code(self, code, df):
# try:
# local_env = {"df": df, "pd": pd}
# result = eval(code, {"__builtins__": {}}, local_env)
# return result
# except Exception as e:
# print(f" Error executing code: {e}")
# return None
# def get_code(self, user_query,api_mode=False):
# if detect_vague_query(user_query):
# print(" Please specify what you want — number of claims, total amount, or claim details?")
# return None
# dataset_name = self.detect_dataset(user_query)
# print(f" Using dataset: {dataset_name}")
# bot = self.models[dataset_name]
# code = bot.get_code(user_query)
# df = self.datasets[dataset_name].get("df")
# if df is not None and code:
# print(f"\n Suggested Pandas Code:\n{code}")
# result = self.execute_code(code, df)
# if result is not None:
# print(f"\n Result:\n{result}")
# log_interaction(user_query, code, result, dataset_name)
# # if not api_mode:
# # save_user_example(dataset_name, user_query, code,result)
# return code,result
class MultiModelGenerator:
def __init__(self, model_dirs):
self.model_dirs = {
"modelA": "model/modelA_dir",
"modelB": "model/modelB_dir"
}
self.models = {}
self.tokenizers = {}
for name, path in model_dirs.items():
try:
tok = AutoTokenizer.from_pretrained(path)
try:
model = AutoModelForSeq2SeqLM.from_pretrained(path)
except:
from transformers import T5ForConditionalGeneration
model = T5ForConditionalGeneration.from_pretrained(path)
self.models[name] = model
self.tokenizers[name] = tok
print(f"[MultiModel] Loaded model: {name} from {path}")
except Exception as e:
print(f"[MultiModel] Failed loading {name}: {e}")
self.router = SentenceTransformer("all-MiniLM-L6-v2")
def pick_best_model(self, query):
query_l = query.lower()
# KEYWORD-BASED ROUTING RULES
trigger_keywords = [
"settelement age",
"outstanding age",
"top",
"claim age",
"rank"
]
for kw in trigger_keywords:
if kw in query_l:
print(f"[MultiModel] Keyword '{kw}' detected → Using Model 2")
return "modelB" # your second model
# Otherwise → ALWAYS use model 1
print("[MultiModel] Default routing → Using Model 1")
return "modelA"
def generate(self, query):
print("\n============= MULTI-MODEL GENERATE =============")
chosen = self.pick_best_model(query)
model = self.models[chosen]
tokenizer = self.tokenizers[chosen]
inputs = tokenizer(query, return_tensors="pt")
out = model.generate(**inputs, max_length=128)
decoded = tokenizer.decode(out[0], skip_special_tokens=True)
print(f"[MultiModel] Final Answer from {chosen}{decoded}")
return decoded, chosen
class ClaimBoticsEngine:
def __init__(self):
print("\n=== Initializing Multi-Dataset + Multi-Model Hybrid Engine ===\n")
# 1️⃣ DATASET SETUP
self.datasets = {
"bill": {"folder": "data/bill_data", "csv_path": "data/bill_data/bill_dataset.csv"},
"status": {"folder": "data/status_data", "csv_path": "data/status_data/dataset.csv"},
"history": {"folder": "data/history_data", "csv_path": "data/history_data/history_dataset.csv"}
}
self.models_by_dataset = {}
for name, meta in self.datasets.items():
print(f"Loading dataset: {name}")
# Load JSON training data for retrieval
data_pairs = load_training_data(meta["folder"])
# Load CSV dataframe
if os.path.exists(meta["csv_path"]):
df = pd.read_csv(meta["csv_path"])
self.datasets[name]["df"] = df
print(f"Loaded {len(df)} rows for dataset: {name}")
else:
self.datasets[name]["df"] = None
print(f"CSV not found: {meta['csv_path']}")
# Load retriever = trained JSON Q&A
retriever = EnhancedRetriever(data_pairs)
# Load two generation LLM models
generator = MultiModelGenerator({
"modelA": "model/my_finetuned_model",
"modelB": "model/my_second_model"
})
# Store all dataset tools
self.models_by_dataset[name] = {
"retriever": retriever,
"generator": generator,
"training_data": data_pairs,
}
print("\nEngine fully loaded!\n")
# 2️⃣ DATASET ROUTING (Bill / Status / History)
def detect_dataset(self, user_query):
q = user_query.lower()
if any(x in q for x in ["bill", "billing", "invoice"]):
print("Routing → BILL dataset")
return "bill"
if any(x in q for x in ["history", "older claims", "previous"]):
print("Routing → HISTORY dataset")
return "history"
print("Routing → STATUS dataset")
return "status"
# 3️⃣ CODE EXECUTION
def execute_code(self, code, df):
try:
local_env = {"df": df, "pd": pd}
result = eval(code, {"__builtins__": {}}, local_env)
return result
except Exception as e:
print("Execution error:", e)
return None
# 4️⃣ MAIN PIPELINE
def process_query(self, user_query):
# --- dataset routing ---
dataset = self.detect_dataset(user_query)
df = self.datasets[dataset]["df"]
retriever = self.models_by_dataset[dataset]["retriever"]
generator = self.models_by_dataset[dataset]["generator"]
# --- retrieve top matches from JSON training files ---
retrieved = retriever.retrieve_best_match(user_query, top_k=3)
best = select_best_template(retrieved, user_query)
print(f"Best similarity = {best['similarity']:.4f}")
# --- use template if high similarity ---
if best["similarity"] > 0.95:
print("High similarity → using template")
code = best["pandas_code"]
# --- modify template if moderate similarity ---
elif best["similarity"] >= 0.90:
print("Medium similarity → adapting template")
code = enhanced_adaptation(user_query, best["pandas_code"], best["query"])
# --- low similarity → LLM calls & multi-model selection ---
else:
print("Low similarity → generating via LLM (ModelA / ModelB)")
code, used_model = generator.generate(user_query)
print(f"LLM Model Used: {used_model}")
# Final code cleanup
code = post_process_code(code, user_query)
# --- Execute Pandas Code ---
result = None
if df is not None:
result = self.execute_code(code, df)
# Log interaction
log_interaction(user_query, code, result, dataset)
return {
"dataset": dataset,
"code": code,
"result": result
}
def log_interaction(query, code, result, dataset):
os.makedirs("logs", exist_ok=True)
log_path = "logs/interaction_log.jsonl"
entry = {
"timestamp": datetime.datetime.now().isoformat(),
"query": query,
"dataset": dataset,
"code": code,
"result_preview": str(result)[:300]
}
with open(log_path, "a", encoding="utf-8") as f:
f.write(json.dumps(entry) + "\n")
# def save_user_example(dataset, query, code,result):
# os.makedirs(f"logs", exist_ok=True)
# path = f"logs/new_user_data.jsonl"
# new_entry = {
# "timestamp": datetime.datetime.now().isoformat(),
# "query": query,
# "dataset": dataset,
# "code": code,
# "result_preview": str(result)[:300]
# }
# with open(path, "a", encoding="utf-8") as f:
# f.write(json.dumps(new_entry) + "\n")
# print(f" Saved new user example to {path}")
if __name__ == "__main__":
print("\nInitializing Hybrid Text-to-Pandas Bot...\n")
bot = ClaimBoticsEngine()
print("Chatbot ready! Type your queries below. Type 'exit' to quit.\n")
while True:
try:
user_query = input("You: ").strip()
if user_query.lower() in ["exit", "quit"]:
print("Goodbye!")
break
if not user_query:
continue
code, result = bot.get_code(user_query)
print("\nBot:")
if code:
print(f" Suggested Pandas Code:\n {code}")
else:
print(" Could not generate code. Please clarify your query.")
if result is not None:
print(f"\n Execution Result:\n {result}")
print("\n" + "-"*50 + "\n")
except KeyboardInterrupt:
print("\nInterrupted. Exiting chatbot...")
break
except Exception as e:
print(f"Error: {e}")
continue