livebook/lib/livebook/runtime/definitions.ex
2025-03-07 11:51:09 +01:00

517 lines
13 KiB
Elixir

defmodule Livebook.Runtime.Definitions do
@kino_requirement "~> 0.15.0"
def kino_requirement do
@kino_requirement
end
kino = %{
name: "kino",
dependency: %{dep: {:kino, @kino_requirement}, config: []}
}
kino_vega_lite = %{
name: "kino_vega_lite",
dependency: %{dep: {:kino_vega_lite, "~> 0.1.11"}, config: []}
}
kino_db = %{
name: "kino_db",
dependency: %{dep: {:kino_db, "~> 0.3.0"}, config: []}
}
exqlite = %{
name: "exqlite",
dependency: %{dep: {:exqlite, "~> 0.23.0"}, config: []}
}
kino_maplibre = %{
name: "kino_maplibre",
dependency: %{dep: {:kino_maplibre, "~> 0.1.12"}, config: []}
}
kino_slack = %{
name: "kino_slack",
dependency: %{dep: {:kino_slack, "~> 0.1.1"}, config: []}
}
kino_bumblebee = %{
name: "kino_bumblebee",
dependency: %{dep: {:kino_bumblebee, "~> 0.5.0"}, config: []}
}
exla = %{
name: "exla",
dependency: %{dep: {:exla, ">= 0.0.0"}, config: [nx: [default_backend: EXLA.Backend]]}
}
torchx = %{
name: "torchx",
dependency: %{dep: {:torchx, ">= 0.0.0"}, config: [nx: [default_backend: Torchx.Backend]]}
}
kino_explorer = %{
name: "kino_explorer",
dependency: %{dep: {:kino_explorer, "~> 0.1.20"}, config: []}
}
kino_flame = %{
name: "kino_flame",
dependency: %{dep: {:kino_flame, "~> 0.1.5"}, config: []}
}
flame_k8s_backend = %{
name: "flame_k8s_backend",
dependency: %{dep: {:flame_k8s_backend, "~> 0.5"}, config: []}
}
explorer = %{
name: "explorer",
dependency: %{dep: {:explorer, "~> 0.10.0"}, config: []}
}
stb_image = %{
name: "stb_image",
dependency: %{dep: {:stb_image, "~> 0.6.9"}, config: []}
}
xlsx_reader = %{
name: "xlsx_reader",
dependency: %{dep: {:xlsx_reader, "~> 0.8.5"}, config: []}
}
yaml_elixir = %{
name: "yaml_elixir",
dependency: %{dep: {:yaml_elixir, "~> 2.0"}, config: []}
}
windows? = match?({:win32, _}, :os.type())
nx_backend_package = if(windows?, do: torchx, else: exla)
@smart_cell_definitions [
%{
kind: "Elixir.KinoDB.ConnectionCell",
name: "Database connection",
requirement_presets: [
%{
name: "Amazon Athena",
packages: [
kino_db,
%{
name: "req_athena",
dependency: %{dep: {:req_athena, ">= 0.0.0"}, config: []}
},
explorer
]
},
%{
name: "Clickhouse",
packages: [
kino_db,
%{
name: "req_ch",
dependency: %{dep: {:req_ch, ">= 0.0.0"}, config: []}
},
explorer
]
},
%{
name: "DuckDB",
packages: [
kino_db,
kino_explorer,
%{
name: "adbc",
dependency: %{dep: {:adbc, ">= 0.0.0"}, config: [adbc: [drivers: [:duckdb]]]}
}
]
},
%{
name: "Google BigQuery",
packages: [
kino_db,
kino_explorer,
%{
name: "adbc",
dependency: %{dep: {:adbc, ">= 0.0.0"}, config: [adbc: [drivers: [:bigquery]]]}
}
]
},
%{
name: "MySQL",
packages: [
kino_db,
%{name: "myxql", dependency: %{dep: {:myxql, ">= 0.0.0"}, config: []}}
]
},
%{
name: "PostgreSQL",
packages: [
kino_db,
%{name: "postgrex", dependency: %{dep: {:postgrex, ">= 0.0.0"}, config: []}}
]
},
%{
name: "Snowflake",
packages: [
kino_db,
kino_explorer,
%{
name: "adbc",
dependency: %{dep: {:adbc, ">= 0.0.0"}, config: [adbc: [drivers: [:snowflake]]]}
}
]
},
%{
name: "SQLite",
packages: [kino_db, exqlite]
},
%{
name: "SQLServer",
packages: [
kino_db,
%{name: "tds", dependency: %{dep: {:tds, ">= 0.0.0"}, config: []}}
]
}
]
},
%{
kind: "Elixir.KinoDB.SQLCell",
name: "SQL query",
requirement_presets: [
%{
name: "Default",
packages: [kino_db]
}
]
},
%{
kind: "Elixir.KinoVegaLite.ChartCell",
name: "Chart",
requirement_presets: [
%{
name: "Default",
packages: [kino_vega_lite]
}
]
},
%{
kind: "Elixir.KinoMapLibre.MapCell",
name: "Map",
requirement_presets: [
%{
name: "Default",
packages: [kino_maplibre]
}
]
},
%{
kind: "Elixir.KinoSlack.MessageCell",
name: "Slack message",
requirement_presets: [
%{
name: "Default",
packages: [kino_slack]
}
]
},
%{
kind: "Elixir.KinoBumblebee.TaskCell",
name: "Neural Network task",
requirement_presets: [
%{
name: "Default",
packages: [kino_bumblebee, nx_backend_package]
}
]
},
%{
kind: "Elixir.KinoExplorer.DataTransformCell",
name: "Data transform",
requirement_presets: [
%{
name: "Default",
packages: [kino_explorer]
}
]
},
%{
kind: "Elixir.Kino.RemoteExecutionCell",
name: "Remote execution",
requirement_presets: [
%{
name: "Default",
packages: [kino]
}
]
},
%{
kind: "Elixir.KinoFLAME.RunnerCell",
name: "FLAME runner",
requirement_presets: [
%{
name: "Fly",
packages: [kino_flame]
},
%{
name: "Kubernetes",
packages: [kino_flame, flame_k8s_backend, yaml_elixir]
}
]
}
]
@snippet_definitions [
# Examples
%{
type: :example,
name: "Form",
icon: "bill-line",
variants: [
%{
name: "Default",
source: """
form =
Kino.Control.form(
[
name: Kino.Input.text("Name")
],
submit: "Submit"
)
Kino.listen(form, fn event ->
IO.inspect(event)
end)
form\
""",
packages: [kino]
}
]
},
# File actions
%{
type: :file_action,
file_types: :any,
description: "Read file content",
source: """
content =
Kino.FS.file_path("{{NAME}}")
|> File.read!()\
""",
packages: [kino]
},
%{
type: :file_action,
file_types: ["application/json"],
description: "Parse JSON content",
source: """
data =
Kino.FS.file_path("{{NAME}}")
|> File.read!()
|> JSON.decode!()
Kino.Tree.new(data)\
""",
packages: [kino]
},
%{
type: :file_action,
file_types: ["text/csv"],
description: "Create a dataframe",
source: """
df =
Kino.FS.file_path("{{NAME}}")
|> Explorer.DataFrame.from_csv!()\
""",
packages: [kino, kino_explorer]
},
%{
type: :file_action,
file_types: [".parquet"],
description: "Create a dataframe",
source: """
df =
Kino.FS.file_spec("{{NAME}}")
|> Explorer.DataFrame.from_parquet!(lazy: true)\
""",
packages: [kino, kino_explorer]
},
%{
type: :file_action,
file_types: ["image/*"],
description: "Classify image",
source: """
# To explore more models, see "+ Smart" > "Neural Network task"
{:ok, model_info} = Bumblebee.load_model({:hf, "microsoft/resnet-50"})
{:ok, featurizer} = Bumblebee.load_featurizer({:hf, "microsoft/resnet-50"})
#{if windows? do
"""
serving = Bumblebee.Vision.image_classification(model_info, featurizer)\
"""
else
"""
serving =
Bumblebee.Vision.image_classification(model_info, featurizer,
compile: [batch_size: 1],
defn_options: [compiler: EXLA]
)\
"""
end}
image = Kino.FS.file_path("{{NAME}}") |> StbImage.read_file!()
output = Nx.Serving.run(serving, image)\
""",
packages: [kino_bumblebee, nx_backend_package, stb_image]
},
%{
type: :file_action,
file_types: ["image/*"],
description: "Describe image",
source: """
# To explore more models, see "+ Smart" > "Neural Network task"
repo = {:hf, "Salesforce/blip-image-captioning-base"}
{:ok, model_info} = Bumblebee.load_model(repo)
{:ok, featurizer} = Bumblebee.load_featurizer(repo)
{:ok, tokenizer} = Bumblebee.load_tokenizer(repo)
{:ok, generation_config} = Bumblebee.load_generation_config(repo)
generation_config = Bumblebee.configure(generation_config, max_new_tokens: 100)
#{if windows? do
"""
serving = Bumblebee.Vision.image_to_text(model_info, featurizer, tokenizer, generation_config)\
"""
else
"""
serving =
Bumblebee.Vision.image_to_text(model_info, featurizer, tokenizer, generation_config,
compile: [batch_size: 1],
defn_options: [compiler: EXLA]
)\
"""
end}
image = Kino.FS.file_path("{{NAME}}") |> StbImage.read_file!()
Nx.Serving.run(serving, image)\
""",
packages: [kino_bumblebee, nx_backend_package, stb_image]
},
%{
type: :file_action,
file_types: ["audio/*"],
description: "Transcribe speech",
source:
"""
# To explore more models, see "+ Smart" > "Neural Network task"
{:ok, model_info} = Bumblebee.load_model({:hf, "openai/whisper-tiny"})
{:ok, featurizer} = Bumblebee.load_featurizer({:hf, "openai/whisper-tiny"})
{:ok, tokenizer} = Bumblebee.load_tokenizer({:hf, "openai/whisper-tiny"})
{:ok, generation_config} = Bumblebee.load_generation_config({:hf, "openai/whisper-tiny"})
generation_config = Bumblebee.configure(generation_config, max_new_tokens: 100)
""" <>
if windows? do
"""
serving =
Bumblebee.Audio.speech_to_text_whisper(model_info, featurizer, tokenizer, generation_config,
chunk_num_seconds: 30,
timestamps: :segments,
stream: true,
compile: [batch_size: 4]
)
"""
else
"""
serving =
Bumblebee.Audio.speech_to_text_whisper(model_info, featurizer, tokenizer, generation_config,
chunk_num_seconds: 30,
timestamps: :segments,
stream: true,
compile: [batch_size: 4],
defn_options: [compiler: EXLA]
)
"""
end <>
~S"""
path = Kino.FS.file_path("{{NAME}}")
Kino.render(Kino.Text.new("(Start of transcription)", chunk: true))
for chunk <- Nx.Serving.run(serving, {:file, path}) do
[start_mark, end_mark] =
for seconds <- [chunk.start_timestamp_seconds, chunk.end_timestamp_seconds] do
seconds
|> round()
|> Time.from_seconds_after_midnight()
|> Time.to_string()
end
text = "\n#{start_mark}-#{end_mark}: #{chunk.text}"
Kino.render(Kino.Text.new(text, chunk: true))
end
Kino.render(Kino.Text.new("\n(End of transcription)", chunk: true))
:ok
""",
packages: [kino_bumblebee, nx_backend_package]
},
%{
type: :file_action,
file_types: [".db", ".sqlite"],
description: "Describe SQLite database",
source: """
database_path = Kino.FS.file_path("{{NAME}}")
{:ok, conn} = Kino.start_child({Exqlite, database: database_path})
Exqlite.query!(conn, "PRAGMA table_list", [])\
""",
packages: [kino_db, exqlite]
},
%{
type: :file_action,
file_types: [".xlsx", ".xlsm"],
description: "Read sheets",
source: """
xlsx_file = Kino.FS.file_path("{{NAME}}")
{:ok, package} = XlsxReader.open(xlsx_file)
tabs =
for sheet <- XlsxReader.sheet_names(package) do
maps =
case XlsxReader.sheet(package, sheet) do
{:ok, []} ->
[]
{:ok, [header | rows]} ->
Enum.map(rows, fn row -> header |> Enum.zip(row) |> Map.new() end)
end
{sheet, Kino.DataTable.new(maps)}
end
Kino.Layout.tabs(tabs)
""",
packages: [kino, xlsx_reader]
}
]
def smart_cell_definitions(), do: @smart_cell_definitions
def snippet_definitions(), do: @snippet_definitions
def pythonx_dependency() do
%{dep: {:pythonx, "~> 0.4.2"}, config: []}
end
def kino_pythonx_dependency() do
%{dep: {:kino_pythonx, github: "livebook-dev/kino_pythonx"}, config: []}
end
def pythonx_requirement(), do: "~> 0.4.0"
end