defmodule Pi.Features do
@moduledoc false
@disabled_values ~w[0 false off no]
@features %{
llm: %{
env: "PI_ELIXIR_LLM",
disabled: "BEAM-initiated LLM is disabled."
},
sessions: %{
env: "PI_ELIXIR_SESSIONS",
disabled: "BEAM sessions are disabled."
},
plugins: %{
env: "PI_ELIXIR_PLUGINS",
disabled: "Project-local plugins are disabled."
},
skills: %{
env: "PI_ELIXIR_SKILLS",
disabled: "Executable Elixir skills are disabled."
}
}
defmacro gate(feature, do: block) do
quote do
if Pi.Features.enabled?(unquote(feature)) do
unquote(block)
else
{:error, Pi.Features.disabled_message(unquote(feature))}
end
end
end
def enabled?(feature) when is_atom(feature) do
feature
|> env_name()
|> env_enabled?()
end
def env_enabled?(env) when is_binary(env) do
case System.get_env(env) do
nil -> true
value -> String.downcase(String.trim(value)) not in @disabled_values
end
end
def disabled_message(feature) when is_atom(feature) do
@features
|> Map.fetch!(feature)
|> Map.fetch!(:disabled)
end
def snapshot do
Map.new(@features, fn {feature, %{env: env}} ->
{feature, %{enabled: enabled?(feature), env: env}}
end)
end
def llm?, do: enabled?(:llm)
def plugins?, do: enabled?(:plugins)
def sessions?, do: enabled?(:sessions)
def skills?, do: enabled?(:skills)
defp env_name(feature) do
@features
|> Map.fetch!(feature)
|> Map.fetch!(:env)
end
end