Class: Moderate::Label

Inherits:
Data
  • Object
show all
Defined in:
lib/moderate/label.rb,
lib/moderate/label.rb

Overview

— Canonical taxonomy constants (attached to Moderate::Label) ————- Defined here, by reopening the class, rather than inside the Data.define block above, because constant assignment inside that block would leak to the ‘Moderate` namespace instead of landing on `Moderate::Label`.

Constant Summary collapse

TAXONOMY =

The canonical OpenAI moderation taxonomy: each top-level category mapped to its allowed subcategories. Sources, in the README and OpenAI’s docs (developers.openai.com/api/docs/guides/moderation):

harassment      → :threatening
hate            → :threatening
sexual          → :minors
self-harm       → :intent, :instructions
violence        → :graphic
illicit         → :violent

‘nil` is always an implicitly-valid subcategory (the bare top-level category, e.g. plain `:hate` with no qualifier).

NOTE: ‘self-harm` is the hyphenated symbol `:“self-harm”` to match OpenAI’s wire format verbatim — ‘Result#categories` joins category+subcategory with “/” to reproduce OpenAI’s exact slug strings (“self-harm/intent” etc.), so downstream consumers comparing against OpenAI labels line up byte-for-byte.

{
  harassment: %i[threatening],
  hate: %i[threatening],
  sexual: %i[minors],
  "self-harm": %i[intent instructions],
  violence: %i[graphic],
  illicit: %i[violent]
}.freeze
CATEGORIES =

Every canonical category as a flat symbol list, for validation and iteration.

TAXONOMY.keys.freeze
INPUTS =

Which inputs an adapter can attribute a label to. ‘:text` and `:image` mirror OpenAI’s multimodal ‘category_applied_input_types`; `:unknown` is the safe default for adapters (like the offline wordlist) that only see one kind of input and don’t bother to say which.

%i[text image unknown].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(category:, subcategory: nil, score: 1.0, flagged: true, input: :unknown) ⇒ Label

Normalize everything on the way in so adapters can be sloppy about types:

- category/subcategory/input accepted as String or Symbol, downcased
- score coerced to Float (defaults to 1.0 — deterministic adapters like the
  wordlist have no probability, so a trip is "certain")
- flagged defaults to true (you only build a Label when something matched)


35
36
37
38
39
40
41
42
43
# File 'lib/moderate/label.rb', line 35

def initialize(category:, subcategory: nil, score: 1.0, flagged: true, input: :unknown)
  super(
    category: normalize_symbol(category),
    subcategory: subcategory.nil? ? nil : normalize_symbol(subcategory),
    score: score.nil? ? nil : score.to_f,
    flagged: flagged ? true : false,
    input: normalize_symbol(input || :unknown)
  )
end

Instance Attribute Details

#categoryObject (readonly)

Returns the value of attribute category

Returns:

  • (Object)

    the current value of category



22
23
24
# File 'lib/moderate/label.rb', line 22

def category
  @category
end

#flaggedObject (readonly)

Returns the value of attribute flagged

Returns:

  • (Object)

    the current value of flagged



22
23
24
# File 'lib/moderate/label.rb', line 22

def flagged
  @flagged
end

#inputObject (readonly)

Returns the value of attribute input

Returns:

  • (Object)

    the current value of input



22
23
24
# File 'lib/moderate/label.rb', line 22

def input
  @input
end

#scoreObject (readonly)

Returns the value of attribute score

Returns:

  • (Object)

    the current value of score



22
23
24
# File 'lib/moderate/label.rb', line 22

def score
  @score
end

#subcategoryObject (readonly)

Returns the value of attribute subcategory

Returns:

  • (Object)

    the current value of subcategory



22
23
24
# File 'lib/moderate/label.rb', line 22

def subcategory
  @subcategory
end

Instance Method Details

#canonical?Boolean

True when this label belongs to the canonical taxonomy. Adapters MAY emit off-taxonomy labels (a provider category we haven’t mapped) — we don’t raise, we just let callers filter on ‘canonical?` when they want strictness.

Returns:

  • (Boolean)


55
56
57
58
59
60
61
62
# File 'lib/moderate/label.rb', line 55

def canonical?
  # `self.class::TAXONOMY` resolves the constant on the Label class regardless
  # of the Data.define block's quirky lexical scope (see the note at the top).
  subs = self.class::TAXONOMY[category]
  return false if subs.nil?

  subcategory.nil? || subs.include?(subcategory)
end

#slugObject

The full slug, OpenAI-style: “hate/threatening”, “self-harm/intent”, or just “hate” when there’s no subcategory. This is the canonical wire/storage form used by ‘Moderate::Result#categories` and persisted on `Moderate::Flag`.



48
49
50
# File 'lib/moderate/label.rb', line 48

def slug
  subcategory ? "#{category}/#{subcategory}" : category.to_s
end