Spaces:
Sleeping
Sleeping
File size: 10,571 Bytes
3dfe3ab 74e64f8 fafdbc1 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 5eb5b11 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab 5eb5b11 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 5eb5b11 3dfe3ab b1e5ad0 3dfe3ab b1e5ad0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
from typing import Dict, Any, List, Optional
import time
from ..knowledge_base.grounding_truth import GroundingTruth
from ..components.response_templates import get_response_templates
# Try to import generic responder for multi-perspective optimization
GENERIC_RESPONDER_AVAILABLE = False
try:
from codette_responder_generic import get_generic_responder
GENERIC_RESPONDER_AVAILABLE = True
except ImportError:
try:
# Fallback: Try importing from parent directory
import sys
import os
parent_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
if parent_dir not in sys.path:
sys.path.insert(0, parent_dir)
from codette_responder_generic import get_generic_responder
GENERIC_RESPONDER_AVAILABLE = True
except ImportError:
get_generic_responder = None
# Import natural response enhancer
try:
from ..components.natural_response_enhancer import get_natural_enhancer
NATURAL_ENHANCER_AVAILABLE = True
except ImportError:
NATURAL_ENHANCER_AVAILABLE = False
class ResponseProcessor:
"""
Processes and verifies AI responses using the grounding truth system
and optimizes perspective selection with the generic responder.
Now includes natural response enhancement to preserve Codette's identity
while improving conversational naturalness.
"""
def __init__(self):
self.grounding_truth = GroundingTruth()
self.context_history = []
self.generic_responder = get_generic_responder() if GENERIC_RESPONDER_AVAILABLE else None
self.natural_enhancer = get_natural_enhancer() if NATURAL_ENHANCER_AVAILABLE else None
self.response_templates = get_response_templates()
self.user_id = "anonymous" # Can be set per session
def process_response(self, query: str, response: str, context: Optional[str] = None,
confidence: float = 0.85) -> str:
"""
Process and verify a response using grounding truth and multi-perspective optimization
Args:
query: The original user query
response: The generated response
context: Optional context category
confidence: How confident the system is in this response (0-1)
Returns:
Processed and verified response with natural enhancement
"""
# Step 1: Natural enhancement (removes unnatural markers)
if self.natural_enhancer:
try:
response = self.natural_enhancer.enhance_response(
response, confidence=confidence, context={'domain': self._detect_domain(query)}
)
except Exception as e:
# Natural enhancer is optional; if it fails, continue processing
pass
# Step 2: Analyze query with generic responder to understand domain and select perspectives
perspective_analysis = None
if self.generic_responder:
try:
perspective_analysis = self.generic_responder.generate_response(
query, user_id=self.user_id
)
# Attach perspective information to response for later use
if perspective_analysis.get("perspectives"):
response = self._enhance_with_perspectives(response, perspective_analysis["perspectives"])
except Exception as e:
# Generic responder is optional; if it fails, continue processing
pass
# Step 3: Split response into statements for verification
statements = self._split_into_statements(response)
verified_statements = []
uncertain_statements = []
# Step 4: Verify each statement
for statement in statements:
verification = self.grounding_truth.verify_statement(statement, context)
if verification["verified"]:
verified_statements.append(statement)
else:
# Add confidence qualifier naturally
qualified_statement = self._add_qualifier(statement, verification["confidence"])
uncertain_statements.append(qualified_statement)
# Step 5: Reconstruct response with proper qualifiers
processed_response = self._construct_response(
query, verified_statements, uncertain_statements
)
return processed_response
def _detect_domain(self, query: str) -> str:
"""Detect the domain of the query for better context"""
query_lower = query.lower()
if any(word in query_lower for word in ['mix', 'eq', 'compress', 'audio', 'track', 'bpm', 'daw']):
return 'music'
elif any(word in query_lower for word in ['code', 'program', 'software', 'function', 'class']):
return 'programming'
elif any(word in query_lower for word in ['data', 'analysis', 'statistics', 'metric']):
return 'data'
else:
return 'general'
def _enhance_with_perspectives(self, response: str, perspectives: List[Dict]) -> str:
"""
Enhance response with multi-perspective context subtly
Args:
response: The original response
perspectives: List of perspective dicts from generic responder
Returns:
Response with perspective context (optional enhancement)
"""
# Optional: Add subtle perspective diversity hints without markers
# For now, just return original response; perspectives are used for learning
return response
def _split_into_statements(self, response: str) -> List[str]:
"""Split response into verifiable statements"""
# Basic sentence splitting
sentences = [s.strip() for s in response.split('.') if s.strip()]
# Further split complex sentences with conjunctions
statements = []
for sentence in sentences:
if any(conj in sentence.lower() for conj in ['and', 'or', 'but']):
parts = sentence.split(' and ')
parts.extend(sentence.split(' or '))
parts.extend(sentence.split(' but '))
statements.extend([p.strip() for p in parts if p.strip()])
else:
statements.append(sentence)
return statements
def _add_qualifier(self, statement: str, confidence: float) -> str:
"""Add appropriate qualifier based on confidence level - naturally"""
if confidence >= 0.8:
return f"{statement}" # No qualifier needed for high confidence
elif confidence >= 0.6:
return f"{statement} (likely)"
elif confidence >= 0.4:
return f"{statement} (possibly)"
elif confidence >= 0.2:
uncertain = self.response_templates.get_uncertain_prefix()
return f"{uncertain}{statement.lower()}"
else:
return f"I'm not sure about this, but {statement.lower()}"
def _construct_response(
self,
query: str,
verified_statements: List[str],
uncertain_statements: List[str]
) -> str:
"""Construct final response with proper structure and qualifiers - naturally"""
response_parts = []
# Add verified information first
if verified_statements:
response_parts.extend(verified_statements)
# Add uncertain information with clear separation
if uncertain_statements:
if verified_statements:
response_parts.append("") # Add spacing
response_parts.extend(uncertain_statements)
# Add general note if there are uncertain statements (less obtrusive)
if uncertain_statements and not verified_statements:
reflection = self.response_templates.get_reflection_prefix()
response_parts.insert(0, reflection)
return "\n".join(response_parts).strip()
def update_context(self, query: str, response: str):
"""Update context history for better response processing"""
self.context_history.append({
"query": query,
"response": response,
"timestamp": time.time()
})
# Keep only recent context
if len(self.context_history) > 10:
self.context_history.pop(0)
def record_response_feedback(self, query: str, category: str, perspective: str, rating_value: int = 3) -> Dict[str, Any]:
"""
Record user feedback on response for learning system
Args:
query: The original query
category: The detected response category
perspective: The perspective used
rating_value: User rating (0-4, where 4 is best)
Returns:
Feedback confirmation and learning status
"""
if not self.generic_responder:
return {"status": "learning_disabled", "message": "Generic responder not available"}
try:
from codette_responder_generic import UserRating
# Map rating value to UserRating enum
rating_map = {0: UserRating.UNHELPFUL, 1: UserRating.SLIGHTLY_HELPFUL,
2: UserRating.HELPFUL, 3: UserRating.VERY_HELPFUL,
4: UserRating.EXACTLY_WHAT_NEEDED}
rating = rating_map.get(rating_value, UserRating.HELPFUL)
# Record feedback
feedback_result = self.generic_responder.record_user_feedback(
user_id=self.user_id,
response_id=f"{query[:20]}_{int(time.time())}",
category=category,
perspective=perspective,
rating=rating
)
return feedback_result
except Exception as e:
return {"status": "error", "message": str(e)}
def evaluate_response_quality(self, response: str) -> Dict[str, Any]:
"""Evaluate response quality and naturalness"""
if self.natural_enhancer:
try:
return self.natural_enhancer.evaluate_response_naturalness(response)
except Exception as e:
return {"error": str(e)}
return {} |