Skip to main content

lib/jido/chat/content/file.ex

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

  Represents a generic file attachment (documents, audio, video, etc.).

  ## Fields

  - `url` - URL to the file (optional if data is provided)
  - `data` - Base64-encoded file data (optional if url is provided)
  - `media_type` - MIME type (e.g., "application/pdf", "audio/mp3")
  - `filename` - Original filename
  - `size` - File size in bytes (optional)

  ## Examples

      File.new("https://example.com/doc.pdf", "document.pdf")
      File.new("https://example.com/doc.pdf", "document.pdf", media_type: "application/pdf")
  """

  @schema Zoi.struct(
            __MODULE__,
            %{
              type: Zoi.literal(:file) |> Zoi.default(:file),
              url: Zoi.string() |> Zoi.nullish(),
              data: Zoi.string() |> Zoi.nullish(),
              media_type: Zoi.string() |> Zoi.nullish(),
              filename: Zoi.string() |> Zoi.nullish(),
              size: 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 File content"
  def schema, do: @schema

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

  ## Options

  - `:media_type` - MIME type of the file
  - `:size` - File size in bytes
  """
  def new(url, filename, opts \\ []) when is_binary(url) and is_binary(filename) do
    %__MODULE__{
      url: url,
      filename: filename,
      media_type: Keyword.get(opts, :media_type),
      size: Keyword.get(opts, :size)
    }
  end

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

  ## Parameters

  - `data` - Base64-encoded file data
  - `filename` - Original filename
  - `media_type` - MIME type
  - `opts` - Additional options (size)
  """
  def from_base64(data, filename, media_type, opts \\ [])
      when is_binary(data) and is_binary(filename) and is_binary(media_type) do
    %__MODULE__{
      data: data,
      filename: filename,
      media_type: media_type,
      size: Keyword.get(opts, :size)
    }
  end
end