Spaces:
No application file
No application file
import json | |
import os | |
import logging | |
import re | |
import subprocess | |
from functools import wraps | |
from tools.tools import verify_sql_query | |
from langchain_groq import ChatGroq | |
from langchain.prompts import ChatPromptTemplate | |
# Configure logging | |
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") | |
class ValidLM: | |
"""Validation & Logging System for LLM Applications""" | |
PROJECTS_DIR = "projects" # Define the directory for project files | |
def __init__(self, project_name="default_project"): | |
self.project_name = project_name | |
self.project_file = os.path.join(self.PROJECTS_DIR, f"{project_name}.json") | |
self.knowledge_base = None # Could be a link, PDF, or CSV | |
self._initialize_project() | |
# self._start_streamlit_ui | |
def _initialize_project(self): | |
"""Create an empty project file if it doesn't exist""" | |
if not os.path.exists(self.project_file): | |
initial_data = { | |
"project_name": self.project_name, | |
"assertions": { | |
"deterministic": [], | |
"misc": [], | |
"factual": False, | |
"sql-only": False, | |
"knowledgebase": None | |
}, | |
"log_history": [], | |
"accuracy_history": [] | |
} | |
with open(self.project_file, "w") as f: | |
json.dump(initial_data, f, indent=4) | |
def _load_project(self): | |
"""Load the project data from the JSON file""" | |
with open(self.project_file, "r") as f: | |
return json.load(f) | |
def _save_project(self, data): | |
"""Save the project data to the JSON file""" | |
with open(self.project_file, "w") as f: | |
json.dump(data, f, indent=4) | |
def _start_streamlit_ui(self): | |
"""Start Streamlit UI in the background""" | |
app_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "app.py")) | |
# Start Streamlit without blocking the main thread | |
subprocess.Popen( | |
["streamlit", "run", app_path], | |
stdout=subprocess.DEVNULL, | |
stderr=subprocess.DEVNULL, | |
) | |
print(f"β Streamlit UI started for project '{self.project_name}'") | |
def add_assertion(self, assertion_type, assertion): | |
"""Add an assertion to the project file""" | |
valid_types = {"deterministic", "factual", "misc", "sql-only", "knowledgebase"} | |
if assertion_type not in valid_types: | |
raise ValueError(f"Invalid assertion type. Choose from {valid_types}") | |
project_data = self._load_project() | |
if assertion_type in {"factual", "sql-only"}: | |
project_data["assertions"][assertion_type] = assertion | |
elif assertion_type == "knowledgebase": | |
project_data["assertions"]["knowledgebase"] = assertion | |
else: | |
project_data["assertions"][assertion_type].append(assertion) | |
self._save_project(project_data) | |
logging.info(f"Added {assertion_type} assertion: {assertion}") | |
def generate_clarifying_questions(self, user_input): | |
"""Generate clarifying questions using ChatGroq in JSON mode.""" | |
llm = ChatGroq(temperature=0, response_format="json") | |
prompt = ChatPromptTemplate.from_template(""" | |
Given the user prompt: "{user_input}", generate clarifying multiple-choice questions | |
to define constraints, preferences, and requirements. | |
Example Output: | |
[ | |
{ | |
"question": "What is the preferred programming language?", | |
"options": ["Python", "Java", "C++"] | |
}, | |
{ | |
"question": "Should the solution be optimized for speed?", | |
"options": ["Yes", "No"] | |
} | |
] | |
Return ONLY valid JSON as per the format above. | |
""") | |
response = llm.predict(prompt.format(user_input=user_input)) | |
try: | |
clarifying_questions = json.loads(response) | |
self.clarifying_questions = clarifying_questions | |
return clarifying_questions | |
except json.JSONDecodeError: | |
logging.error("Invalid JSON response from LLM.") | |
self.clarifying_questions = [] | |
return [] | |
def verify_assertions(self, user_input, llm_output): | |
"""Run checks against stored assertions""" | |
# 1. Deterministic | |
# 2. Fact correction | |
# 3. Misc check via llm | |
# 4. Behaviour check | |
project_data = self._load_project() | |
assertions = project_data["assertions"] | |
results = {"deterministic": [], "factual": [], "misc": []} | |
# π΅ Deterministic Assertions | |
for assertion in assertions["deterministic"]: | |
pattern = assertion.get("value") | |
check_type = assertion.get("check_type") | |
if check_type == "regex": | |
match = re.search(pattern, llm_output) is not None | |
elif check_type == "contains": | |
match = pattern in llm_output | |
elif check_type == "not-contains": | |
match = pattern not in llm_output | |
elif check_type == "json_format": | |
try: | |
json.loads(llm_output) | |
match = True | |
except json.JSONDecodeError: | |
match = False | |
elif check_type == "sql_format": | |
match = verify_sql_query(llm_output) | |
else: | |
match = False | |
results["deterministic"].append((assertion, match)) | |
# π‘ Factual Assertions ############################# use module 3 | |
if assertions["factual"] and assertions["knowledgebase"]: | |
# Load and parse the knowledge base (PDF, etc.) here for comparison | |
kb_path = assertions["knowledgebase"] | |
# Placeholder for actual factual verification | |
for fact in ["sample fact"]: | |
match = fact in llm_output | |
results["factual"].append((fact, match)) | |
else: | |
results["factual"].append(("Knowledge Base Missing or Disabled", False)) | |
# π’ Miscellaneous Assertions | |
for assertion in assertions["misc"]: ######################### | |
validation = "complex check passed" # Placeholder for complex checks | |
results["misc"].append((assertion, validation)) | |
return results | |
# def trace(self, func): | |
# """Decorator for tracing function calls and verifying LLM responses""" | |
# @wraps(func) | |
# def wrapper(*args, **kwargs): | |
# user_input = args[0] if args else None | |
# logging.info(f"Executing {func.__name__} with input: {user_input}") | |
# result = func(*args, **kwargs) | |
# logging.info(f"Received Output: {result}") | |
# verification_results = self.verify_assertions(user_input, result) | |
# logging.info(f"Verification Results: {verification_results}") | |
# # Update accuracy history | |
# project_data = self._load_project() | |
# project_data["accuracy_history"].append(verification_results) | |
# self._save_project(project_data) | |
# return result | |
# return wrapper | |