PhoenixKitCatalogue.Workers.TranslationSweepWorker (PhoenixKitCatalogue v0.44.2)

Copy Markdown View Source

Opt-in, self-rescheduling Oban worker that tops up catalogue AI translations: on every tick it enqueues one PhoenixKitAI.TranslateWorker job per (resource, target language) pair currently :missing or :stale:unknown is never picked up automatically (design source doc §4.1: an operator decides that pair's fate explicitly).

Settings (PhoenixKitCatalogue.Web.Settings, Task 4):

  • catalogue_translation_sweep_enabled — off by default; a disabled tick still reschedules its successor, it just does no work.
  • catalogue_translation_sweep_interval_minutes — gap to the next tick.
  • catalogue_translation_sweep_langs — target languages to consider.
  • catalogue_translation_sweep_max_per_run — enqueue cap for one tick, counted in jobs (across every resource type) not resources.

Self-rescheduling

The very first action of perform/1 is scheduling the NEXT tick — before any of the tick's own work runs — so a crashed or slow tick never breaks the chain. Uniqueness (period: :infinity, states: [:available, :scheduled]) keeps at most one pending tick in the queue at a time; the explicit state list (rather than Oban's default, which references :suspended) sidesteps the 22P02 landmine PhoenixKitAI.Translations and PhoenixKitCatalogue.Workers.PdfExtractor already document for hosts whose oban_job_state enum predates that value. max_attempts: 1 — retrying a missed tick is pointless, the next one is already scheduled.

ensure_scheduled/0 seeds the very first tick at boot (see PhoenixKitCatalogue.children/0) and is safe to call again any time (e.g. a future settings save) — the same uniqueness collapses repeat calls to the one pending job.

Summary

Functions

Resolves the AI endpoint and per-resource-type prompt uuids, the same way every sweep tick does: Translations.available?/0 ALONE isn't enough (it doesn't verify the configured default endpoint still exists/is enabled) — the double check the design source calls for (§4.3 step 2).

Ensures exactly one sweep tick is available/scheduled. Called from the boot-time task registered in children/0; concurrent callers (boot, a future settings save, the tick itself) all collapse onto the same unique row.

Boot-time gate for ensure_scheduled/0: seeds the chain only if the sweep is already enabled, so a host that has never turned it on gets no ticking job row. Web.Settings.update_sweep_enabled/1 calls ensure_scheduled/0 unconditionally when it flips the setting to true, so the chain always starts exactly when a host first opts in — at boot (already enabled from a previous save) or from that save itself.

The ai_translatables/0 resource_type string for a TranslationStatus.list/2 type atom.

Functions

endpoint_and_prompts()

@spec endpoint_and_prompts() ::
  {:ok, String.t(), %{required(String.t()) => String.t()}} | :unavailable

Resolves the AI endpoint and per-resource-type prompt uuids, the same way every sweep tick does: Translations.available?/0 ALONE isn't enough (it doesn't verify the configured default endpoint still exists/is enabled) — the double check the design source calls for (§4.3 step 2).

Public so Web.TranslationsLive's manual "Translate" / bulk actions share this exact resolution path with the automatic sweep tick, rather than re-deriving which prompt belongs to which resource type a second time.

ensure_scheduled()

@spec ensure_scheduled() :: {:ok, Oban.Job.t()} | {:error, term()}

Ensures exactly one sweep tick is available/scheduled. Called from the boot-time task registered in children/0; concurrent callers (boot, a future settings save, the tick itself) all collapse onto the same unique row.

ensure_scheduled_if_enabled()

@spec ensure_scheduled_if_enabled() ::
  {:ok, Oban.Job.t()} | {:error, term()} | :skipped

Boot-time gate for ensure_scheduled/0: seeds the chain only if the sweep is already enabled, so a host that has never turned it on gets no ticking job row. Web.Settings.update_sweep_enabled/1 calls ensure_scheduled/0 unconditionally when it flips the setting to true, so the chain always starts exactly when a host first opts in — at boot (already enabled from a previous save) or from that save itself.

resource_type_for(type)

@spec resource_type_for(:item | :category | :set_label | :set_value) :: String.t()

The ai_translatables/0 resource_type string for a TranslationStatus.list/2 type atom.