Mexar / backend /modules /explainability.py
Devrajsinh bharatsinh gohil
Initial commit of MEXAR Ultimate - Phase 2 cleanup complete
b0b150b
"""
MEXAR Core Engine - Explainability Generator Module
Packages reasoning traces for UI display.
"""
import logging
from typing import Dict, List, Any, Optional
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class ExplainabilityGenerator:
"""
Generates structured explainability data for the UI.
Prepares reasoning traces and source citations.
"""
def __init__(self):
"""Initialize the explainability generator."""
pass
def generate(
self,
reasoning_result: Dict[str, Any]
) -> Dict[str, Any]:
"""
Generate comprehensive explainability data.
Args:
reasoning_result: Output from ReasoningEngine.reason()
Returns:
Structured explainability data for UI
"""
explainability = reasoning_result.get("explainability", {})
# Enhance the existing explainability data
enhanced = {
"summary": self._generate_summary(reasoning_result),
"inputs": self._format_inputs(explainability.get("inputs", {})),
"retrieval": self._format_retrieval(explainability.get("retrieval", {})),
"reasoning_steps": self._format_reasoning_steps(
explainability.get("reasoning_trace", [])
),
"confidence": self._format_confidence(
explainability.get("confidence_breakdown", {})
),
"sources": self._format_sources(explainability.get("sources_cited", []))
}
return enhanced
def _generate_summary(self, reasoning_result: Dict[str, Any]) -> Dict[str, Any]:
"""Generate a human-readable summary."""
confidence = reasoning_result.get("confidence", 0)
in_domain = reasoning_result.get("in_domain", True)
sources = reasoning_result.get("sources", [])
if not in_domain:
status = "rejected"
message = "Query was outside the agent's domain expertise"
color = "red"
elif confidence >= 0.8:
status = "high_confidence"
message = "Answer is well-supported by the knowledge base"
color = "green"
elif confidence >= 0.5:
status = "moderate_confidence"
message = "Answer is partially supported, some uncertainty exists"
color = "yellow"
else:
status = "low_confidence"
message = "Limited support in knowledge base, treat with caution"
color = "orange"
return {
"status": status,
"message": message,
"color": color,
"quick_stats": {
"sources_found": len(sources),
"confidence_percent": f"{confidence * 100:.0f}%"
}
}
def _format_inputs(self, inputs: Dict) -> Dict[str, Any]:
"""Format input information."""
return {
"query": inputs.get("original_query", ""),
"has_multimodal": inputs.get("has_multimodal", False),
"multimodal_type": self._detect_multimodal_type(inputs),
"multimodal_preview": inputs.get("multimodal_preview", "")
}
def _detect_multimodal_type(self, inputs: Dict) -> Optional[str]:
"""Detect the type of multimodal input."""
preview = inputs.get("multimodal_preview", "")
if not preview:
return None
if "[AUDIO" in preview:
return "audio"
elif "[IMAGE" in preview:
return "image"
elif "[VIDEO" in preview:
return "video"
return "text"
def _format_retrieval(self, retrieval: Dict) -> Dict[str, Any]:
"""Format retrieval information."""
return {
"chunks_retrieved": retrieval.get("chunks_retrieved", 0),
"previews": retrieval.get("chunk_previews", [])
}
def _format_reasoning_steps(self, trace: List[Dict]) -> List[Dict[str, Any]]:
"""Format reasoning trace into displayable steps."""
steps = []
for item in trace:
step = {
"step_number": item.get("step", len(steps) + 1),
"action": item.get("action", "unknown"),
"action_display": self._get_action_display(item.get("action", "unknown")),
"explanation": item.get("explanation", ""),
"icon": self._get_action_icon(item.get("action", "unknown"))
}
steps.append(step)
return steps
def _get_action_display(self, action: str) -> str:
"""Get display-friendly action name."""
action_map = {
"domain_check": "Domain Relevance Check",
"vector_retrieval": "Semantic Search",
"llm_generation": "Answer Generation",
"guardrail_rejection": "Domain Guardrail"
}
return action_map.get(action, action.replace("_", " ").title())
def _get_action_icon(self, action: str) -> str:
"""Get icon for reasoning action."""
icon_map = {
"domain_check": "βœ…",
"vector_retrieval": "πŸ”",
"llm_generation": "πŸ’¬",
"guardrail_rejection": "🚫"
}
return icon_map.get(action, "▢️")
def _format_confidence(self, breakdown: Dict) -> Dict[str, Any]:
"""Format confidence breakdown for display."""
overall = breakdown.get("overall", 0)
# Determine confidence level
if overall >= 0.8:
level = "high"
color = "#22c55e" # Green
message = "High confidence answer"
elif overall >= 0.5:
level = "moderate"
color = "#eab308" # Yellow
message = "Moderate confidence"
else:
level = "low"
color = "#f97316" # Orange
message = "Low confidence - verify independently"
return {
"overall_score": overall,
"overall_percent": f"{overall * 100:.0f}%",
"level": level,
"color": color,
"message": message,
"factors": [
{
"name": "Domain Relevance",
"score": breakdown.get("domain_relevance", 0),
"percent": f"{breakdown.get('domain_relevance', 0) * 100:.0f}%",
"description": "How well the query matches the agent's domain"
},
{
"name": "Retrieval Quality",
"score": breakdown.get("retrieval_quality", 0),
"percent": f"{breakdown.get('retrieval_quality', 0) * 100:.0f}%",
"description": "Quality of retrieved context chunks"
}
]
}
def _format_sources(self, sources: List[str]) -> List[Dict[str, str]]:
"""Format source citations."""
formatted = []
for source in sources:
source_type = self._detect_source_type(source)
formatted.append({
"citation": source,
"type": source_type,
"icon": self._get_source_icon(source_type)
})
return formatted
def _detect_source_type(self, source: str) -> str:
"""Detect the type of source citation."""
source_lower = source.lower()
if ".csv" in source_lower:
return "csv"
elif ".pdf" in source_lower:
return "pdf"
elif ".json" in source_lower:
return "json"
elif ".docx" in source_lower or ".doc" in source_lower:
return "docx"
elif "entry" in source_lower or "row" in source_lower:
return "entry"
else:
return "text"
def _get_source_icon(self, source_type: str) -> str:
"""Get icon for source type."""
icon_map = {
"csv": "πŸ“Š",
"pdf": "πŸ“„",
"json": "πŸ“‹",
"docx": "πŸ“",
"txt": "πŸ“ƒ",
"entry": "πŸ“Œ"
}
return icon_map.get(source_type, "πŸ“Ž")
def format_for_display(
self,
explainability_data: Dict[str, Any],
format_type: str = "full"
) -> Dict[str, Any]:
"""
Format explainability data for specific display contexts.
Args:
explainability_data: Generated explainability data
format_type: 'full', 'compact', or 'minimal'
Returns:
Formatted data appropriate for the display context
"""
if format_type == "minimal":
return {
"summary": explainability_data.get("summary", {}),
"confidence": {
"score": explainability_data.get("confidence", {}).get("overall_percent", "0%"),
"level": explainability_data.get("confidence", {}).get("level", "unknown")
}
}
elif format_type == "compact":
return {
"summary": explainability_data.get("summary", {}),
"retrieval": explainability_data.get("retrieval", {}),
"confidence": explainability_data.get("confidence", {}),
"sources": explainability_data.get("sources", [])[:3]
}
# Full format
return explainability_data
# Factory function
def create_explainability_generator() -> ExplainabilityGenerator:
"""Create a new ExplainabilityGenerator instance."""
return ExplainabilityGenerator()