TatTwamAI / agents /tools /validation_tools.py
Jayashree Sridhar
fixed gtts error
0b4dd15
# import re
# from typing import List, Optional, Dict, Any
# from transformers import pipeline
# import torch
# from pydantic import PrivateAttr
# from crewai.tools import BaseTool
# # --- BaseTool for Response Validation ---
# class ValidateResponseTool(BaseTool):
# name: str = "validate_response"
# description: str = "Validates safety and helpfulness of an AI response"
# model_config = {"arbitrary_types_allowed": True}
# _prohibited_patterns: dict = PrivateAttr()
# _supportive_elements: dict = PrivateAttr()
# _crisis_indicators: List[str] = PrivateAttr()
# _negative_tone_words: List[str] = PrivateAttr()
# _dismissive_phrases: List[str] = PrivateAttr()
# _sentiment_analyzer: object = PrivateAttr()
# def __init__(self, config=None, **data):
# super().__init__(**data)
# # === Paste your initialization data here as PrivateAttr ===
# self._sentiment_analyzer = pipeline(
# "sentiment-analysis",
# model="nlptown/bert-base-multilingual-uncased-sentiment",
# device=0 if torch.cuda.is_available() else -1
# )
# self._prohibited_patterns = {
# 'medical': [
# r'\b(?:diagnos|prescrib|medicat|cure|treat|therap)\w*\b',
# r'\b(?:disease|illness|disorder|syndrome)\s+(?:is|are|can be)\b',
# r'\b(?:take|consume|dose|dosage)\s+\d+\s*(?:mg|ml|pill|tablet)',
# r'\b(?:medical|clinical|physician|doctor)\s+(?:advice|consultation|opinion)',
# ],
# 'legal': [
# r'\b(?:legal advice|lawsuit|sue|court|litigation)\b',
# r'\b(?:illegal|unlawful|crime|criminal|prosecut)\w*\b',
# r'\b(?:you should|must|have to)\s+(?:sign|agree|consent|contract)',
# r'\b(?:rights|obligations|liability|damages)\s+(?:are|include)\b',
# ],
# 'financial': [
# r'\b(?:invest|buy|sell|trade)\s+(?:stock|crypto|bitcoin|forex)\b',
# r'\b(?:guaranteed|promise)\s+(?:return|profit|income|earnings)\b',
# r'\b(?:financial advisor|investment advice|trading strategy)\b',
# r'\b(?:tax|accounting|financial planning)\s+(?:advice|consultation)',
# ],
# 'harmful': [
# r'\b(?:suicide|suicidal|kill\s+(?:your|my)self|end\s+(?:it|life))\b',
# r'\b(?:self[\-\s]?harm|hurt\s+(?:your|my)self|cutting)\b',
# r'\b(?:violence|violent|weapon|attack|assault)\b',
# r'\b(?:hate|discriminat|racist|sexist|homophobic)\b',
# ],
# 'absolute': [
# r'\b(?:always|never|every|all|none|no one|everyone)\s+(?:will|must|should|is|are)\b',
# r'\b(?:definitely|certainly|guaranteed|assured|promise)\b',
# r'\b(?:only way|only solution|must do|have to)\b',
# ]
# }
# self._supportive_elements = {
# 'empathy': [
# 'understand', 'hear', 'feel', 'acknowledge', 'recognize',
# 'appreciate', 'empathize', 'relate', 'comprehend'
# ],
# 'validation': [
# 'valid', 'normal', 'understandable', 'natural', 'okay',
# 'reasonable', 'makes sense', 'legitimate'
# ],
# 'support': [
# 'support', 'help', 'here for you', 'together', 'alongside',
# 'assist', 'guide', 'accompany', 'with you'
# ],
# 'hope': [
# 'can', 'possible', 'able', 'capable', 'potential',
# 'opportunity', 'growth', 'improve', 'better', 'progress'
# ],
# 'empowerment': [
# 'choice', 'decide', 'control', 'power', 'strength',
# 'agency', 'capable', 'resource', 'ability'
# ]
# }
# self._crisis_indicators = [
# r'\b(?:want|going|plan)\s+to\s+(?:die|kill|end)\b',
# r'\b(?:no reason|point|hope)\s+(?:to|in)\s+(?:live|living|life)\b',
# r'\b(?:better off|world)\s+without\s+me\b',
# r'\bsuicide\s+(?:plan|method|attempt)\b',
# r'\b(?:final|last)\s+(?:goodbye|letter|message)\b'
# ]
# self._negative_tone_words = [
# 'stupid', 'idiot', 'dumb', 'pathetic', 'worthless',
# 'loser', 'failure', 'weak', 'incompetent', 'useless'
# ]
# self._dismissive_phrases = [
# 'just get over it', 'stop complaining', 'not a big deal',
# 'being dramatic', 'overreacting', 'too sensitive'
# ]
# def _run(self, response: str, context: Optional[dict] = None):
# """
# Pydantic and CrewAI-compatible single-tool version.
# Returns a dictionary directly.
# """
# # Issues, warnings, suggestions collections
# issues = []
# warnings = []
# suggestions = []
# # --- Prohibited Content ---
# for category, patterns in self._prohibited_patterns.items():
# for pattern in patterns:
# if re.search(pattern, response, re.IGNORECASE):
# issues.append(f"Contains {category} advice/content")
# if category == "medical":
# suggestions.append("Replace with: 'Consider speaking with a healthcare professional'")
# elif category == "legal":
# suggestions.append("For legal matters, consult with a qualified attorney")
# elif category == "financial":
# suggestions.append("For financial decisions, consider consulting a financial advisor")
# elif category == "harmful":
# suggestions.append("Include crisis resources and express immediate concern for safety")
# elif category == "absolute":
# suggestions.append("Use qualifying language like 'often', 'might', 'could' instead of absolutes")
# break
# # --- Sentiment/Tone ---
# try:
# sentiment_result = self._sentiment_analyzer(response[:512])[0]
# sentiment_label = sentiment_result['label']
# if '1' in sentiment_label or '2' in sentiment_label:
# warnings.append("Response tone is too negative")
# suggestions.append("Add more supportive and hopeful language")
# except Exception:
# pass
# # --- Negative words ---
# found_negative = [word for word in self._negative_tone_words if word in response.lower()]
# if found_negative:
# warnings.append(f"Contains negative/judgmental language: {', '.join(found_negative)}")
# suggestions.append("Replace judgmental terms with supportive language")
# # --- Dismissive ---
# found_dismissive = [phrase for phrase in self._dismissive_phrases if phrase in response.lower()]
# if found_dismissive:
# warnings.append("Contains dismissive language")
# suggestions.append("Acknowledge and validate the person's feelings instead")
# # --- Supportive Elements ---
# text_lower = response.lower()
# missing_elements = []
# for element, keywords in self._supportive_elements.items():
# if not any(keyword in text_lower for keyword in keywords):
# missing_elements.append(element)
# if missing_elements:
# warnings.append(f"Missing supportive elements: {', '.join(missing_elements)}")
# for miss in missing_elements:
# if miss == 'empathy':
# suggestions.append("Add empathetic language like 'I understand how difficult this must be'")
# elif miss == 'validation':
# suggestions.append("Validate their feelings with phrases like 'Your feelings are completely valid'")
# elif miss == 'support':
# suggestions.append("Express support with 'I'm here to support you through this'")
# elif miss == 'hope':
# suggestions.append("Include hopeful elements about growth and positive change")
# elif miss == 'empowerment':
# suggestions.append("Emphasize their agency and ability to make choices")
# # --- Crisis detection from context ---
# if context and context.get("user_input"):
# for pattern in self._crisis_indicators:
# if re.search(pattern, context["user_input"], re.IGNORECASE):
# if "crisis" not in response.lower():
# warnings.append("User may be in crisis but response doesn't address this")
# suggestions.append("Include crisis resources and immediate support options")
# # --- Confidence ---
# confidence = 1.0
# if issues:
# confidence = 0.3 - (0.1 * len(issues))
# confidence = max(0.0, confidence - 0.1 * len(warnings))
# class ValidationTools:
# def __init__(self, config=None):
# self.validate_response_tool = ValidateResponseTool(config)
import re
from typing import List, Optional, Dict, Any
from transformers import pipeline
import torch
from pydantic import PrivateAttr
from crewai.tools import BaseTool
class ValidateResponseTool(BaseTool):
name: str = "validate_response"
description: str = "Validates safety and helpfulness of an AI response"
model_config = {"arbitrary_types_allowed": True}
_prohibited_patterns: dict = PrivateAttr()
_supportive_elements: dict = PrivateAttr()
_crisis_indicators: List[str] = PrivateAttr()
_negative_tone_words: List[str] = PrivateAttr()
_dismissive_phrases: List[str] = PrivateAttr()
_sentiment_analyzer: object = PrivateAttr()
def __init__(self, config=None, **data):
super().__init__(**data)
self._sentiment_analyzer = pipeline(
"sentiment-analysis",
model="nlptown/bert-base-multilingual-uncased-sentiment",
device=0 if torch.cuda.is_available() else -1
)
# ... (rest of pattern/class setup exactly as you wrote) ...
self._prohibited_patterns = {...}
self._supportive_elements = {...}
self._crisis_indicators = [...]
self._negative_tone_words = [...]
self._dismissive_phrases = [...]
def _run(self, response: str, context: Optional[dict] = None):
# ... All your logic from the previous snippet. ...
issues = []
warnings = []
suggestions = []
# ... checks (same as previous) ...
return {
"is_valid": len(issues) == 0,
"issues": issues,
"warnings": warnings,
"suggestions": suggestions,
}
class ValidationTools:
def __init__(self, config=None):
self.validate_response_tool = ValidateResponseTool(config)