Skip to main content

lib/apple_intents/jido/context.ex

defmodule AppleIntents.Jido.Context do
  @moduledoc """
  Converts `AppleIntents.Context` into a Jido-compatible execution context map.
  """

  alias AppleIntents.Context
  alias AppleIntents.Payload

  @spec to_jido(Context.t(), keyword()) :: map()
  def to_jido(%Context{} = context, opts \\ []) do
    %{
      intent_id: context.payload.intent_id,
      intent_name: context.payload.intent_name,
      request_id: context.payload.request_id,
      user_id: context.payload.user_id,
      bundle_id: context.payload.bundle_id,
      dry_run: context.dry_run,
      metadata: Map.merge(context.metadata, intent_metadata(context.payload)),
      apple_intents: %{
        payload: context.payload,
        config: context.config
      },
      source: "apple_intents_jido",
      jido: Keyword.get(opts, :jido),
      orchestrator: orchestrator_name(opts, context)
    }
    |> drop_nil_values()
  end

  defp intent_metadata(%Payload{} = payload) do
    %{
      "parameters" => payload.parameters,
      "raw" => payload.raw,
      "issued_at" => payload.issued_at,
      "expires_at" => payload.expires_at
    }
    |> drop_nil_values()
  end

  defp orchestrator_name(opts, context) do
    orchestrator =
      opts[:orchestrator] || context.metadata[:orchestrator] ||
        Application.get_env(:apple_intents_jido, :orchestrator)

    case orchestrator do
      value when is_atom(value) -> Atom.to_string(value)
      value -> value
    end
  end

  defp drop_nil_values(map) when is_map(map) do
    map
    |> Enum.map(fn
      {key, value} when is_map(value) -> {key, drop_nil_values(value)}
      pair -> pair
    end)
    |> Enum.reject(fn {_key, value} -> is_nil(value) end)
    |> Map.new()
  end
end