Skip to main content

lib/jido/chat/content/image.ex

defmodule Jido.Chat.Content.Image do
  @moduledoc """
  Image content block for messages.

  Represents an image attachment in a message. Supports both URL-based
  and base64-encoded images.

  ## Fields

  - `url` - URL to the image (optional if data is provided)
  - `data` - Base64-encoded image data (optional if url is provided)
  - `media_type` - MIME type (e.g., "image/png", "image/jpeg")
  - `alt_text` - Alternative text description
  - `width` - Image width in pixels (optional)
  - `height` - Image height in pixels (optional)

  ## Examples

      Image.new("https://example.com/photo.jpg")
      Image.new("https://example.com/photo.jpg", media_type: "image/jpeg", alt_text: "A photo")
      Image.from_base64(base64_data, "image/png")
  """

  @schema Zoi.struct(
            __MODULE__,
            %{
              type: Zoi.literal(:image) |> Zoi.default(:image),
              url: Zoi.string() |> Zoi.nullish(),
              data: Zoi.string() |> Zoi.nullish(),
              media_type: Zoi.string() |> Zoi.nullish(),
              alt_text: Zoi.string() |> Zoi.nullish(),
              width: Zoi.integer() |> Zoi.nullish(),
              height: Zoi.integer() |> Zoi.nullish()
            },
            coerce: true
          )

  @type t :: unquote(Zoi.type_spec(@schema))

  @enforce_keys Zoi.Struct.enforce_keys(@schema)
  defstruct Zoi.Struct.struct_fields(@schema)

  @doc "Returns the Zoi schema for Image content"
  def schema, do: @schema

  @doc """
  Creates a new image content block from a URL.

  ## Options

  - `:media_type` - MIME type of the image
  - `:alt_text` - Alternative text description
  - `:width` - Image width in pixels
  - `:height` - Image height in pixels
  """
  def new(url, opts \\ []) when is_binary(url) do
    %__MODULE__{
      url: url,
      media_type: Keyword.get(opts, :media_type),
      alt_text: Keyword.get(opts, :alt_text),
      width: Keyword.get(opts, :width),
      height: Keyword.get(opts, :height)
    }
  end

  @doc """
  Creates a new image content block from base64-encoded data.

  ## Parameters

  - `data` - Base64-encoded image data
  - `media_type` - MIME type (required for base64 images)
  - `opts` - Additional options (alt_text, width, height)
  """
  def from_base64(data, media_type, opts \\ []) when is_binary(data) and is_binary(media_type) do
    %__MODULE__{
      data: data,
      media_type: media_type,
      alt_text: Keyword.get(opts, :alt_text),
      width: Keyword.get(opts, :width),
      height: Keyword.get(opts, :height)
    }
  end
end