GLINR Studio LogoTypeWeaver

ML Integration Guide

Best practices for integrating ML-powered toxicity detection into your application

Edit on GitHub

This guide covers best practices, architecture patterns, and optimization strategies for ML-powered content moderation.

When to Use ML Detection

ML detection excels at catching contextual toxicity that word lists miss — insults, threats, and harmful content expressed without explicit profanity.

ML Catches What Rules Miss

ExampleRulesML
"you fucking idiot"✅✅
"you are worthless"❌✅
"go kys loser"❌✅
"nobody wants you here"❌✅
"I hope bad things happen to you"❌✅

When to Enable ML

Enable ML when:

  • Moderating user-generated content (comments, chat, forums)
  • Users actively try to evade filters
  • Context matters (gaming, social, professional)
  • You need to catch threats and harassment

Skip ML when:

  • Simple word filtering is sufficient
  • Latency is critical (under 10ms requirement)
  • Running on edge/serverless with strict limits
  • Budget constraints (ML uses more compute)

Architecture Patterns

Pattern 1: ML as Primary (High Accuracy)

import { HybridFilter } from 'glin-profanity/ml';

const filter = new HybridFilter({
  enableML: true,
  combinationMode: 'ml-override',
  mlThreshold: 0.85,
});

// ML result takes precedence
const result = await filter.checkProfanityAsync(text);

Pros: Best accuracy, handles context well Cons: Slower (~50-100ms), higher resource usage

Pattern 2: Rules First, ML Fallback (Balanced)

import { HybridFilter } from 'glin-profanity/ml';

const filter = new HybridFilter({
  enableML: true,
  combinationMode: 'rules-first',
  borderlineThreshold: 0.5,
  detectLeetspeak: true,
});

// Fast rules catch obvious cases, ML handles edge cases
const result = await filter.checkProfanityAsync(text);

Pros: Fast for common cases, ML safety net Cons: Slightly more complex logic

Pattern 3: Tiered Moderation (High Volume)

import { Filter } from 'glin-profanity';
import { ToxicityDetector } from 'glin-profanity/ml';

class TieredModerator {
  private ruleFilter = new Filter({ detectLeetspeak: true });
  private mlDetector = new ToxicityDetector({ threshold: 0.85 });
  private mlReady = false;

  async initialize() {
    await this.mlDetector.loadModel();
    this.mlReady = true;
  }

  async moderate(text: string) {
    // Tier 1: Fast rule-based check
    const ruleResult = this.ruleFilter.checkProfanity(text);
    if (ruleResult.containsProfanity) {
      return { action: 'block', reason: 'profanity', fast: true };
    }

    // Tier 2: ML check for borderline content
    if (this.mlReady) {
      const mlResult = await this.mlDetector.analyze(text);
      if (mlResult.isToxic) {
        return { action: 'flag', reason: mlResult.matchedCategories, fast: false };
      }
    }

    return { action: 'allow', fast: true };
  }
}

Pros: Maximum throughput, graceful degradation Cons: More code to maintain

Optimization Strategies

1. Preload Model on Startup

// app.ts or server.ts
import { HybridFilter } from 'glin-profanity/ml';

let moderator: HybridFilter;

async function initializeApp() {
  moderator = new HybridFilter({ enableML: true });

  console.log('Loading ML model...');
  await moderator.initialize();
  console.log('ML model ready');

  // Start server after model is ready
  app.listen(3000);
}

initializeApp();

2. Use Batch Processing

// Process multiple texts in one ML call
const comments = ['comment1', 'comment2', 'comment3'];

// Bad: N separate ML calls
for (const comment of comments) {
  await filter.checkProfanityAsync(comment); // Slow!
}

// Good: Single batched ML call
const results = await filter.checkProfanityBatchAsync(comments); // Fast!

3. Cache Results

import { LRUCache } from 'lru-cache';

const cache = new LRUCache<string, boolean>({ max: 1000 });

async function checkWithCache(text: string) {
  const cached = cache.get(text);
  if (cached !== undefined) return cached;

  const result = await filter.isToxicAsync(text);
  cache.set(text, result);
  return result;
}

4. Use Rules for Quick Reject

async function smartModerate(text: string) {
  // Quick sync check first
  if (filter.isProfane(text)) {
    return { blocked: true, source: 'rules' };
  }

  // ML only for content that passed rules
  const mlResult = await filter.analyzeWithML(text);
  if (mlResult?.isToxic) {
    return { blocked: true, source: 'ml' };
  }

  return { blocked: false };
}

Memory Management

Serverless Considerations

// Lambda / Vercel Functions
let filter: HybridFilter | null = null;

export async function handler(event) {
  // Reuse filter across warm invocations
  if (!filter) {
    filter = new HybridFilter({ enableML: true });
    await filter.initialize();
  }

  const result = await filter.checkProfanityAsync(event.body.text);
  return { statusCode: 200, body: JSON.stringify(result) };
}

Long-Running Services

class ModerationService {
  private filter: HybridFilter;

  constructor() {
    this.filter = new HybridFilter({ enableML: true });
  }

  async start() {
    await this.filter.initialize();
  }

  async stop() {
    // Free memory on shutdown
    this.filter.dispose();
  }
}

Error Handling

import { HybridFilter } from 'glin-profanity/ml';

async function safeModeerate(text: string) {
  try {
    const result = await filter.checkProfanityAsync(text);
    return result;
  } catch (error) {
    console.error('ML moderation failed:', error);

    // Fallback to rule-based only
    const ruleResult = filter.checkProfanity(text);
    return {
      isToxic: ruleResult.containsProfanity,
      reason: 'Rule-based fallback (ML unavailable)',
      confidence: 0.7,
    };
  }
}

Testing ML Integration

// test/moderation.test.ts
import { ToxicityDetector } from 'glin-profanity/ml';

describe('ML Moderation', () => {
  let detector: ToxicityDetector;

  beforeAll(async () => {
    detector = new ToxicityDetector({ threshold: 0.85 });
    await detector.loadModel();
  });

  afterAll(() => {
    detector.dispose();
  });

  test('detects insults', async () => {
    const result = await detector.analyze('you are an idiot');
    expect(result.isToxic).toBe(true);
    expect(result.matchedCategories).toContain('insult');
  });

  test('allows clean content', async () => {
    const result = await detector.analyze('great work on this project');
    expect(result.isToxic).toBe(false);
  });

  test('handles batch processing', async () => {
    const results = await detector.analyzeBatch([
      'hello friend',
      'you are terrible',
    ]);
    expect(results[0].isToxic).toBe(false);
    expect(results[1].isToxic).toBe(true);
  });
});

Monitoring & Logging

import { HybridFilter } from 'glin-profanity/ml';

async function moderateWithMetrics(text: string) {
  const startTime = Date.now();

  const result = await filter.checkProfanityAsync(text);

  // Log metrics
  const metrics = {
    latency: Date.now() - startTime,
    isToxic: result.isToxic,
    confidence: result.confidence,
    mlUsed: result.mlResult !== null,
    categories: result.mlResult?.matchedCategories ?? [],
  };

  // Send to monitoring (Datadog, etc.)
  logger.info('moderation_check', metrics);

  return result;
}

Cross-References