Packages

Core chat SDK types and adapter contracts for the Jido ecosystem

Current section

Files

Jump to
jido_chat lib jido chat capability_matrix.ex
Raw

lib/jido/chat/capability_matrix.ex

defmodule Jido.Chat.CapabilityMatrix do
@moduledoc """
Adapter capability declaration matrix (`:native | :fallback | :unsupported`).
"""
alias Jido.Chat.Wire
@statuses [:native, :fallback, :unsupported]
@schema Zoi.struct(
__MODULE__,
%{
adapter_name: Zoi.atom() |> Zoi.nullish(),
capabilities: Zoi.map() |> Zoi.default(%{})
},
coerce: true
)
@type capability_status :: :native | :fallback | :unsupported
@type t :: unquote(Zoi.type_spec(@schema))
@enforce_keys Zoi.Struct.enforce_keys(@schema)
defstruct Zoi.Struct.struct_fields(@schema)
@doc "Returns the Zoi schema for CapabilityMatrix."
def schema, do: @schema
@doc "Creates normalized capability matrix payload."
@spec new(map()) :: t()
def new(attrs) when is_map(attrs) do
capabilities =
attrs[:capabilities] || attrs["capabilities"] || attrs[:matrix] || attrs["matrix"] || %{}
attrs
|> Map.put(:capabilities, normalize_capabilities(capabilities))
|> Map.delete("capabilities")
|> Map.delete(:matrix)
|> Map.delete("matrix")
|> then(&Jido.Chat.Schema.parse!(__MODULE__, @schema, &1))
end
@doc "Returns capability matrix map."
@spec as_map(t()) :: %{optional(atom()) => capability_status()}
def as_map(%__MODULE__{} = matrix), do: matrix.capabilities
@doc "Returns declared support status for capability."
@spec status(t(), atom()) :: capability_status()
def status(%__MODULE__{} = matrix, capability) when is_atom(capability) do
Map.get(matrix.capabilities, capability, :unsupported)
end
@doc "Serializes capability matrix into plain map with type marker."
@spec to_map(t()) :: map()
def to_map(%__MODULE__{} = matrix) do
matrix
|> Map.from_struct()
|> Wire.to_plain()
|> Map.put("__type__", "capability_matrix")
end
@doc "Builds capability matrix from serialized map."
@spec from_map(map()) :: t()
def from_map(map) when is_map(map), do: new(map)
defp normalize_capabilities(capabilities) when is_map(capabilities) do
Enum.reduce(capabilities, %{}, fn {capability, status}, acc ->
case normalize_capability_key(capability) do
{:ok, capability_key} -> Map.put(acc, capability_key, normalize_status(status))
:error -> acc
end
end)
end
defp normalize_capabilities(capabilities) when is_list(capabilities) do
Enum.reduce(capabilities, %{}, fn capability, acc ->
case normalize_capability_key(capability) do
{:ok, capability_key} -> Map.put(acc, capability_key, :native)
:error -> acc
end
end)
end
defp normalize_capabilities(_), do: %{}
defp normalize_capability_key(capability) when is_atom(capability), do: {:ok, capability}
defp normalize_capability_key(capability) when is_binary(capability) do
capability = String.trim(capability)
case capability do
"" ->
:error
_ ->
try do
{:ok, String.to_existing_atom(capability)}
rescue
ArgumentError -> :error
end
end
end
defp normalize_capability_key(_capability), do: :error
defp normalize_status(status) when status in @statuses, do: status
defp normalize_status(status) when is_binary(status) do
status = String.trim(status)
case status do
"" ->
:unsupported
_ ->
try do
String.to_existing_atom(status) |> normalize_status()
rescue
ArgumentError -> :unsupported
end
end
end
defp normalize_status(_), do: :unsupported
end