Introduction: Why NPS Became Our Most Critical AI API Metric

I manage the AI infrastructure for a mid-sized e-commerce platform handling 50,000+ daily customer inquiries. When we launched our AI customer service system last year, we tracked response times, token costs, and accuracy—but completely ignored the human satisfaction dimension. Six months in, our system was technically "working," yet our support tickets increased by 23%. Users weren't complaining about AI errors—they were frustrated by inconsistent experiences that made them lose trust in our brand. That's when I discovered AI API NPS tracking, and it transformed how we evaluate and optimize our LLM integrations.

Net Promoter Score measures user loyalty on a simple 0-10 scale: Promoters (9-10) are loyal enthusiasts, Passives (7-8) are satisfied but vulnerable to competitors, and Detractors (0-6) are unhappy customers who may damage your reputation. For AI APIs specifically, NPS captures whether users trust the AI's responses, find interactions helpful, and would recommend your service to colleagues. HolySheep AI has emerged as a cost-effective alternative with sub-50ms latency and support for WeChat and Alipay payments, making it an attractive option for teams prioritizing both performance and user satisfaction metrics.

Understanding AI API NPS vs Traditional SaaS NPS

Traditional SaaS NPS measures long-term account health, but AI API NPS operates in real-time at the conversation level. When a user asks your RAG system a question and receives a response, they form an immediate opinion about quality, speed, and relevance. Capturing this feedback immediately—while the interaction is fresh—provides actionable data that monthly surveys simply cannot.

Key differences in AI API contexts:

Implementation Architecture

System Design Overview

Our implementation uses a lightweight feedback collection layer between the user interface and the HolySheep AI API. This middleware captures request metadata, response quality signals, and explicit NPS ratings without introducing measurable latency overhead. The architecture supports both synchronous feedback (thumbs up/down after each response) and delayed surveys (email follow-up for enterprise accounts).

#!/usr/bin/env python3
"""
AI API NPS Collection System
Integrates with HolySheep AI for cost-effective LLM inference
Rate: ¥1=$1 (saves 85%+ vs ¥7.3 competitors)
Latency: <50ms p99
"""

import json
import time
import sqlite3
from datetime import datetime, timedelta
from typing import Optional, Dict, List
from dataclasses import dataclass, asdict
from flask import Flask, request, jsonify
import requests

@dataclass
class NPSRecord:
    """Structured NPS feedback record"""
    feedback_id: str
    timestamp: str
    user_id: str
    session_id: str
    model: str
    nps_score: int  # 0-10
    feedback_type: str  # 'explicit' | 'implicit' | 'delayed_survey'
    response_latency_ms: float
    tokens_used: int
    cost_usd: float
    query_preview: str  # First 100 chars
    response_preview: str  # First 200 chars
    was_helpful: bool
    error_occurred: bool
    error_type: Optional[str] = None

