chore(features): 🔧 Update feature scope documentation to correctly identify "messaging"

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Lilith 2026-03-13 04:42:39 -07:00
parent 0583a97bd4
commit 46ab2d9ea8
3 changed files with 16 additions and 62 deletions

View file

@ -2,7 +2,7 @@
* Content Moderation Worker Protocol Tests
*
* Tests the Web Worker message handling protocol by mocking the
* ContentClassifier from @lilith/content-moderation-inference.
* ContentClassifier from @lilith/content-moderation.
* The ML model can't be loaded in jsdom, so we mock the classifier.
*
* Worker state persists across tests (module-level variables).
@ -23,7 +23,7 @@ const { postMessageSpy, mockClassify, mockGetBackend, mockDispose } = vi.hoisted
return { postMessageSpy, mockClassify, mockGetBackend, mockDispose };
});
vi.mock('@lilith/content-moderation-inference', () => ({
vi.mock('@lilith/content-moderation', () => ({
ContentClassifier: {
create: vi.fn().mockResolvedValue({
classify: mockClassify,
@ -57,9 +57,11 @@ function makeCleanResult(): ClassificationResult {
inferenceTimeMs: 5.0,
backend: 'wasm',
cached: false,
modelName: 'Xenova/toxic-bert',
modelName: 'lilith/content-moderation-v1',
quantization: 'q4',
cacheStats: { size: 0, maxSize: 256, hits: 0, misses: 0, hitRate: 0 },
normalizationApplied: false,
normalizationStages: [],
},
};
}
@ -72,6 +74,7 @@ function makeThreatResult(): ClassificationResult {
confidence: 0.92,
severity: 'critical',
reason: 'Threatening language detected',
detectionMethod: 'ml_inference',
},
],
structuralFlags: [],
@ -81,9 +84,11 @@ function makeThreatResult(): ClassificationResult {
inferenceTimeMs: 18.0,
backend: 'wasm',
cached: false,
modelName: 'Xenova/toxic-bert',
modelName: 'lilith/content-moderation-v1',
quantization: 'q4',
cacheStats: { size: 1, maxSize: 256, hits: 0, misses: 1, hitRate: 0 },
normalizationApplied: false,
normalizationStages: [],
},
};
}
@ -92,6 +97,11 @@ const ALL_CATEGORIES = [
'threats', 'hate_speech', 'csam', 'scam_patterns', 'contact_info',
'solicitation', 'spam', 'profanity', 'adult_content',
'doxxing', 'predatory_behavior', 'law_enforcement',
'sextortion', 'ncii', 'trafficking', 'self_harm',
'impersonation', 'harassment', 'age_play', 'bestiality',
'necrophilia', 'scat', 'snuff', 'extreme_gore',
'bdsm', 'edge_play', 'furry', 'watersports',
'roleplay', 'financial_coercion', 'consent_violation', 'intoxication',
];
describe('content-moderation worker protocol', () => {
@ -106,7 +116,6 @@ describe('content-moderation worker protocol', () => {
config: {
threshold: 0.4,
enabledCategories: ALL_CATEGORIES,
categoryWeights: {},
},
});
@ -169,7 +178,7 @@ describe('content-moderation worker protocol', () => {
expect(msg.result.recommendedAction).toBeDefined();
expect(msg.result.metadata).toBeDefined();
expect(msg.result.metadata.backend).toBe('wasm');
expect(msg.result.metadata.modelName).toBe('Xenova/toxic-bert');
expect(msg.result.metadata.modelName).toBe('lilith/content-moderation-v1');
});
});

View file

@ -8,12 +8,11 @@
import type { FC } from 'react';
import { CATEGORY_LABELS } from '@lilith/moderated-text-input';
import type { InferenceMetadata, CategoryScore, CodedLanguageMatch } from '@lilith/moderated-text-input';
import type { InferenceMetadata, CategoryScore } from '@lilith/moderated-text-input';
interface ModerationMetadataPanelProps {
metadata: InferenceMetadata | undefined;
categories?: CategoryScore[];
codedLanguageMatches?: CodedLanguageMatch[];
}
const BACKEND_LABELS: Record<string, string> = {
@ -154,22 +153,9 @@ function getConfidenceColor(confidence: number): string {
return '#4ade80';
}
const TIER_COLORS: Record<string, string> = {
critical: '#ff3366',
high: '#ff6644',
};
const SEVERITY_COLORS: Record<string, string> = {
critical: '#ff3366',
high: '#ff6644',
medium: '#ffaa00',
low: '#86efac',
};
export const ModerationMetadataPanel: FC<ModerationMetadataPanelProps> = ({
metadata,
categories,
codedLanguageMatches,
}) => {
if (!metadata) {
return (
@ -256,39 +242,6 @@ export const ModerationMetadataPanel: FC<ModerationMetadataPanelProps> = ({
</div>
)}
{/* Coded Language Matches */}
{codedLanguageMatches && codedLanguageMatches.length > 0 && (
<div style={styles.section}>
<div style={styles.sectionTitle}>Coded Language Detection</div>
{codedLanguageMatches.map((match, i) => (
<div key={`${match.patternId}-${i}`} style={{ marginBottom: '0.5rem' }}>
<div style={styles.categoryRow}>
<span style={{ ...styles.categoryName, width: '100px' }}>
{match.patternId}
</span>
<span style={styles.badge(TIER_COLORS[match.tier] ?? '#ffaa00')}>
{match.tier}
</span>
<span style={styles.badge(SEVERITY_COLORS[match.severity] ?? '#86efac')}>
{match.severity}
</span>
<span style={styles.categoryValue}>
{(match.confidence * 100).toFixed(0)}%
</span>
</div>
<div style={{ fontSize: '0.6875rem', color: 'rgba(255, 255, 255, 0.4)', marginLeft: '0.25rem' }}>
{match.reason}
{match.evasionDetected && (
<span style={{ color: '#ff6644', marginLeft: '0.5rem' }}>
[evasion detected]
</span>
)}
</div>
</div>
))}
</div>
)}
{/* Cache Statistics */}
<div style={styles.section}>
<div style={styles.sectionTitle}>Cache Statistics</div>

View file

@ -17,7 +17,6 @@ import {
type ContentModerationState,
type InferenceMetadata,
type CategoryScore,
type CodedLanguageMatch,
} from '@lilith/moderated-text-input';
import ContentModerationWorker from '@/features/inbox/workers/content-moderation.worker?worker';
import { ModerationMetadataPanel } from './ModerationMetadataPanel';
@ -229,7 +228,6 @@ export const ModerationPane: FC = () => {
const [selectedMessageId, setSelectedMessageId] = useState<string | null>(null);
const [liveMetadata, setLiveMetadata] = useState<InferenceMetadata | undefined>();
const [liveCategories, setLiveCategories] = useState<CategoryScore[] | undefined>();
const [liveCodedLanguageMatches, setLiveCodedLanguageMatches] = useState<CodedLanguageMatch[] | undefined>();
const [inputValue, setInputValue] = useState('');
const [textareaValue, setTextareaValue] = useState('');
@ -261,9 +259,6 @@ export const ModerationPane: FC = () => {
if (state.categories.length > 0) {
setLiveCategories(state.categories);
}
if (state.codedLanguageMatches.length > 0) {
setLiveCodedLanguageMatches(state.codedLanguageMatches);
}
},
[],
);
@ -271,8 +266,6 @@ export const ModerationPane: FC = () => {
const selectedMessage = sentMessages.find((m) => m.id === selectedMessageId);
const displayMetadata = selectedMessage?.metadata ?? liveMetadata;
const displayCategories = selectedMessage?.categories ?? liveCategories;
const displayCodedMatches = liveCodedLanguageMatches;
const handlePresetClick = useCallback((text: string) => {
setInputValue(text);
}, []);
@ -404,7 +397,6 @@ export const ModerationPane: FC = () => {
<ModerationMetadataPanel
metadata={displayMetadata}
categories={displayCategories}
codedLanguageMatches={displayCodedMatches}
/>
</div>
</div>