FRE-4517, FRE-4499: Complete SpamShield implementation and billing updates
- SpamFeedback table migration with timestamp index - Real-time interception engine completion - Billing service enhancements - Classifier and rule engine updates Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -10,6 +10,10 @@ import {
|
||||
DEFAULT_EVALUATION_TIMEOUT,
|
||||
DEFAULT_FALLBACK_DECISION,
|
||||
DEFAULT_FALLBACK_ON_TIMEOUT,
|
||||
SHORT_CALL_SCORE,
|
||||
SHORT_SMS_SCORE,
|
||||
SHORT_CONTENT_SCORE,
|
||||
URGENT_KEYWORD_SCORE,
|
||||
} from '../constants/decision-engine.constants';
|
||||
|
||||
export interface CallMetadata {
|
||||
@@ -44,6 +48,7 @@ export interface DecisionContext {
|
||||
cachedReputation: ReputationResult;
|
||||
ruleMatches: RuleMatch[];
|
||||
userHistory?: UserSpamHistory;
|
||||
requestId?: string;
|
||||
}
|
||||
|
||||
export interface DecisionResult {
|
||||
@@ -59,6 +64,7 @@ export interface DecisionResult {
|
||||
totalScore: number;
|
||||
};
|
||||
executedAt: Date;
|
||||
requestId?: string;
|
||||
}
|
||||
|
||||
export interface DecisionEngineConfig {
|
||||
@@ -109,6 +115,7 @@ export class DecisionEngine {
|
||||
|
||||
async evaluate(context: DecisionContext): Promise<DecisionResult> {
|
||||
const startTime = Date.now();
|
||||
const reqId = context.requestId ?? 'unknown';
|
||||
|
||||
try {
|
||||
const [reputationScore, ruleScore, behavioralScore, userHistoryScore] = await Promise.all([
|
||||
@@ -118,7 +125,7 @@ export class DecisionEngine {
|
||||
this.calculateUserHistoryScore(context.userHistory),
|
||||
]);
|
||||
|
||||
const totalScore =
|
||||
const totalScore =
|
||||
reputationScore * this.config.reputationWeight +
|
||||
ruleScore * this.config.ruleWeight +
|
||||
behavioralScore * this.config.behavioralWeight +
|
||||
@@ -142,10 +149,11 @@ export class DecisionEngine {
|
||||
totalScore,
|
||||
},
|
||||
executedAt: new Date(),
|
||||
requestId: reqId,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[DecisionEngine] Evaluation error:', error);
|
||||
|
||||
console.error(`[DecisionEngine] [${reqId}] Evaluation error:`, error);
|
||||
|
||||
if (this.config.fallbackOnTimeout) {
|
||||
return {
|
||||
decision: this.config.fallbackDecision,
|
||||
@@ -160,6 +168,7 @@ export class DecisionEngine {
|
||||
totalScore: 0.5,
|
||||
},
|
||||
executedAt: new Date(),
|
||||
requestId: reqId,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -187,11 +196,11 @@ export class DecisionEngine {
|
||||
const { callMetadata } = context;
|
||||
|
||||
if (callMetadata.duration && callMetadata.duration < 5) {
|
||||
score += 0.3;
|
||||
score += SHORT_CALL_SCORE;
|
||||
}
|
||||
|
||||
if (callMetadata.callType === 'sms') {
|
||||
score += 0.1;
|
||||
score += SHORT_SMS_SCORE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,11 +208,11 @@ export class DecisionEngine {
|
||||
const { smsContent } = context;
|
||||
|
||||
if (smsContent.body.length < 10) {
|
||||
score += 0.2;
|
||||
score += SHORT_CONTENT_SCORE;
|
||||
}
|
||||
|
||||
if (/\b(URGENT|ACT NOW|LIMITED)\b/i.test(smsContent.body)) {
|
||||
score += 0.3;
|
||||
score += URGENT_KEYWORD_SCORE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { PrismaClient, SpamRule } from '@prisma/client';
|
||||
import { generateRequestId } from '@shieldai/types';
|
||||
|
||||
export interface RuleMatch {
|
||||
ruleId: string;
|
||||
@@ -78,7 +79,7 @@ export class RuleEngine {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[RuleEngine] Invalid pattern for rule ${rule.id}:`, error);
|
||||
console.error(`[RuleEngine] [req:${generateRequestId()}] Invalid pattern for rule ${rule.id}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +107,7 @@ export class RuleEngine {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[RuleEngine] Invalid pattern for rule ${rule.id}:`, error);
|
||||
console.error(`[RuleEngine] [req:${generateRequestId()}] Invalid pattern for rule ${rule.id}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user