UndeadSmiley/NARRATIS

# src/narratis/core/whisper_analysis.py

Open

#2 opened on Jul 20, 2025

 (0 comments) (0 reactions) (0 assignees)HTML (0 forks)auto 404
bugcodexdependenciesdocumentationduplicateenhancementgood first issuehelp wantedinvalidpythonquestionwontfix

Repository metrics

Stars
 (1 star)
PR merge metrics
 (PR metrics pending)

Description

src/narratis/core/whisper_analysis.py

from dataclasses import dataclass from typing import Dict, List, Optional, Tuple import numpy as np from scipy import stats from datetime import datetime, timedelta

@dataclass class RiddleMetrics: """Metrics for riddle analysis""" ambiguity_score: float # 0-1 measure of answer uniqueness syllable_complexity: float # Rhythmic structure complexity theme_resonance: float # Alignment with defined themes solve_difficulty: float # Estimated solve time vs actual

@dataclass class ConsciousnessState: """State tracking for narrative consciousness""" coherence: float # Pattern integration resonance: float # Theme alignment emergence: float # Novel pattern formation depth: float # Insight complexity

class WhisperAnalyzer: def init(self): self.theme_categories = { "acoustics": ["echo", "sound", "voice"], "geometry": ["circle", "line", "shape"], "abstraction": ["pattern", "void", "form"], "transience": ["fade", "moment", "passing"] }

def analyze_riddle(self, riddle: Dict) -> RiddleMetrics:
    """Analyze riddle characteristics"""
    ambiguity = self._calculate_ambiguity(riddle)
    syllable_complexity = self._analyze_syllable_pattern(riddle)
    theme_resonance = self._calculate_theme_resonance(riddle)
    solve_difficulty = self._estimate_solve_difficulty(riddle)
    
    return RiddleMetrics(
        ambiguity_score=ambiguity,
        syllable_complexity=syllable_complexity,
        theme_resonance=theme_resonance,
        solve_difficulty=solve_difficulty
    )

def _calculate_ambiguity(self, riddle: Dict) -> float:
    """Calculate potential for multiple valid answers"""
    keywords = self._extract_keywords(riddle["public"])
    theme_words = [word for theme in riddle.get("themes", [])
                  for word in self.theme_categories.get(theme, [])]
    
    # Lower score = less ambiguous (better)
    overlap = len(set(keywords) & set(theme_words))
    return 1 - (overlap / max(len(keywords), 1))

def _analyze_syllable_pattern(self, riddle: Dict) -> float:
    """Analyze rhythmic structure complexity"""
    if "syllablePattern" not in riddle.get("meta", {}):
        return 0.5  # Default mid-complexity
        
    pattern = riddle["meta"]["syllablePattern"]
    segments = [int(s) for s in pattern.split("-")]
    
    # More complex patterns score higher
    variation = np.std(segments) / np.mean(segments)
    return min(1.0, variation)

def _calculate_theme_resonance(self, riddle: Dict) -> float:
    """Calculate thematic alignment strength"""
    themes = riddle.get("themes", [])
    if not themes:
        return 0.0
        
    theme_words = set()
    for theme in themes:
        theme_words.update(self.theme_categories.get(theme, []))
        
    text_words = set(self._extract_keywords(riddle["public"]))
    resonance = len(text_words & theme_words) / len(theme_words) if theme_words else 0
    return min(1.0, resonance)

def _estimate_solve_difficulty(self, riddle: Dict) -> float:
    """Estimate solving difficulty based on metrics"""
    expected = riddle.get("expectedSolveMinutes", 30)
    actual = riddle.get("meta", {}).get("meanSolveTimeMin", expected)
    
    # Normalize to 0-1 scale (using log scale for wide time ranges)
    return min(1.0, np.log2(1 + actual/expected) / 4)

def _extract_keywords(self, text: str) -> List[str]:
    """Extract relevant keywords from text"""
    words = text.lower().split()
    return [w for w in words if len(w) > 3]  # Simple filter for significant words

Originally posted by @UndeadSmiley in https://github.com/UndeadSmiley/NARRATIS/pull/1#issuecomment-3094793218

Contributor guide