fix(小宝预警): 增加快照节流和 AI cooldown
This commit is contained in:
@@ -24,6 +24,14 @@ type RiskInsightPrevious = Pick<
|
||||
const SCORE_TRIGGER_DELTA = 15;
|
||||
const CONFIDENCE_DROP_DELTA = 15;
|
||||
const ONE_DAY_MS = 86_400_000;
|
||||
const RISK_INSIGHT_COOLDOWN_MS = 6 * 60 * 60 * 1000;
|
||||
const RISK_LEVEL_RANK: Record<XiaobaoVersionRisk['riskLevel'], number> = {
|
||||
on_track: 0,
|
||||
attention: 1,
|
||||
at_risk: 2,
|
||||
likely_delayed: 3,
|
||||
blocked: 4,
|
||||
};
|
||||
|
||||
export function shouldRequestRiskInsight(current: RiskInsightCurrent, previous?: RiskInsightPrevious): boolean {
|
||||
if (current.riskLevel === 'on_track') return false;
|
||||
@@ -57,6 +65,31 @@ export function shouldRequestRiskInsight(current: RiskInsightCurrent, previous?:
|
||||
);
|
||||
}
|
||||
|
||||
export function findLatestRiskInsightForVersion(
|
||||
cache: XiaobaoRiskInsightCacheItem[],
|
||||
versionId: string,
|
||||
): XiaobaoRiskInsightCacheItem | undefined {
|
||||
return cache
|
||||
.filter((item) => item.versionId === versionId)
|
||||
.sort((a, b) => getTime(b.generatedAt) - getTime(a.generatedAt))[0];
|
||||
}
|
||||
|
||||
export function shouldRequestRiskInsightWithCooldown(
|
||||
current: RiskInsightCurrent,
|
||||
previous?: RiskInsightPrevious,
|
||||
latestInsight?: XiaobaoRiskInsightCacheItem,
|
||||
now: Date = new Date(),
|
||||
): boolean {
|
||||
if (!shouldRequestRiskInsight(current, previous)) return false;
|
||||
if (!latestInsight) return true;
|
||||
if (isRiskLevelEscalation(current.riskLevel, latestInsight)) return true;
|
||||
|
||||
const generatedAt = getTime(latestInsight.generatedAt);
|
||||
const nowTime = now.getTime();
|
||||
if (!Number.isFinite(generatedAt) || !Number.isFinite(nowTime)) return true;
|
||||
return nowTime - generatedAt >= RISK_INSIGHT_COOLDOWN_MS;
|
||||
}
|
||||
|
||||
export function buildRiskInsightSignature(risk: XiaobaoVersionRisk): string {
|
||||
return JSON.stringify({
|
||||
versionId: risk.versionId,
|
||||
@@ -219,3 +252,26 @@ function compareSignatureRows<T>(a: T, b: T): number {
|
||||
function clampScore(score: number): number {
|
||||
return Math.max(0, Math.min(100, Math.round(score)));
|
||||
}
|
||||
|
||||
function isRiskLevelEscalation(
|
||||
currentLevel: XiaobaoVersionRisk['riskLevel'],
|
||||
latestInsight: XiaobaoRiskInsightCacheItem,
|
||||
): boolean {
|
||||
const previousLevel = parseRiskLevel(latestInsight.riskSignature);
|
||||
if (!previousLevel) return false;
|
||||
return RISK_LEVEL_RANK[currentLevel] > RISK_LEVEL_RANK[previousLevel];
|
||||
}
|
||||
|
||||
function parseRiskLevel(signature: string): XiaobaoVersionRisk['riskLevel'] | undefined {
|
||||
try {
|
||||
const parsed = JSON.parse(signature) as { riskLevel?: XiaobaoVersionRisk['riskLevel'] };
|
||||
return parsed.riskLevel && parsed.riskLevel in RISK_LEVEL_RANK ? parsed.riskLevel : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function getTime(value: string): number {
|
||||
const time = new Date(value).getTime();
|
||||
return Number.isFinite(time) ? time : Number.NaN;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user