fix: auto-fix code quality [skip ci] (#81)

This commit is contained in:
asepharyana
2026-09-23 18:32:41 +07:00
committed by GitHub
parent b9bba643bd
commit b5b370e6eb
4 changed files with 75 additions and 57 deletions
@@ -279,26 +279,37 @@ export type JevAnswers = Record<
}
>;
/** Reads the per-message answer subset by id, missing → null. */
function answersOf(
answers: JevAnswers,
id: string,
): {
/** Per-message answer subset (nullable until validated — `answersOf`). */
interface JevMessageAnswers {
v?: { type: "noul"; noul: number };
status?: { type: "choice"; choice: string; confidence: number };
severity?: { type: "choice"; choice: string };
category?: { type: "choice"; choice: string };
action?: { type: "choice"; choice: string };
} {
}
/** Reads the per-message answer subset by id, missing → undefined. */
function answersOf(answers: JevAnswers, id: string): JevMessageAnswers {
return {
v: answers[`${id}__v`] as never,
status: answers[`${id}__status`] as never,
severity: answers[`${id}__severity`] as never,
category: answers[`${id}__category`] as never,
action: answers[`${id}__action`] as never,
v: answers[`${id}__v`] as JevMessageAnswers["v"],
status: answers[`${id}__status`] as JevMessageAnswers["status"],
severity: answers[`${id}__severity`] as JevMessageAnswers["severity"],
category: answers[`${id}__category`] as JevMessageAnswers["category"],
action: answers[`${id}__action`] as JevMessageAnswers["action"],
};
}
/** Optionally-typed accessor for a choice answer's label ("" when missing). */
function labelOf(a: { type: "choice"; choice: string } | undefined): string {
return a?.type === "choice" ? a.choice : "";
}
/** Set form of the vocab arrays for O(1) membership tests. */
const JEV_STATUS_SET = new Set<string>(JEV_STATUSES);
const JEV_SEVERITY_SET = new Set<string>(JEV_SEVERITIES);
const JEV_CATEGORY_SET = new Set<string>(JEV_CATEGORIES);
const JEV_ACTION_SET = new Set<string>(JEV_ACTIONS);
/**
* Decide per-message Jev acceptance. Requires ALL five questions present
* with valid labels and CROSS-CONSISTENT semantics:
@@ -331,39 +342,42 @@ export function isJevAccepted(
)
return false;
if (!JEV_STATUSES.includes(status.choice as (typeof JEV_STATUSES)[number]))
return false;
// Valid label = one of the vocab const arrays. The membership guards
// (Set.has) reject anything unknown, then the labels are narrowed via the
// const-array includes so the downstream comparisons typecheck.
const statusLabel = labelOf(status);
const severityLabel = labelOf(severity);
const categoryLabel = labelOf(category);
const actionLabel = labelOf(action);
if (
!JEV_SEVERITIES.includes(severity.choice as (typeof JEV_SEVERITIES)[number])
!JEV_STATUS_SET.has(statusLabel) ||
!JEV_SEVERITY_SET.has(severityLabel) ||
!JEV_CATEGORY_SET.has(categoryLabel) ||
!JEV_ACTION_SET.has(actionLabel)
)
return false;
if (
!JEV_CATEGORIES.includes(category.choice as (typeof JEV_CATEGORIES)[number])
)
return false;
if (!JEV_ACTIONS.includes(action.choice as (typeof JEV_ACTIONS)[number]))
return false;
return false; // unknown label — LLM fallback
const s = statusLabel as (typeof JEV_STATUSES)[number];
const sev = severityLabel as (typeof JEV_SEVERITIES)[number];
const cat = categoryLabel as (typeof JEV_CATEGORIES)[number];
const act = actionLabel as (typeof JEV_ACTIONS)[number];
const noulVal = a.v.noul;
// noul ↔ status consistency
if (status.choice === "clean" && noulVal >= 0.5) return false;
if (status.choice !== "clean" && noulVal < 0.5) return false;
if (s === "clean" && noulVal >= 0.5) return false;
if (s !== "clean" && noulVal < 0.5) return false;
// severity ↔ status: clean must be none; flagged/warn must NOT be none
if (status.choice === "clean" && severity.choice !== "none") return false;
if (status.choice !== "clean" && severity.choice === "none") return false;
if (s === "clean" && sev !== "none") return false;
if (s !== "clean" && sev === "none") return false;
// action ↔ status: clean must be none; flagged must NOT be none;
// warn must not delete/escalate; clean must never delete/escalate
if (status.choice === "clean" && action.choice !== "none") return false;
if (status.choice === "flagged" && action.choice === "none") return false;
if (
status.choice === "warn" &&
(action.choice === "delete" || action.choice === "escalate")
)
return false;
if (s === "clean" && act !== "none") return false;
if (s === "flagged" && act === "none") return false;
if (s === "warn" && (act === "delete" || act === "escalate")) return false;
// category ↔ status: clean must be none; flagged must NOT be none
if (status.choice === "clean" && category.choice !== "none") return false;
if (status.choice === "flagged" && category.choice === "none") return false;
if (s === "clean" && cat !== "none") return false;
if (s === "flagged" && cat === "none") return false;
return true;
}
@@ -378,12 +392,16 @@ export function mapJevAnswersToResult(
): AnalysisResult {
const a = answersOf(answers, messageId);
const v = a.v as { type: "noul"; noul: number };
const st = a.status as { type: "choice"; choice: string; confidence: number };
const sev = a.severity as { type: "choice"; choice: string };
const cat = a.category as { type: "choice"; choice: string };
const act = a.action as { type: "choice"; choice: string };
const st = a.status as {
type: "choice";
choice: string;
confidence: number;
};
const sev = labelOf(a.severity);
const cat = labelOf(a.category);
const act = labelOf(a.action);
const status = st.choice as "clean" | "warn" | "flagged";
const status = st.choice as (typeof JEV_STATUSES)[number];
// Calibrated score: clean → 0; warn → 0.45; flagged → P(violates) clamped.
const rawNoul = typeof v.noul === "number" ? v.noul : 0;
const score =
@@ -400,15 +418,15 @@ export function mapJevAnswersToResult(
return {
messageId,
status,
flags: cat.choice === "none" ? [] : [cat.choice],
flags: cat === "none" ? [] : [cat],
score,
analysis:
`[Jev] status=${status}, kategori=${cat.choice}, keparahan=${sev.choice}, ` +
`keyakinan=${confidence.toFixed(2)}, tindakan=${act.choice}, p_melanggar=${rawNoul.toFixed(2)}`,
categories: cat.choice === "none" ? [] : [cat.choice],
severity: sev.choice as AnalysisResult["severity"],
`[Jev] status=${status}, kategori=${cat}, keparahan=${sev}, ` +
`keyakinan=${confidence.toFixed(2)}, tindakan=${act}, p_melanggar=${rawNoul.toFixed(2)}`,
categories: cat === "none" ? [] : [cat],
severity: sev as AnalysisResult["severity"],
confidence,
recommendedAction: act.choice as AnalysisResult["recommendedAction"],
recommendedAction: act as AnalysisResult["recommendedAction"],
policyVersion: JEV_POLICY_VERSION,
evidence: [],
};
@@ -483,7 +501,7 @@ export async function analyzeBatchWithJev(
outcome.raw = systemOneResult;
const answers = systemOneResult.answers as unknown as JevAnswers;
const minConfidence = config.AI_LLM_JEV_MIN_CONFIDENCE ?? 0.9;
const minConfidence = config.AI_LLM_JEV_MIN_CONFIDENCE;
for (const t of targets) {
if (isJevAccepted(answers, t.id, minConfidence)) {
@@ -171,7 +171,11 @@ export async function runTextOnlyBatch(
if (urlArr.length >= 10) break;
}
if (urlArr.length === 0) {
return { text: new Map(), image: new Map(), title: new Map() };
return {
text: new Map(),
image: new Map(),
title: new Map(),
} satisfies UrlFetchResult;
}
const results = await Promise.allSettled(
urlArr.map((url) => fetchUrlSafely(url)),
@@ -365,7 +369,7 @@ export async function runTextOnlyBatch(
const messagesBlock = (
await Promise.all(
workingSet.map(async (msg) => {
const content = truncateForAi(getAnalysisContent(msg));
const content = analysisContentOf(msg);
const msgUrls = extractUrlsFromText(content);
const urlContexts = msgUrls
.map((url) => {
@@ -201,8 +201,8 @@ export const configSchema = z
AI_LLM_JEV_ENABLED: z
.string()
.optional()
.transform((v) => v === "true")
.default(true),
.default("false")
.transform((v) => v === "true"),
AI_LLM_JEV_API_KEY: z.string().optional().default(""),
AI_LLM_JEV_BASE_URL: z.string().url().default("http://127.0.0.1:4014"),
AI_LLM_JEV_MODEL: z.string().default("oc/jev-1.13-free"),
@@ -11,6 +11,7 @@
*/
import { describe, expect, it } from "vitest";
import { analyzeBatchWithJev } from "../src/modules/ai-moderation/jevAnalyzer.js";
import type { AnalysisResult } from "../src/modules/message-capture/types.js";
const SMOKE = process.env.AI_LLM_JEV_SMOKE === "1";
const SKIP_REASON =
@@ -75,13 +76,8 @@ describe("Jev live smoke (real 9router /v1/systemone)", () => {
expect(byId.m_clean?.status).toBe("clean");
expect(byId.m_help?.status).toBe("clean");
// Verdicts are calibration-honest
const verdicts = outcome.results as Array<{
status: string;
confidence: number;
score: number;
analysis: string;
}>;
// Verdicts are calibration-honest (typed by the real pipeline shape)
const verdicts = outcome.results as AnalysisResult[];
for (const r of verdicts) {
expect(r.confidence).toBeGreaterThanOrEqual(0.9);
if (r.status === "clean") expect(r.score).toBe(0);