PhoenixKit.Modules.AI.Translations (phoenix_kit v1.7.131)

Copy Markdown View Source

Core orchestration for AI-driven translation — the shared layer every feature module enqueues against instead of re-implementing its own context + worker.

Pairs with:

What a consumer does

  1. Implement a Translatable adapter and register it via ai_translatables/0 on its PhoenixKit.Module.
  2. From a form LV: subscribe/1, then on a button click call enqueue/1 (or enqueue_all_missing/2), and react to the {:ai_translation, event, payload} messages.

Everything else — the AI call, parsing, persistence (via the adapter), retry policy, broadcasts, and the unified audit log — is shared.

Status messages

The worker broadcasts {:ai_translation, event, payload} where event is :translation_started | :translation_completed | :translation_failed. payload always has :resource_type, :resource_uuid, :source_lang, :target_lang. :translation_completed adds :fields (the translated %{field => text} map, possibly empty); :translation_failed adds :reason.

Summary

Functions

Is AI translation usable right now? PhoenixKitAI loaded + enabled + at least one enabled endpoint configured. Hosts gate the UI on this.

Default endpoint UUID: the ai_translation_endpoint_uuid setting, else the first enabled endpoint, else nil.

Is the shared default translation prompt already provisioned?

Default prompt UUID: the ai_translation_prompt_uuid setting, else the shared phoenixkit-translate-content prompt, else nil.

Enqueue one translation job for a single (resource, target_lang).

Enqueue one job per missing target language. base_params is enqueue_params minus :target_lang. Returns {:ok, %{enqueued, conflicts, errors, in_flight}}in_flight is the langs a host should mark spinning (newly enqueued + conflicts; never the errored ones, since no broadcast will arrive to clear them).

Idempotently provision the shared translation prompt. Returns {:ok, prompt} (existing or freshly created) or {:error, reason}. {:error, :ai_not_installed} when the plugin is unavailable.

Configured AI endpoints as [{uuid, name}]. Empty when unavailable.

Configured AI prompts as [{uuid, name}]. Empty when unavailable.

Given the enabled base language codes, the primary code, and the langs that already have a translation, return the still-missing base codes (primary excluded — it's the source, never a target).

Subscribe to the global translation topic.

Subscribe to a single resource's translation topic.

The global AI-translation status topic.

Per-resource AI-translation status topic.

Types

enqueue_params()

@type enqueue_params() :: %{
  :resource_type => String.t(),
  :resource_uuid => String.t(),
  :endpoint_uuid => String.t(),
  :prompt_uuid => String.t(),
  :source_lang => String.t(),
  :target_lang => String.t(),
  optional(:actor_uuid) => String.t() | nil
}

Functions

available?()

@spec available?() :: boolean()

Is AI translation usable right now? PhoenixKitAI loaded + enabled + at least one enabled endpoint configured. Hosts gate the UI on this.

default_endpoint_uuid()

@spec default_endpoint_uuid() :: String.t() | nil

Default endpoint UUID: the ai_translation_endpoint_uuid setting, else the first enabled endpoint, else nil.

default_prompt_exists?()

@spec default_prompt_exists?() :: boolean()

Is the shared default translation prompt already provisioned?

default_prompt_uuid()

@spec default_prompt_uuid() :: String.t() | nil

Default prompt UUID: the ai_translation_prompt_uuid setting, else the shared phoenixkit-translate-content prompt, else nil.

enqueue(params)

@spec enqueue(map()) :: {:ok, %{conflict?: boolean()}} | {:error, term()}

Enqueue one translation job for a single (resource, target_lang).

Returns {:ok, %{conflict?: boolean}} (conflict?: true when an identical job is already in flight) or {:error, reason} on a malformed input map.

enqueue_all_missing(base_params, missing_langs)

@spec enqueue_all_missing(map(), [String.t()]) ::
  {:ok,
   %{
     enqueued: non_neg_integer(),
     conflicts: non_neg_integer(),
     errors: [{String.t(), term()}],
     in_flight: [String.t()]
   }}
  | {:error, term()}

Enqueue one job per missing target language. base_params is enqueue_params minus :target_lang. Returns {:ok, %{enqueued, conflicts, errors, in_flight}}in_flight is the langs a host should mark spinning (newly enqueued + conflicts; never the errored ones, since no broadcast will arrive to clear them).

ensure_default_prompt()

@spec ensure_default_prompt() :: {:ok, struct()} | {:error, term()}

Idempotently provision the shared translation prompt. Returns {:ok, prompt} (existing or freshly created) or {:error, reason}. {:error, :ai_not_installed} when the plugin is unavailable.

list_endpoints()

@spec list_endpoints() :: [{String.t(), String.t()}]

Configured AI endpoints as [{uuid, name}]. Empty when unavailable.

list_prompts()

@spec list_prompts() :: [{String.t(), String.t()}]

Configured AI prompts as [{uuid, name}]. Empty when unavailable.

missing_languages(enabled_codes, primary_code, existing_langs)

@spec missing_languages([String.t()], String.t(), [String.t()]) :: [String.t()]

Given the enabled base language codes, the primary code, and the langs that already have a translation, return the still-missing base codes (primary excluded — it's the source, never a target).

subscribe()

@spec subscribe() :: :ok | {:error, term()}

Subscribe to the global translation topic.

subscribe(resource_type, resource_uuid)

@spec subscribe(String.t(), String.t()) :: :ok | {:error, term()}

Subscribe to a single resource's translation topic.

topic()

@spec topic() :: String.t()

The global AI-translation status topic.

topic(resource_type, resource_uuid)

@spec topic(String.t(), String.t()) :: String.t()

Per-resource AI-translation status topic.