Skip to main content

lib/buble.ex

defmodule Buble do
  @moduledoc """
  Server-side Elixir SDK for the Buble public API.

  [Buble](https://buble.ai/) provides AI chat, image generation, video
  generation, file uploads, and preconfigured AI app workflows. The
  [Buble API documentation](https://buble.ai/docs) describes authentication,
  model discovery, file uploads, asynchronous generation tasks, app workflows,
  polling, and chat model endpoints.

  ## Quick start

      client = Buble.Client.new!(api_key: "sk_...")

      {:ok, task} =
        Buble.Generations.create(client, %{
          model: "nano-banana",
          prompt: "A cinematic studio product photo"
        })

      {:ok, result} = Buble.Generations.wait(client, task["data"]["id"])

  The SDK also reads `BUBLE_API_KEY` and `BUBLE_BASE_URL` from the environment
  when omitted.
  """

  alias Buble.Error

  @doc """
  Creates a `Buble.Client`.
  """
  @spec client(keyword()) :: {:ok, Buble.Client.t()} | {:error, Error.t()}
  def client(opts \\ []), do: Buble.Client.new(opts)

  @doc """
  Creates a `Buble.Client` or raises `Buble.Error`.
  """
  @spec client!(keyword()) :: Buble.Client.t()
  def client!(opts \\ []), do: Buble.Client.new!(opts)

  @doc false
  def unwrap!({:ok, value}), do: value
  def unwrap!({:error, %Error{} = error}), do: raise(error)

  @doc false
  def normalize_params(params) when is_map(params) do
    Map.new(params, fn {key, value} -> {to_string(key), value} end)
  end

  def normalize_params(params) when is_list(params) do
    params
    |> Enum.into(%{})
    |> normalize_params()
  end

  @doc false
  def compact_params(params) do
    params
    |> normalize_params()
    |> Enum.reject(fn {_key, value} -> is_nil(value) or value == "" or value == [] end)
    |> Map.new()
  end
end