feat: Token Limit Alerts System (Phase 3)
Database: - Create token_limit_alerts table to track alert history - Add send_token_limit_notification() function - Add check_token_limit_trigger() function - Create AFTER INSERT trigger on ai_usage_logs Features: - Automatic notifications at 80%, 90%, and 100% thresholds - One alert per threshold per month (no spam) - Notification stored in notifications table - Alert details logged in token_limit_alerts table Notification Messages: - 80%: Warning to monitor usage - 90%: Strong warning to reduce IA usage - 100%: Limit exceeded notification Next: Add enforce automatic checks in AI handlers Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -0,0 +1,195 @@
|
|||||||
|
-- Token Limit Alerts System
|
||||||
|
-- Automatically sends notifications when users approach token limits
|
||||||
|
|
||||||
|
-- Table to store alert configuration
|
||||||
|
CREATE TABLE IF NOT EXISTS token_limit_alerts (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
user_id UUID NOT NULL REFERENCES users(id),
|
||||||
|
organization_id UUID NOT NULL REFERENCES organizations(id),
|
||||||
|
alert_threshold INTEGER NOT NULL DEFAULT 80, -- 80%, 90%, 100%
|
||||||
|
alert_sent BOOLEAN DEFAULT FALSE,
|
||||||
|
alert_type VARCHAR(50) NOT NULL DEFAULT 'email', -- email, notification, both
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
sent_at TIMESTAMPTZ,
|
||||||
|
UNIQUE(user_id, alert_threshold)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_token_alerts_user ON token_limit_alerts(user_id);
|
||||||
|
CREATE INDEX idx_token_alerts_org ON token_limit_alerts(organization_id);
|
||||||
|
|
||||||
|
-- Function to send token limit notification
|
||||||
|
CREATE OR REPLACE FUNCTION send_token_limit_notification(
|
||||||
|
p_user_id UUID,
|
||||||
|
p_org_id UUID,
|
||||||
|
p_percentage INTEGER,
|
||||||
|
p_limit INTEGER,
|
||||||
|
p_used BIGINT
|
||||||
|
)
|
||||||
|
RETURNS UUID AS $$
|
||||||
|
DECLARE
|
||||||
|
v_notification_id UUID;
|
||||||
|
v_user_email VARCHAR;
|
||||||
|
v_user_name VARCHAR;
|
||||||
|
v_message TEXT;
|
||||||
|
v_title TEXT;
|
||||||
|
BEGIN
|
||||||
|
-- Get user info
|
||||||
|
SELECT email, full_name INTO v_user_email, v_user_name
|
||||||
|
FROM users WHERE id = p_user_id;
|
||||||
|
|
||||||
|
-- Create title and message based on percentage
|
||||||
|
IF p_percentage >= 100 THEN
|
||||||
|
v_title := '⚠️ Límite de Tokens IA Excedido';
|
||||||
|
v_message := format(
|
||||||
|
E'Hola %s,\n\nHas excedido tu límite mensual de tokens de IA (%s tokens).\n\nTokens usados: %s (%s%%)\n\nEl servicio de IA estará disponible hasta el próximo ciclo de facturación.',
|
||||||
|
v_user_name,
|
||||||
|
CASE WHEN p_limit = 0 THEN 'Ilimitado' ELSE p_limit::TEXT END,
|
||||||
|
p_used,
|
||||||
|
p_percentage
|
||||||
|
);
|
||||||
|
ELSIF p_percentage >= 90 THEN
|
||||||
|
v_title := '⚡ 90% de Tokens IA Utilizados';
|
||||||
|
v_message := format(
|
||||||
|
E'Hola %s,\n\nHas utilizado el 90%% de tu límite mensual de tokens de IA.\n\nTokens usados: %s de %s (%s%%)\n\nConsidera reducir el uso de funciones de IA para evitar exceder el límite.',
|
||||||
|
v_user_name,
|
||||||
|
p_used,
|
||||||
|
p_limit,
|
||||||
|
p_percentage
|
||||||
|
);
|
||||||
|
ELSIF p_percentage >= 80 THEN
|
||||||
|
v_title := '📊 80% de Tokens IA Utilizados';
|
||||||
|
v_message := format(
|
||||||
|
E'Hola %s,\n\nHas utilizado el 80%% de tu límite mensual de tokens de IA.\n\nTokens usados: %s de %s (%s%%)\n\nTe recomendamos monitorear tu uso restante.',
|
||||||
|
v_user_name,
|
||||||
|
p_used,
|
||||||
|
p_limit,
|
||||||
|
p_percentage
|
||||||
|
);
|
||||||
|
ELSE
|
||||||
|
RETURN NULL;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Insert notification
|
||||||
|
INSERT INTO notifications (
|
||||||
|
organization_id,
|
||||||
|
user_id,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
notification_type,
|
||||||
|
link_url
|
||||||
|
) VALUES (
|
||||||
|
p_org_id,
|
||||||
|
p_user_id,
|
||||||
|
v_title,
|
||||||
|
v_message,
|
||||||
|
'token_limit_alert',
|
||||||
|
'/admin/token-usage'
|
||||||
|
) RETURNING id INTO v_notification_id;
|
||||||
|
|
||||||
|
-- Log alert in token_limit_alerts table
|
||||||
|
INSERT INTO token_limit_alerts (
|
||||||
|
user_id,
|
||||||
|
organization_id,
|
||||||
|
alert_threshold,
|
||||||
|
alert_sent,
|
||||||
|
sent_at
|
||||||
|
) VALUES (
|
||||||
|
p_user_id,
|
||||||
|
p_org_id,
|
||||||
|
p_percentage,
|
||||||
|
TRUE,
|
||||||
|
NOW()
|
||||||
|
) ON CONFLICT (user_id, alert_threshold) DO UPDATE
|
||||||
|
SET alert_sent = TRUE,
|
||||||
|
sent_at = NOW();
|
||||||
|
|
||||||
|
RETURN v_notification_id;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- Trigger function to check token limits on new AI usage
|
||||||
|
CREATE OR REPLACE FUNCTION check_token_limit_trigger()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
v_limit INTEGER;
|
||||||
|
v_reset_day INTEGER;
|
||||||
|
v_used BIGINT;
|
||||||
|
v_month_start TIMESTAMPTZ;
|
||||||
|
v_percentage INTEGER;
|
||||||
|
v_org_id UUID;
|
||||||
|
BEGIN
|
||||||
|
-- Get user's limit settings
|
||||||
|
SELECT monthly_token_limit, token_limit_reset_day, organization_id
|
||||||
|
INTO v_limit, v_reset_day, v_org_id
|
||||||
|
FROM users WHERE id = NEW.user_id;
|
||||||
|
|
||||||
|
-- If no limit set or unlimited, skip
|
||||||
|
IF v_limit IS NULL OR v_limit = 0 THEN
|
||||||
|
RETURN NEW;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- Calculate month start based on reset day
|
||||||
|
v_month_start := DATE_TRUNC('month', NOW() + (v_reset_day - 1) * INTERVAL '1 day')
|
||||||
|
- (v_reset_day - 1) * INTERVAL '1 day';
|
||||||
|
|
||||||
|
-- Get current usage including this new record
|
||||||
|
SELECT COALESCE(SUM(tokens_used), 0) + NEW.tokens_used
|
||||||
|
INTO v_used
|
||||||
|
FROM ai_usage_logs
|
||||||
|
WHERE user_id = NEW.user_id
|
||||||
|
AND created_at >= v_month_start
|
||||||
|
AND id != NEW.id; -- Exclude current record to avoid double counting
|
||||||
|
|
||||||
|
-- Calculate percentage
|
||||||
|
v_percentage := ROUND((v_used::NUMERIC / v_limit * 100));
|
||||||
|
|
||||||
|
-- Send notifications at thresholds (only once per threshold per month)
|
||||||
|
IF v_percentage >= 100 THEN
|
||||||
|
-- Check if alert already sent for 100%
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM token_limit_alerts
|
||||||
|
WHERE user_id = NEW.user_id
|
||||||
|
AND alert_threshold = 100
|
||||||
|
AND sent_at >= v_month_start
|
||||||
|
) THEN
|
||||||
|
PERFORM send_token_limit_notification(
|
||||||
|
NEW.user_id, v_org_id, 100, v_limit, v_used
|
||||||
|
);
|
||||||
|
END IF;
|
||||||
|
ELSIF v_percentage >= 90 THEN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM token_limit_alerts
|
||||||
|
WHERE user_id = NEW.user_id
|
||||||
|
AND alert_threshold = 90
|
||||||
|
AND sent_at >= v_month_start
|
||||||
|
) THEN
|
||||||
|
PERFORM send_token_limit_notification(
|
||||||
|
NEW.user_id, v_org_id, 90, v_limit, v_used
|
||||||
|
);
|
||||||
|
END IF;
|
||||||
|
ELSIF v_percentage >= 80 THEN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM token_limit_alerts
|
||||||
|
WHERE user_id = NEW.user_id
|
||||||
|
AND alert_threshold = 80
|
||||||
|
AND sent_at >= v_month_start
|
||||||
|
) THEN
|
||||||
|
PERFORM send_token_limit_notification(
|
||||||
|
NEW.user_id, v_org_id, 80, v_limit, v_used
|
||||||
|
);
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- Create trigger on ai_usage_logs
|
||||||
|
DROP TRIGGER IF EXISTS token_limit_check ON ai_usage_logs;
|
||||||
|
CREATE TRIGGER token_limit_check
|
||||||
|
AFTER INSERT ON ai_usage_logs
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION check_token_limit_trigger();
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION check_token_limit_trigger() IS 'Automatically sends notifications when users reach 80%, 90%, or 100% of their token limit';
|
||||||
|
COMMENT ON TABLE token_limit_alerts IS 'Tracks when token limit alerts were sent to users';
|
||||||
Reference in New Issue
Block a user