class HolySheepNPSCollector:
    """HolySheep AI API integration with built-in NPS tracking"""
    
    def __init__(self, api_key: str, db_path: str = "nps_data.db"):
        self.api_key = api_key
        self.base_url = "https://api.holysheep.ai/v1"
        self.db_path = db_path
        self._init_database()
    
    def _init_database(self):
        """Initialize SQLite database for NPS storage"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS nps_feedback (
                feedback_id TEXT PRIMARY KEY,
                timestamp TEXT NOT NULL,
                user_id TEXT NOT NULL,
                session_id TEXT NOT NULL,
                model TEXT NOT NULL,
                nps_score INTEGER NOT NULL,
                feedback_type TEXT NOT NULL,
                response_latency_ms REAL NOT NULL,
                tokens_used INTEGER NOT NULL,
                cost_usd REAL NOT NULL,
                query_preview TEXT NOT NULL,
                response_preview TEXT NOT NULL,
                was_helpful INTEGER NOT NULL,
                error_occurred INTEGER NOT NULL,
                error_type TEXT,
                INDEX idx_timestamp (timestamp),
                INDEX idx_model (model),
                INDEX idx_user (user_id)
            )
        ''')
        conn.commit()
        conn.close()
    
    def calculate_nps(self, 
                      start_date: Optional[str] = None,
                      end_date: Optional[str] = None,
                      model: Optional[str] = None) -> Dict:
        """
        Calculate Net Promoter Score from collected feedback
        NPS = % Promoters - % Detractors
        Range: -100 to +100
        """
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        query = "SELECT nps_score FROM nps_feedback WHERE nps_score IS NOT NULL"
        params = []
        
        if start_date:
            query += " AND timestamp >= ?"
            params.append(start_date)
        if end_date:
            query += " AND timestamp <= ?"
            params.append(end_date)
        if model:
            query += " AND model = ?"
            params.append(model)
        
        cursor.execute(query, params)
        scores = [row[0] for row in cursor.fetchall()]
        conn.close()
        
        if not scores:
            return {"nps": 0, "total_responses": 0, "breakdown": {}}
        
        promoters = sum(1 for s in scores if s >= 9)
        passives = sum(1 for s in scores if 7 <= s <= 8)
        detractors = sum(1 for s in scores if s <= 6)
        total = len(scores)
        
        nps = ((promoters - detractors) / total) * 100
        
        return {
            "nps": round(nps, 2),
            "total_responses": total,
            "breakdown": {
                "promoters": {"count": promoters, "percentage": round(promoters/total*100, 1)},
                "passives": {"count": passives, "percentage": round(passives/total*100, 1)},
                "detractors": {"count": detractors, "percentage": round(detractors/total*100, 1)}
            },
            "average_score": round(sum(scores)/len(scores), 2)
        }

app = Flask(__name__)

Initialize HolySheep NPS Collector

nps_collector = HolySheepNPSCollector( api_key="YOUR_HOLYSHEEP_API_KEY", db_path="production_nps.db" )

Model pricing for cost tracking (2026 rates)

MODEL_PRICING = { "gpt-4.1": {"input": 0.002, "output": 0.008}, # $2/MTok input, $8/MTok output "claude-sonnet-4.5": {"input": 0.003, "output": 0.015}, # $3/$15 "gemini-2.5-flash": {"input": 0.00035, "output": 0.0025}, # $0.35/$2.50 "deepseek-v3.2": {"input": 0.00007, "output": 0.00042} # $0.07/$0.42 } @app.route('/api/chat', methods=['POST']) def chat_with_nps_tracking(): """AI chat endpoint with automatic NPS metadata collection""" start_time = time.time() data = request.json user_id = data.get('user_id', 'anonymous') session_id = data.get('session_id', 'unknown') model = data.get('model', 'deepseek-v3.2') # Default to cheapest messages = data.get('messages', []) headers = { "Authorization": f"Bearer {nps_collector.api_key}", "Content-Type": "application/json" } payload = { "model": model, "messages": messages, "temperature": 0.7, "max_tokens": 2000 } try: # Call HolySheep AI API response = requests.post( f"{nps_collector.base_url}/chat/completions", headers=headers, json=payload, timeout=30 ) response_data = response.json() latency_ms = (time.time() - start_time) * 1000 # Extract usage for cost calculation usage = response_data.get('usage', {}) input_tokens = usage.get('prompt_tokens', 0) output_tokens = usage.get('completion_tokens', 0) pricing = MODEL_PRICING.get(model, MODEL_PRICING['deepseek-v3.2']) cost_usd = (input_tokens / 1_000_000 * pricing['input'] + output_tokens / 1_000_000 * pricing['output']) # Store NPS metadata (rating not yet collected) nps_record = NPSRecord( feedback_id=f"nps_{int(time.time()*1000)}", timestamp=datetime.now().isoformat(), user_id=user_id, session_id=session_id, model=model, nps_score=-1, # Pending rating feedback_type='pending', response_latency_ms=round(latency_ms, 2), tokens_used=input_tokens + output_tokens, cost_usd=round(cost_usd, 6), query_preview=messages[-1]['content'][:100] if messages else '', response_preview=response_data['choices'][0]['message']['content'][:200], was_helpful=False, error_occurred=False ) _store_nps_record(nps_record) return jsonify({ "success": True, "response": response_data, "metadata": { "latency_ms": round(latency_ms, 2), "cost_usd": round(cost_usd, 6), "feedback_id": nps_record.feedback_id, "can_rate": True } }) except Exception as e: latency_ms = (time.time() - start_time) * 1000 return jsonify({ "success": False, "error": str(e), "metadata": {"latency_ms": round(latency_ms, 2)} }), 500 def _store_nps_record(record: NPSRecord): """Persist NPS record to database""" conn = sqlite3.connect(nps_collector.db_path) cursor = conn.cursor() cursor.execute(''' INSERT INTO nps_feedback VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( record.feedback_id, record.timestamp, record.user_id, record.session_id, record.model, record.nps_score, record.feedback_type, record.response_latency_ms, record.tokens_used, record.cost_usd, record.query_preview, record.response_preview, 1 if record.was_helpful else 0, 1 if record.error_occurred else 0, record.error_type )) conn.commit() conn.close() if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False)

Frontend NPS Widget Implementation

The collection system is only as good as its user interface. We implemented a lightweight widget that captures feedback without disrupting the conversation flow. Users see a subtle prompt after receiving responses, with options for quick rating (0-10) and optional detailed feedback.

<!-- AI Chat NPS Widget - Vanilla JS Implementation -->
<div id="nps-widget" class="nps-widget" style="display: none;">
    <div class="nps-header">
        <span>How was this response?</span>
        <button onclick="closeNPSWidget()" class="nps-close">×</button>
    </div>
    <div class="nps-scores">
        <button class="nps-btn" onclick="submitNPS(0)">0</button>
        <button class="nps-btn" onclick="submitNPS(1)">1</button>
        <button class="nps-btn" onclick="submitNPS(2)">2</button>
        <button class="nps-btn" onclick="submitNPS(3)">3</button>
        <button class="nps-btn" onclick="submitNPS(4)">4</button>
        <button class="nps-btn detractor" onclick="submitNPS(5)">5</button>
        <button class="nps-btn detractor" onclick="submitNPS(6)">6</button>
        <button class="nps-btn passive" onclick="submitNPS(7)">7</button>
        <button class="nps-btn passive" onclick="submitNPS(8)">8</button>
        <button class="nps-btn promoter" onclick="submitNPS(9)">9</button>
        <button class="nps-btn promoter" onclick="submitNPS(10)">10</button>
    </div>
    <div class="nps-feedback" id="nps-feedback-section" style="display: none;">
        <textarea id="nps-comment" placeholder="Optional: Tell us more..."></textarea>
        <button onclick="submitDetailedFeedback()" class="nps-submit">Submit Feedback</button>
    </div>
    <div class="nps-labels">
        <span>Not helpful</span>
        <span>Very helpful</span>
    </div>
</div>

<style>
.nps-widget {
    position: fixed;
    bottom: 20px;
    right: 20px;
    background: white;
    border-radius: 12px;
    box-shadow: 0 4px 20px rgba(0,0,0,0.15);
    padding: 16px;
    width: 320px;
    font-family: -apple-system, BlinkMacSystemFont, sans-serif;
    z-index: 10000;
    transition: all 0.3s ease;
}

.nps-widget.detractors {
    border: 2px solid #ff4444;
}

.nps-widget.promoters {
    border: 2px solid #00c853;
}

.nps-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 12px;
    font-weight: 600;
    color: #333;
}

.nps-close {
    background: none;
    border: none;
    font-size: 20px;
    cursor: pointer;
    color: #999;
}

.nps-scores {
    display: flex;
    justify-content: space-between;
    margin-bottom: 8px;
}

.nps-btn {
    width: 24px;
    height: 24px;
    border-radius: 4px;
    border: 1px solid #ddd;
    background: white;
    cursor: pointer;
    font-size: 11px;
    transition: all 0.2s;
}

.nps-btn:hover {
    transform: scale(1.1);
    box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}

.nps-btn.detractor { background: #ffebee; color: #c62828; }
.nps-btn.passive { background: #fff8e1; color: #f57f17; }
.nps-btn.promoter { background: #e8f5e9; color: #2e7d32; }

.nps-labels {
    display: flex;
    justify-content: space-between;
    font-size: 10px;
    color: #999;
    margin-top: 4px;
}

.nps-feedback {
    margin-top: 12px;
}

.nps-feedback textarea {
    width: 100%;
    height: 60px;
    border: 1px solid #ddd;
    border-radius: 6px;
    padding: 8px;
    font-size: 13px;
    resize: none;
}

.nps-submit {
    width: 100%;
    margin-top: 8px;
    padding: 8px;
    background: #1976d2;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-weight: 500;
}

.nps-submit:hover { background: #1565c0; }
</style>

<script>
// NPS Widget State
let currentFeedbackId = null;
let selectedScore = null;

// Initialize NPS widget after AI response
function initNPSWidget(feedbackId, autoShowDelay = 5000) {
    currentFeedbackId = feedbackId;
    selectedScore = null;
    
    // Auto-show after delay (configurable)
    setTimeout(() => {
        const widget = document.getElementById('nps-widget');
        widget.style.display = 'block';
        widget.className = 'nps-widget';
        document.getElementById('nps-feedback-section').style.display = 'none';
    }, autoShowDelay);
}

function submitNPS(score) {
    selectedScore = score;
    
    // Update button styles
    document.querySelectorAll('.nps-btn').forEach(btn => {
        btn.style.transform = '';
        btn.style.boxShadow = '';
    });
    event.target.style.transform = 'scale(1.2)';
    event.target.style.boxShadow = '0 2px 8px rgba(0,0,0,0.3)';
    
    // Update widget border based on category
    const widget = document.getElementById('nps-widget');
    if (score <= 6) {
        widget.className = 'nps-widget detractors';
    } else if (score <= 8) {
        widget.className = 'nps-widget passives';
    } else {
        widget.className = 'nps-widget promoters';
    }
    
    // Send immediate rating to backend
    fetch('/api/nps/rate', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            feedback_id: currentFeedbackId,
            nps_score: score,
            feedback_type: 'explicit'
        })
    });
    
    // Show detailed feedback option for detractors and promoters
    if (score <= 6 || score >= 9) {
        document.getElementById('nps-feedback-section').style.display = 'block';
    } else {
        // Auto-close for passives after 2 seconds
        setTimeout(closeNPSWidget, 2000);
    }
}

function submitDetailedFeedback() {
    const comment = document.getElementById('nps-comment').value;
    
    fetch('/api/nps/feedback', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            feedback_id: currentFeedbackId,
            nps_score: selectedScore,
            comment: comment,
            feedback_type: 'detailed'
        })
    });
    
    closeNPSWidget();
}

function closeNPSWidget() {
    document.getElementById('nps-widget').style.display = 'none';
    currentFeedbackId = null;
}

// Hook into AI response completion
const originalFetch = window.fetch;
window.fetch = async function(...args) {
    const response = await originalFetch.apply(this, args);
    
    // If this is our chat endpoint
    if (args[0]?.includes?.('/api/chat')) {
        const data = await response.clone().json();
        if (data.metadata?.feedback_id) {
            // Widget will auto-show after 5 seconds
            initNPSWidget(data.metadata.feedback_id, 5000);
        }
    }
    
    return response;
};
</script>

Advanced Analytics: Correlating NPS with Technical Metrics

The real power of AI API NPS emerges when you correlate satisfaction scores with technical performance data. Our analytics pipeline identifies patterns: do users rate responses lower when latency exceeds 200ms? When token costs spike during complex queries? When models hallucinate specific entity types?

#!/usr/bin/env python3
"""
NPS Analytics Dashboard - Correlation Engine
Identifies patterns between technical metrics and user satisfaction
"""

import sqlite3
import pandas as pd
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
from scipy import stats
import numpy as np

class NPSAnalyticsEngine:
    """Advanced NPS analysis for AI API performance"""
    
    def __init__(self, db_path: str = "production_nps.db"):
        self.db_path = db_path
    
    def get_comprehensive_report(self, days: int = 30) -> dict:
        """Generate comprehensive NPS report with correlations"""
        
        cutoff_date = (datetime.now() - timedelta(days=days)).isoformat()
        
        conn = sqlite3.connect(self.db_path)
        
        # Load all data with ratings
        df = pd.read_sql_query('''
            SELECT * FROM nps_feedback 
            WHERE nps_score >= 0 AND timestamp >= ?
        ''', conn, params=(cutoff_date,))
        
        conn.close()
        
        df['timestamp'] = pd.to_datetime(df['timestamp'])
        df['hour'] = df['timestamp'].dt.hour
        df['day_of_week'] = df['timestamp'].dt.dayofweek
        df['is_weekend'] = df['day_of_week'] >= 5
        
        report = {
            'overall_nps': self._calculate_nps(df['nps_score'].tolist()),
            'response_metrics': self._analyze_response_metrics(df),
            'model_comparison': self._compare_models(df),
            'latency_correlation': self._correlate_with_latency(df),
            'cost_satisfaction': self._analyze_cost_satisfaction(df),
            'temporal_patterns': self._analyze_temporal_patterns(df),
            'error_impact': self._analyze_error_impact(df),
            'detractor_analysis': self._analyze_detractors(df)
        }
        
        return report
    
    def _calculate_nps(self, scores: list) -> dict:
        """Calculate NPS from score list"""
        if not scores:
            return {"nps": 0, "total": 0}
        
        promoters = sum(1 for s in scores if s >= 9)
        detractors = sum(1 for s in scores if s <= 6)
        total = len(scores)
        
        nps = ((promoters - detractors) / total) * 100
        
        return {
            "nps": round(nps, 1),
            "total_responses": total,
            "promoters": promoters,
            "passives": sum(1 for s in scores if 7 <= s <= 8),
            "detractors": detractors,
            "average_score": round(sum(scores) / len(scores), 2),
            "median_score": round(sorted(scores)[len(scores)//2], 1)
        }
    
    def _analyze_response_metrics(self, df: pd.DataFrame) -> dict:
        """Analyze response time and quality metrics"""
        
        return {
            "avg_latency_ms": round(df['response_latency_ms'].mean(), 2),
            "p50_latency_ms": round(df['response_latency_ms'].quantile(0.5), 2),
            "p95_latency_ms": round(df['response_latency_ms'].quantile(0.95), 2),
            "p99_latency_ms": round(df['response_latency_ms'].quantile(0.99), 2),
            "avg_cost_per_request": round(df['cost_usd'].mean(), 6),
            "total_cost_usd": round(df['cost_usd'].sum(), 2),
            "avg_tokens_per_request": round(df['tokens_used'].mean(), 0),
            "error_rate": round(df['error_occurred'].mean() * 100, 2)
        }
    
    def _compare_models(self, df: pd.DataFrame) -> dict:
        """
        Compare NPS by model with cost efficiency analysis
        2026 Model Pricing: GPT-4.1 $8, Claude Sonnet 4.5 $15, 
        Gemini 2.5 Flash $2.50, DeepSeek V3.2 $0.42
        """
        
        model_comparison = {}
        
        for model in df['model'].unique():
            model_df = df[df['model'] == model]
            scores = model_df['nps_score'].tolist()
            
            if not scores:
                continue
            
            model_comparison[model] = {
                "nps": round(self._calculate_nps(scores)['nps'], 1),
                "response_count": len(scores),
                "avg_latency_ms": round(model_df['response_latency_ms'].mean(), 2),
                "avg_cost_per_request": round(model_df['cost_usd'].mean(), 6),
                "total_cost_usd": round(model_df['cost_usd'].sum(), 2),
                "nps_per_dollar": round(
                    self._calculate_nps(scores)['nps'] / model_df['cost_usd'].sum() 
                    if model_df['cost_usd'].sum() > 0 else 0, 2
                ),
                "avg_score": round(sum(scores) / len(scores), 2)
            }
        
        return model_comparison
    
    def _correlate_with_latency(self, df: pd.DataFrame) -> dict:
        """Analyze correlation between latency and NPS"""
        
        valid_df = df[df['nps_score'] >= 0].copy()
        
        if len(valid_df) < 10:
            return {"correlation": 0, "interpretation": "Insufficient data"}
        
        correlation, p_value = stats.pearsonr(
            valid_df['response_latency_ms'],
            valid_df['nps_score']
        )
        
        # Bin latencies and calculate NPS for each bin
        bins = [0, 50, 100, 200, 500, 1000, float('inf')]
        labels = ['0-50ms', '50-100ms', '100-200ms', '200-500ms', '500ms-1s', '1s+']
        valid_df['latency_bin'] = pd.cut(
            valid_df['response_latency_ms'], 
            bins=bins, 
            labels=labels
        )
        
        latency_nps = {}
        for bin_label in labels:
            bin_df = valid_df[valid_df['latency_bin'] == bin_label]
            if len(bin_df) > 0:
                latency_nps[bin_label] = {
                    "nps": round(self._calculate_nps(bin_df['nps_score'].tolist())['nps'], 1),
                    "count": len(bin_df),
                    "avg_latency": round(bin_df['response_latency_ms'].mean(), 1)
                }
        
        return {
            "pearson_correlation": round(correlation, 3),
            "p_value": round(p_value, 4),
            "interpretation": self._interpret_correlation(correlation, p_value),
            "latency_bins": latency_nps,
            "threshold_recommendation": self._find_latency_threshold(valid_df)
        }
    
    def _interpret_correlation(self, correlation: float, p_value: float) -> str:
        """Human-readable interpretation of correlation"""
        
        if p_value > 0.05:
            return "Not statistically significant (p > 0.05)"
        
        if abs(correlation) < 0.1:
            strength = "negligible"
        elif abs(correlation) < 0.3:
            strength = "weak"
        elif abs(correlation) < 0.5:
            strength = "moderate"
        elif abs(correlation) < 0.7:
            strength = "strong"
        else:
            strength = "very strong"
        
        direction = "negative" if correlation < 0 else "positive"
        
        return f"{strength.capitalize()} {direction} correlation - {'Higher latency reduces NPS' if correlation < 0 else 'Higher latency increases NPS'}"
    
    def _find_latency_threshold(self, df: pd.DataFrame) -> dict:
        """Find latency threshold where NPS drops significantly"""
        
        # HolySheep AI targets <50ms latency
        holy_sheep_target = 50
        
        below_50 = df[df['response_latency_ms'] <= holy_sheep_target]
        above_50 = df[df['response_latency_ms'] > holy_sheep_target]
        
        nps_below = self._calculate_nps(below_50['nps_score'].tolist())['nps'] if len(below_50) > 0 else 0
        nps_above = self._calculate_nps(above_50['nps_score'].tolist())['nps'] if len(above_50) > 0 else 0
        
        return {
            "holy_sheep_target_ms": holy_sheep_target,
            "nps_at_or_below_target": round(nps_below, 1),
            "nps_above_target": round(nps_above, 1),
            "nps_improvement": round(nps_below - nps_above, 1),
            "meets_sla": nps_above < nps_below
        }
    
    def _analyze_cost_satisfaction(self, df: pd.DataFrame) -> dict:
        """Analyze relationship between cost per request and satisfaction"""
        
        valid_df = df[df['cost_usd'] > 0].copy()
        
        if len(valid_df) < 10:
            return {"correlation": 0, "interpretation": "Insufficient data"}
        
        correlation, p_value = stats.spearmanr(
            valid_df['cost_usd'],
            valid_df['nps_score']
        )
        
        # HolySheep AI offers ¥1=$1 rate (85%+ savings vs ¥7.3 competitors)
        cost_bins = [0, 0.001, 0.005, 0.01, 0.05, 0.10, float('inf')]
        labels = ['<$0.001', '$0.001-0.005', '$0.005-0.01', '$0.01-0.05', '$0.05-0.10', '>$0.10']
        valid_df['cost_bin'] = pd.cut(valid_df['cost_usd'], bins=cost_bins, labels=labels)
        
        cost_satisfaction = {}
        for label in labels:
            bin_df = valid_df[valid_df['cost_bin'] == label]
            if len(bin_df) > 0:
                cost_satisfaction[label] = {
                    "nps": round(self._calculate_nps(bin_df['nps_score'].tolist())['nps'], 1),
                    "count": len(bin_df),
                    "avg_cost": round(bin_df['cost_usd'].mean(), 6)
                }
        
        return {
            "spearman_correlation": round(correlation, 3),
            "p_value": round(p_value, 4),
            "cost_bins": cost_satisfaction,
            "value_recommendation": self._calculate_value_recommendation(cost_satisfaction)
        }
    
    def _calculate_value_recommendation(self, cost_satisfaction: dict) -> str:
        """Determine optimal cost-satisfaction balance"""
        
        if not cost_satisfaction:
            return "Insufficient data"
        
        # Find cost tier with best NPS
        best_tier = max(cost_satisfaction.items(), key=lambda x: x[1]['nps'])
        
        # Find most cost-efficient tier (high NPS, low cost)
        efficient_tiers = [
            (tier, data) for tier, data in cost_satisfaction.items() 
            if data['nps'] > 0 and data['count'] >= 5
        ]
        
        if efficient_tiers:
            best_value = max(efficient_tiers, key=lambda x: x[1]['nps'] / (x[1]['avg_cost'] + 0.0001))
            return f"Best satisfaction: {best_tier[0]} (NPS: {best_tier[1]['nps']}). Best value: {best_value[0]} (NPS: {best_value[1]['nps']}, Cost: ${best_value[1]['avg_cost']:.6f})"
        
        return f"Best tier: {best_tier[0]} with NPS {best_tier[1]['nps']}"
    
    def _analyze_temporal_patterns(self, df: pd.DataFrame) -> dict:
        """Analyze NPS patterns by time of day and day of week"""
        
        hour_nps = {}
        for hour in range(24):
            hour_df = df[df['hour'] == hour]
            if len(hour_df) > 0:
                hour_nps[f"{hour:02d}:00"] = {
                    "nps": round(self._calculate_nps(hour_df['nps_score'].tolist())['nps'], 1),
                    "count": len(hour_df)
                }
        
        dow_names = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
        dow_nps = {}
        for dow in range(7):
            dow_df = df[df['day_of_week'] == dow]
            if len(dow_df) > 0:
                dow_nps[dow_names[dow]] = {
                    "nps": round(self._calculate_nps(dow_df['nps_score'].tolist())['