ALLM.ModerationResponse (allm v0.6.1)

Copy Markdown View Source

A content-moderation response — Layer A serializable data.

iex> result = ALLM.ModerationResult.new(flagged: true, categories: %{"violence" => true})
iex> resp = ALLM.ModerationResponse.new(results: [result])
iex> ALLM.ModerationResponse.flagged?(resp)
true

Cardinality

length(response.results) == length(request.input) when ALLM.ModerationRequest.multimodal?/1 is false, and == 1 when it is true. The rule is stated in full on ALLM.ModerationRequest.

No :usage

Unlike ALLM.EmbeddingResponse and ALLM.ImageResponse, this struct has no :usage field. Moderation endpoints are free and return no usage object, so there are no counters to carry and synthesizing empty ones would be noise. ALLM.moderate/3's [:allm, :moderate, :stop] telemetry metadata still carries a :usage key (always nil) so that a metrics handler written against one capability does not KeyError when pointed at another; the absence is on the struct only.

Fields

:provider is the provider atom (:openai), populated by the adapter. It is present here and not on ALLM.EmbeddingResponse because a moderation verdict is routinely persisted alongside the content it judged, and "which policy engine said this" is the field an auditor asks for six months later.

:id is the provider's own identifier for the call, and there is exactly one per HTTP round-trip. :raw carries the same caller-responsibility contract as ALLM.Response.raw: a non-JSON-encodable :raw raises at encode time.

Summary

Functions

True when any result is flagged.

Union of every result's flagged categories, deduplicated and sorted.

Build a %ModerationResponse{} from keyword opts.

Types

t()

@type t() :: %ALLM.ModerationResponse{
  id: String.t() | nil,
  metadata: map(),
  model: String.t() | nil,
  provider: atom() | nil,
  raw: term(),
  request_id: String.t() | nil,
  results: [ALLM.ModerationResult.t()]
}

Functions

flagged?(moderation_response)

@spec flagged?(t()) :: boolean()

True when any result is flagged.

This is the one-call answer to "should I stop here?" for the common case of moderating a single string.

Examples

iex> clean = ALLM.ModerationResult.new(flagged: false)
iex> flagged = ALLM.ModerationResult.new(flagged: true)
iex> ALLM.ModerationResponse.flagged?(ALLM.ModerationResponse.new(results: [clean, flagged]))
true

iex> ALLM.ModerationResponse.flagged?(ALLM.ModerationResponse.new())
false

flagged_categories(moderation_response)

@spec flagged_categories(t()) :: [String.t()]

Union of every result's flagged categories, deduplicated and sorted.

Same meaning as ALLM.ModerationResult.flagged_categories/1, one scope up: use this to report what tripped anywhere in a batch, and the per-result function to report which item tripped it.

Examples

iex> a = ALLM.ModerationResult.new(flagged: true, categories: %{"violence" => true})
iex> b = ALLM.ModerationResult.new(flagged: true, categories: %{"violence" => true, "hate" => true})
iex> ALLM.ModerationResponse.flagged_categories(ALLM.ModerationResponse.new(results: [a, b]))
["hate", "violence"]

iex> ALLM.ModerationResponse.flagged_categories(ALLM.ModerationResponse.new())
[]

new(opts \\ [])

@spec new(keyword()) :: t()

Build a %ModerationResponse{} from keyword opts.

Unknown keys raise KeyError via struct!/2.

Examples

iex> resp = ALLM.ModerationResponse.new(model: "omni-moderation-latest")
iex> resp.results
[]
iex> resp.provider
nil