WebServer
Mix.install(
[
{:plug, "~> 1.17"},
{:kino, "~> 0.15.3"},
{:bandit, "~> 1.6"},
{:req, "~> 0.5.10"},
{:kino_bumblebee, "~> 0.5.0"},
{:exla, ">= 0.0.0"}
],
config: [nx: [default_backend: EXLA.Backend]]
)
1. Writting a simple webserver
defmodule MyWeb do
use Plug.Builder
plug :fetch_query_params
plug :render
def render(conn, _opts) do
text = conn.params["text"]
output = Nx.Serving.batched_run(:my_ai, text)
[%{label: label, score: _score} | _tail] = output.predictions
Plug.Conn.send_resp(conn, 200, "Analysis: #{label}")
end
end
################################### ML Model
{:ok, model_info} =
Bumblebee.load_model({:hf, "finiteautomata/bertweet-base-emotion-analysis"})
{:ok, tokenizer} = Bumblebee.load_tokenizer({:hf, "vinai/bertweet-base"})
serving =
Bumblebee.Text.text_classification(model_info, tokenizer,
compile: [batch_size: 1, sequence_length: 100],
defn_options: [compiler: EXLA]
)
Kino.start_child!({Nx.Serving, serving: serving, name: :my_ai})
Kino.start_child!({Bandit, plug: MyWeb, port: 6789})
22:42:40.961 [info] Running MyWeb with Bandit 1.6.11 at 0.0.0.0:6789 (http)
#PID<0.347.0>
2. Kino start child
Starts a process under the Kino supervisor.
The process is automatically terminated when the current process terminates or the current cell reevaluates.
After run this, Kino knows there is a supervision tree to render.
#Kino.start_child!({Bandit, plug: MyWeb, port: 6789})
nil
3. Test our webserver using Req
Req.get!("http://localhost:6789/", params: [text: "Coding in elixir is awesome!"]) # -> 200
%Req.Response{
status: 200,
headers: %{
"cache-control" => ["max-age=0, private, must-revalidate"],
"date" => ["Mon, 14 Apr 2025 04:42:56 GMT"],
"vary" => ["accept-encoding"]
},
body: "Analysis: joy",
trailers: %{},
private: %{}
}
4. AI Spech to Text
################################### ML Model
{:ok, model_info} =
Bumblebee.load_model({:hf, "finiteautomata/bertweet-base-emotion-analysis"})
{:ok, tokenizer} = Bumblebee.load_tokenizer({:hf, "vinai/bertweet-base"})
serving =
Bumblebee.Text.text_classification(model_info, tokenizer,
compile: [batch_size: 1, sequence_length: 100],
defn_options: [compiler: EXLA]
)
Kino.start_child!({Nx.Serving, serving: serving, name: :my_ai})
#PID<0.4428.0>
text_input = Kino.Input.textarea("Text", default: "Oh wow, I didn't know that!")
form = Kino.Control.form([text: text_input], submit: "Run")
frame = Kino.Frame.new()
Kino.listen(form, fn %{data: %{text: text}} ->
Kino.Frame.render(frame, Kino.Text.new("Running..."))
output = Nx.Serving.batched_run(:my_ai, text)
output.predictions
|> Enum.map(&{&1.label, &1.score})
|> Kino.Bumblebee.ScoredList.new()
|> then(&Kino.Frame.render(frame, &1))
end)
Kino.Layout.grid([form, frame], boxed: true, gap: 16)