import re from typing import Dict, List, Tuple, Any from dataclasses import dataclass import numpy as np @dataclass class MatchResult: rule_category: str rule_type: str confidence: float matched_phrases: List[str] reasoning: str class SimpleNLPMatcher: """Simplified NLP matcher that doesn't require heavy ML libraries""" def __init__(self): print("🔄 Initializing Simple NLP matcher...") # Key phrase patterns for matching print(" Loading pattern matching rules...") self.positive_patterns = { 'music_first': [ r'\bmusic[- ]first\b', r'\bmusic company\b', r'\bartist empowerment\b', r'\bcreativity\b', r'\bimagination\b', r'\bmusic[- ]making\b', r'\bmusic creation\b', r'\bmusical\b', r'\bcreative\b' ], 'inclusive_tone': [ r'\binclusive\b', r'\baccessible\b', r'\bcommunity\b', r'\bconnection\b', r'\bbelonging\b', r'\bwelcoming\b', r'\btogether\b', r'\bjoin\b' ], 'human_centered': [ r'\bhuman\b', r'\bself[- ]expression\b', r'\bmeaningful creativity\b', r'\bartist\b(?!\s+replacement)', r'\bcollaborat\w+\b', r'\bempower\w*\b', r'\bpeople\b', r'\bmusician\b', r'\bcreator\b' ], 'authentic_voice': [ r'\bauthentic\b', r'\bgenuine\b', r'\bpoetic\b', r'\blyrical\b', r'\bplayful\b', r'\bexperimental\b', r'\boriginal\b', r'\bunique\b' ], 'positive_messaging': [ r'\binspir\w+\b', r'\bmagic\b', r'\bjoy\b', r'\bfun\b', r'\bexplore\b', r'\bdiscover\b', r'\bpassion\b', r'\bcraft\b', r'\bartistry\b' ] } self.negative_patterns = { 'ai_first': [ r'\bAI[- ]first\b', r'\bAI music company\b', r'\btechnology over\b', r'\btech[- ]heavy\b', r'\bAI does everything\b', r'\bAI[- ]powered\b', r'\balgorithm\w*\b', r'\bmachine learning\b', r'\bartificial intelligence\b' ], 'replacement_language': [ r'\breplace\w* (?:human )?artist\w*\b', r'\bmusic is broken\b', r'\bmake music great again\b', r'\bdemocratize music\b', r'\btake over\b', r'\beliminate\b.*\bneed\b', r'\bno more\b.*\bmusician\b' ], 'tech_bro': [ r'\bdisrupt\w*\b', r'\bscale\b', r'\bleverage\b', r'\bsynerg\w+\b', r'\boptimize\b', r'\bmonetize\b', r'\bgrowth hack\b', r'\bunicorn\b', r'\bpivot\b', r'\biterations?\b' ], 'trivializing': [ r'\beasy\b.*\bmusic\b', r'\beffortless\b.*\bcreation\b', r'\bcheap\b.*\bslop\b', r'\bshortcut\b', r'\binstant\b.*\bhit\b', r'\bautomatic\b.*\bsuccess\b', r'\bno skill\b.*\brequired\b' ], 'salesy_tone': [ r'\bREVOLUTIONARY[!]+\b', r'\bGAME[- ]CHANGER[!]*\b', r'\bUNLIMITED[!]+\b', r'\bFREE FOREVER[!]+\b', r'\bBEST EVER[!]+\b', r'\bINCREDIBLE[!]+\b', r'\bMIND[- ]BLOWING[!]+\b', r'\bUNBELIEVABLE[!]+\b' ] } # Weighted keywords for semantic-like matching self.approval_keywords = { 'music': 3.0, 'artist': 2.5, 'creative': 2.0, 'community': 2.0, 'imagination': 2.0, 'authentic': 1.8, 'empowerment': 2.5, 'collaboration': 2.0, 'expression': 1.8, 'inspiration': 1.5, 'craft': 1.8, 'artistry': 2.0, 'human': 1.5, 'people': 1.2, 'musician': 2.0, 'creator': 1.8 } self.rejection_keywords = { 'ai': 2.0, 'algorithm': 2.5, 'automate': 2.0, 'replace': 3.0, 'disrupt': 2.5, 'leverage': 1.8, 'optimize': 1.8, 'scale': 1.5, 'democratize': 2.0, 'revolutionize': 2.0, 'effortless': 2.0, 'instant': 1.8, 'automatic': 1.8 } print(" ✅ Simple NLP matcher ready!") def analyze_content(self, content: str, approval_rules: List, rejection_rules: List) -> Dict[str, Any]: print(f"🔍 Analyzing content: '{content[:50]}{'...' if len(content) > 50 else ''}'") content_lower = content.lower() # Pattern matching print(" Checking pattern matches...") pattern_matches = self._match_patterns(content_lower) print(f" Found {len(pattern_matches)} pattern matches") # Keyword scoring print(" Performing keyword analysis...") keyword_matches = self._keyword_scoring(content_lower) print(f" Found {len(keyword_matches)} keyword matches") # Combine results all_matches = pattern_matches + keyword_matches print(f" Total matches: {len(all_matches)}") # Calculate overall score approval_score = sum(m.confidence for m in all_matches if m.rule_type == 'approve') rejection_score = sum(m.confidence for m in all_matches if m.rule_type == 'reject') net_score = approval_score - rejection_score approved = net_score > 0 and rejection_score < 0.7 print(f" 📊 Scores - Approval: {approval_score:.2f}, Rejection: {rejection_score:.2f}, Net: {net_score:.2f}") print(f" 🎯 Decision: {'APPROVED' if approved else 'REJECTED'}") # Generate reasoning reasoning = self._generate_reasoning(all_matches, approved, net_score) return { "approved": approved, "approval_score": approval_score, "rejection_score": rejection_score, "net_score": net_score, "matches": all_matches, "reasoning": reasoning } def _match_patterns(self, content: str) -> List[MatchResult]: matches = [] # Check positive patterns for category, patterns in self.positive_patterns.items(): for pattern in patterns: if re.search(pattern, content, re.IGNORECASE): matches.append(MatchResult( rule_category=category, rule_type='approve', confidence=0.8, matched_phrases=[pattern], reasoning=f"Contains positive pattern: {pattern}" )) # Check negative patterns for category, patterns in self.negative_patterns.items(): for pattern in patterns: if re.search(pattern, content, re.IGNORECASE): matches.append(MatchResult( rule_category=category, rule_type='reject', confidence=0.9, matched_phrases=[pattern], reasoning=f"Contains problematic pattern: {pattern}" )) return matches def _keyword_scoring(self, content: str) -> List[MatchResult]: matches = [] words = re.findall(r'\b\w+\b', content.lower()) # Check approval keywords approval_score = 0 found_approval_words = [] for word in words: if word in self.approval_keywords: weight = self.approval_keywords[word] approval_score += weight found_approval_words.append(f"{word}({weight})") if approval_score > 0: confidence = min(approval_score / 10.0, 0.9) # Normalize to 0-0.9 matches.append(MatchResult( rule_category="keyword_approval", rule_type='approve', confidence=confidence, matched_phrases=found_approval_words[:3], # Top 3 reasoning=f"Contains positive keywords (score: {approval_score:.1f})" )) # Check rejection keywords rejection_score = 0 found_rejection_words = [] for word in words: if word in self.rejection_keywords: weight = self.rejection_keywords[word] rejection_score += weight found_rejection_words.append(f"{word}({weight})") if rejection_score > 0: confidence = min(rejection_score / 8.0, 0.95) # Normalize to 0-0.95 matches.append(MatchResult( rule_category="keyword_rejection", rule_type='reject', confidence=confidence, matched_phrases=found_rejection_words[:3], # Top 3 reasoning=f"Contains problematic keywords (score: {rejection_score:.1f})" )) return matches def _generate_reasoning(self, matches: List[MatchResult], approved: bool, net_score: float) -> str: if not matches: return "No significant matches found against brand guidelines." approve_matches = [m for m in matches if m.rule_type == 'approve'] reject_matches = [m for m in matches if m.rule_type == 'reject'] reasoning_parts = [] if approved: reasoning_parts.append("✅ APPROVED:") if approve_matches: top_approve = max(approve_matches, key=lambda x: x.confidence) reasoning_parts.append(f"Strong alignment with {top_approve.rule_category}") if reject_matches: reasoning_parts.append(f"Minor concerns noted but overall positive (score: {net_score:.2f})") else: reasoning_parts.append("❌ REJECTED:") if reject_matches: top_reject = max(reject_matches, key=lambda x: x.confidence) reasoning_parts.append(f"Violates {top_reject.rule_category} guidelines") if approve_matches: reasoning_parts.append("Some positive elements found but insufficient to approve") # Add specific match details (limit to top 2) top_matches = sorted(matches, key=lambda x: x.confidence, reverse=True)[:2] for match in top_matches: reasoning_parts.append(f"• {match.reasoning}") return " ".join(reasoning_parts)