host = if repo_name = System.get_env("SPACE_REPO_NAME") do "#{System.get_env("SPACE_AUTHOR_NAME")}-#{String.replace(repo_name, "_", "-")}.hf.space" else "localhost" end Application.put_env(:phoenix, :json_library, Jason) Application.put_env(:phoenix_demo, PhoenixDemo.Endpoint, url: [host: host], http: [ ip: {0, 0, 0, 0, 0, 0, 0, 0}, port: String.to_integer(System.get_env("PORT") || "4000"), transport_options: [socket_opts: [:inet6]] ], server: true, live_view: [signing_salt: :crypto.strong_rand_bytes(8) |> Base.encode16()], secret_key_base: :crypto.strong_rand_bytes(32) |> Base.encode16(), pubsub_server: PhoenixDemo.PubSub ) Mix.install([ {:plug_cowboy, "~> 2.6"}, {:jason, "~> 1.4"}, {:phoenix, "~> 1.7.0-rc.0", override: true}, {:phoenix_live_view, "~> 0.18.3"}, {:bumblebee, "~> 0.1.0"}, {:nx, "~> 0.4.1"}, {:exla, "~> 0.4.1"} ]) Application.put_env(:nx, :default_backend, EXLA.Backend) defmodule PhoenixDemo.Layouts do use Phoenix.Component def render("live.html", assigns) do ~H""" <%= @inner_content %> """ end end defmodule PhoenixDemo.ErrorView do def render(_, _), do: "error" end defmodule PhoenixDemo.SampleLive do use Phoenix.LiveView, layout: {PhoenixDemo.Layouts, :live} def mount(_params, _session, socket) do {:ok, socket |> assign(label: nil, running: false, task_ref: nil) |> allow_upload(:image, accept: :any, max_entries: 1, max_file_size: 300_000, progress: &handle_progress/3, auto_upload: true )} end def render(assigns) do ~H"""

Elixir image classification demo

Powered by Bumblebee, an Nx/Axon library for pre-trained and transformer NN models with 🤗 integration. Deployed on Hugging Face CPU Basic (2vCPU · 16 GiB RAM)

<.image_input id="image" upload={@uploads.image} height={224} width={224} />
Label: <%= if @running do %> <.spinner /> <% else %> <%= @label || "?" %> <% end %>

View the source view source on Hugging Face Hugging Face's logo

""" end defp image_input(assigns) do ~H"""
<.live_file_input upload={@upload} class="hidden" />
Drag an image file here or click to open file browser
""" end defp spinner(assigns) do ~H""" """ end def handle_progress(:image, entry, socket) do if entry.done? do socket |> consume_uploaded_entries(:image, fn meta, _ -> {:ok, File.read!(meta.path)} end) |> case do [binary] -> image = decode_as_tensor(binary) task = Task.async(fn -> Nx.Serving.batched_run(PhoenixDemo.Serving, image) end) {:noreply, assign(socket, running: true, task_ref: task.ref)} [] -> {:noreply, socket} end else {:noreply, socket} end end defp decode_as_tensor(<>) do data |> Nx.from_binary(:u8) |> Nx.reshape({height, width, 3}) end # We need phx-change and phx-submit on the form for live uploads def handle_event("noop", %{}, socket) do {:noreply, socket} end def handle_info({ref, result}, %{assigns: %{task_ref: ref}} = socket) do Process.demonitor(ref, [:flush]) %{predictions: [%{label: label}]} = result {:noreply, assign(socket, label: label, running: false)} end end defmodule PhoenixDemo.Router do use Phoenix.Router import Phoenix.LiveView.Router pipeline :browser do plug(:accepts, ["html"]) end scope "/", PhoenixDemo do pipe_through(:browser) live("/", SampleLive, :index) end end defmodule PhoenixDemo.Endpoint do use Phoenix.Endpoint, otp_app: :phoenix_demo socket("/live", Phoenix.LiveView.Socket) plug(PhoenixDemo.Router) end # Application startup {:ok, model_info} = Bumblebee.load_model({:hf, "microsoft/resnet-50"}) {:ok, featurizer} = Bumblebee.load_featurizer({:hf, "microsoft/resnet-50"}) serving = Bumblebee.Vision.image_classification(model_info, featurizer, top_k: 1, compile: [batch_size: 10], defn_options: [compiler: EXLA] ) # Dry run for copying cached mix install from builder to runner if System.get_env("EXS_DRY_RUN") == "true" do System.halt(0) else {:ok, _} = Supervisor.start_link( [ {Phoenix.PubSub, name: PhoenixDemo.PubSub}, PhoenixDemo.Endpoint, {Nx.Serving, serving: serving, name: PhoenixDemo.Serving, batch_timeout: 100} ], strategy: :one_for_one ) Process.sleep(:infinity) end