Current section

Files

Jump to
agent_session_manager lib asm inference_endpoint contracts.ex
Raw

lib/asm/inference_endpoint/contracts.ex

defmodule ASM.InferenceEndpoint.ConsumerManifest do
@moduledoc """
Shared consumer manifest contract for the ASM inference endpoint facade.
"""
defstruct contract_version: "inference.v1",
consumer: nil,
accepted_runtime_kinds: [:task],
accepted_management_modes: [:jido_managed],
accepted_protocols: [:openai_chat_completions],
required_capabilities: %{},
optional_capabilities: %{},
constraints: %{},
metadata: %{}
@type t :: %__MODULE__{
contract_version: String.t(),
consumer: atom() | String.t() | nil,
accepted_runtime_kinds: [atom()],
accepted_management_modes: [atom()],
accepted_protocols: [atom()],
required_capabilities: map(),
optional_capabilities: map(),
constraints: map(),
metadata: map()
}
@spec new(keyword() | map()) :: {:ok, t()} | {:error, term()}
def new(attrs) when is_list(attrs), do: new(Map.new(attrs))
def new(attrs) when is_map(attrs) do
{:ok,
struct(__MODULE__, %{
contract_version:
value(attrs, :contract_version, "inference.v1") |> normalize_string("contract_version"),
consumer: value(attrs, :consumer),
accepted_runtime_kinds: value(attrs, :accepted_runtime_kinds, [:task]),
accepted_management_modes: value(attrs, :accepted_management_modes, [:jido_managed]),
accepted_protocols: value(attrs, :accepted_protocols, [:openai_chat_completions]),
required_capabilities: value(attrs, :required_capabilities, %{}) |> normalize_map(),
optional_capabilities: value(attrs, :optional_capabilities, %{}) |> normalize_map(),
constraints: value(attrs, :constraints, %{}) |> normalize_map(),
metadata: value(attrs, :metadata, %{}) |> normalize_map()
})}
rescue
error in ArgumentError -> {:error, error}
end
def new(_attrs), do: {:error, :invalid_consumer_manifest}
@spec new!(keyword() | map()) :: t()
def new!(attrs) do
case new(attrs) do
{:ok, %__MODULE__{} = manifest} -> manifest
{:error, reason} -> raise ArgumentError, "invalid consumer manifest: #{inspect(reason)}"
end
end
defp value(attrs, key, default \\ nil),
do: Map.get(attrs, key, Map.get(attrs, Atom.to_string(key), default))
defp normalize_string(value, _field_name) when is_binary(value) and value != "", do: value
defp normalize_string(value, field_name) do
raise ArgumentError, "#{field_name} must be a non-empty string, got: #{inspect(value)}"
end
defp normalize_map(value) when is_map(value), do: Map.new(value)
defp normalize_map(value) do
raise ArgumentError, "expected a map, got: #{inspect(value)}"
end
end
defmodule ASM.InferenceEndpoint.EndpointDescriptor do
@moduledoc """
Shared endpoint publication contract for CLI-backed ASM inference routes.
"""
defstruct contract_version: "inference.v1",
endpoint_id: nil,
runtime_kind: :task,
management_mode: :jido_managed,
target_class: :cli_endpoint,
protocol: :openai_chat_completions,
base_url: nil,
headers: %{},
provider_identity: nil,
model_identity: nil,
source_runtime: :agent_session_manager,
source_runtime_ref: nil,
lease_ref: nil,
health_ref: nil,
boundary_ref: nil,
capabilities: %{},
metadata: %{}
@type t :: %__MODULE__{
contract_version: String.t(),
endpoint_id: String.t(),
runtime_kind: :client | :task | :service,
management_mode: atom(),
target_class: :cloud_provider | :cli_endpoint | :self_hosted_endpoint,
protocol: atom(),
base_url: String.t(),
headers: %{optional(String.t()) => String.t()},
provider_identity: atom() | String.t() | nil,
model_identity: String.t() | nil,
source_runtime: atom(),
source_runtime_ref: String.t() | nil,
lease_ref: String.t() | nil,
health_ref: String.t() | nil,
boundary_ref: String.t() | nil,
capabilities: map(),
metadata: map()
}
@spec new(keyword() | map()) :: {:ok, t()} | {:error, term()}
def new(attrs) when is_list(attrs), do: new(Map.new(attrs))
def new(attrs) when is_map(attrs) do
{:ok,
struct(__MODULE__, %{
contract_version:
value(attrs, :contract_version, "inference.v1") |> normalize_string("contract_version"),
endpoint_id: value(attrs, :endpoint_id) |> normalize_string("endpoint_id"),
runtime_kind: value(attrs, :runtime_kind, :task),
management_mode: value(attrs, :management_mode, :jido_managed),
target_class: value(attrs, :target_class, :cli_endpoint),
protocol: value(attrs, :protocol, :openai_chat_completions),
base_url: value(attrs, :base_url) |> normalize_string("base_url"),
headers: value(attrs, :headers, %{}) |> normalize_headers(),
provider_identity: value(attrs, :provider_identity),
model_identity: value(attrs, :model_identity),
source_runtime: value(attrs, :source_runtime, :agent_session_manager),
source_runtime_ref: value(attrs, :source_runtime_ref),
lease_ref: value(attrs, :lease_ref),
health_ref: value(attrs, :health_ref),
boundary_ref: value(attrs, :boundary_ref),
capabilities: value(attrs, :capabilities, %{}) |> normalize_map(),
metadata: value(attrs, :metadata, %{}) |> normalize_map()
})}
rescue
error in ArgumentError -> {:error, error}
end
def new(_attrs), do: {:error, :invalid_endpoint_descriptor}
@spec new!(keyword() | map()) :: t()
def new!(attrs) do
case new(attrs) do
{:ok, %__MODULE__{} = descriptor} -> descriptor
{:error, reason} -> raise ArgumentError, "invalid endpoint descriptor: #{inspect(reason)}"
end
end
defp value(attrs, key, default \\ nil),
do: Map.get(attrs, key, Map.get(attrs, Atom.to_string(key), default))
defp normalize_string(nil, _field_name), do: nil
defp normalize_string(value, _field_name) when is_binary(value) and value != "", do: value
defp normalize_string(value, field_name) do
raise ArgumentError, "#{field_name} must be a non-empty string, got: #{inspect(value)}"
end
defp normalize_headers(value) when is_map(value) do
Enum.into(value, %{}, fn {key, header_value} ->
{to_string(key), to_string(header_value)}
end)
end
defp normalize_headers(value) do
raise ArgumentError, "headers must be a map, got: #{inspect(value)}"
end
defp normalize_map(value) when is_map(value), do: Map.new(value)
defp normalize_map(value) do
raise ArgumentError, "expected a map, got: #{inspect(value)}"
end
end
defmodule ASM.InferenceEndpoint.BackendManifest do
@moduledoc """
Shared backend manifest contract for the ASM CLI inference endpoint facade.
"""
defstruct contract_version: "inference.v1",
backend: :asm_inference_endpoint,
runtime_kind: :task,
management_modes: [:jido_managed],
startup_kind: :spawned,
protocols: [:openai_chat_completions],
capabilities: %{streaming?: false, tool_calling?: false, embeddings?: :unknown},
supported_surfaces: [:local_subprocess, :ssh_exec, :guest_bridge],
resource_profile: %{},
metadata: %{}
@type t :: %__MODULE__{
contract_version: String.t(),
backend: atom(),
runtime_kind: :task | :service,
management_modes: [atom()],
startup_kind: atom() | nil,
protocols: [atom()],
capabilities: map(),
supported_surfaces: [atom()],
resource_profile: map(),
metadata: map()
}
@spec new(keyword() | map()) :: {:ok, t()} | {:error, term()}
def new(attrs) when is_list(attrs), do: new(Map.new(attrs))
def new(attrs) when is_map(attrs) do
{:ok,
struct(__MODULE__, %{
contract_version:
value(attrs, :contract_version, "inference.v1") |> normalize_string("contract_version"),
backend: value(attrs, :backend, :asm_inference_endpoint),
runtime_kind: value(attrs, :runtime_kind, :task),
management_modes: value(attrs, :management_modes, [:jido_managed]),
startup_kind: value(attrs, :startup_kind, :spawned),
protocols: value(attrs, :protocols, [:openai_chat_completions]),
capabilities:
value(attrs, :capabilities, %{
streaming?: false,
tool_calling?: false,
embeddings?: :unknown
})
|> normalize_capabilities(),
supported_surfaces:
value(attrs, :supported_surfaces, [:local_subprocess, :ssh_exec, :guest_bridge]),
resource_profile: value(attrs, :resource_profile, %{}) |> normalize_map(),
metadata: value(attrs, :metadata, %{}) |> normalize_map()
})}
rescue
error in ArgumentError -> {:error, error}
end
def new(_attrs), do: {:error, :invalid_backend_manifest}
@spec new!(keyword() | map()) :: t()
def new!(attrs) do
case new(attrs) do
{:ok, %__MODULE__{} = manifest} -> manifest
{:error, reason} -> raise ArgumentError, "invalid backend manifest: #{inspect(reason)}"
end
end
defp value(attrs, key, default),
do: Map.get(attrs, key, Map.get(attrs, Atom.to_string(key), default))
defp normalize_string(value, _field_name) when is_binary(value) and value != "", do: value
defp normalize_string(value, field_name) do
raise ArgumentError, "#{field_name} must be a non-empty string, got: #{inspect(value)}"
end
defp normalize_capabilities(value) when is_map(value) do
%{
streaming?: capability_value(value, :streaming?, false),
tool_calling?: capability_value(value, :tool_calling?, false),
embeddings?: capability_value(value, :embeddings?, :unknown)
}
end
defp normalize_capabilities(value) do
raise ArgumentError, "capabilities must be a map, got: #{inspect(value)}"
end
defp capability_value(map, key, default) do
map
|> Map.get(key, Map.get(map, Atom.to_string(key), default))
|> normalize_capability(default)
end
defp normalize_capability(value, _default) when is_boolean(value) or value == :unknown,
do: value
defp normalize_capability("unknown", _default), do: :unknown
defp normalize_capability(value, _default) do
raise ArgumentError, "invalid capability value: #{inspect(value)}"
end
defp normalize_map(value) when is_map(value), do: Map.new(value)
defp normalize_map(value) do
raise ArgumentError, "expected a map, got: #{inspect(value)}"
end
end
defmodule ASM.InferenceEndpoint.CompatibilityResult do
@moduledoc """
Shared compatibility result contract for ASM endpoint publication.
"""
defstruct contract_version: "inference.v1",
compatible?: false,
reason: :unresolved,
resolved_runtime_kind: nil,
resolved_management_mode: nil,
resolved_protocol: nil,
warnings: [],
missing_requirements: [],
metadata: %{}
@type t :: %__MODULE__{
contract_version: String.t(),
compatible?: boolean(),
reason: atom(),
resolved_runtime_kind: atom() | nil,
resolved_management_mode: atom() | nil,
resolved_protocol: atom() | nil,
warnings: [atom()],
missing_requirements: [atom()],
metadata: map()
}
@spec new(keyword() | map()) :: {:ok, t()} | {:error, term()}
def new(attrs) when is_list(attrs), do: new(Map.new(attrs))
def new(attrs) when is_map(attrs) do
{:ok,
struct(__MODULE__, %{
contract_version:
value(attrs, :contract_version, "inference.v1") |> normalize_string("contract_version"),
compatible?: value(attrs, :compatible?, false),
reason: value(attrs, :reason, :unresolved),
resolved_runtime_kind: value(attrs, :resolved_runtime_kind),
resolved_management_mode: value(attrs, :resolved_management_mode),
resolved_protocol: value(attrs, :resolved_protocol),
warnings: value(attrs, :warnings, []),
missing_requirements: value(attrs, :missing_requirements, []),
metadata: value(attrs, :metadata, %{}) |> normalize_map()
})}
rescue
error in ArgumentError -> {:error, error}
end
def new(_attrs), do: {:error, :invalid_compatibility_result}
@spec new!(keyword() | map()) :: t()
def new!(attrs) do
case new(attrs) do
{:ok, %__MODULE__{} = result} -> result
{:error, reason} -> raise ArgumentError, "invalid compatibility result: #{inspect(reason)}"
end
end
defp value(attrs, key, default \\ nil),
do: Map.get(attrs, key, Map.get(attrs, Atom.to_string(key), default))
defp normalize_string(value, _field_name) when is_binary(value) and value != "", do: value
defp normalize_string(value, field_name) do
raise ArgumentError, "#{field_name} must be a non-empty string, got: #{inspect(value)}"
end
defp normalize_map(value) when is_map(value), do: Map.new(value)
defp normalize_map(value) do
raise ArgumentError, "expected a map, got: #{inspect(value)}"
end
end