chore(features): 🔧 Update feature scope documentation to correctly identify "messaging"
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
0583a97bd4
commit
46ab2d9ea8
3 changed files with 16 additions and 62 deletions
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue