Skip to main content

lib/pi/agent/step.ex

defmodule Pi.Agent.Step do
  @moduledoc "Declarative Pi agent orchestration step."

  alias Pi.Protocol.LLM.Message
  alias Pi.Session.State

  defstruct [:name, :system, :prompt, :messages, :parent_id, metadata: %{}]

  @type t :: %__MODULE__{
          name: atom() | String.t() | nil,
          system: String.t() | nil,
          prompt: String.t() | nil,
          messages: [Message.t()] | nil,
          parent_id: String.t() | nil,
          metadata: map()
        }

  def from(%__MODULE__{} = step), do: step
  def from(prompt) when is_binary(prompt), do: %__MODULE__{prompt: prompt}

  def from(%State{} = session),
    do: %__MODULE__{
      name: session.name,
      system: session.system,
      messages: session.messages,
      parent_id: session.parent_id,
      metadata: session.metadata
    }

  def from(opts) when is_list(opts) do
    %__MODULE__{
      name: Keyword.get(opts, :name),
      system: Keyword.get(opts, :system),
      prompt: Keyword.get(opts, :prompt),
      messages: Keyword.get(opts, :messages),
      parent_id: Keyword.get(opts, :parent_id),
      metadata: Keyword.get(opts, :metadata, %{})
    }
  end

  def to_session(step, opts \\ [])

  def to_session(%__MODULE__{prompt: prompt} = step, opts) when is_binary(prompt) do
    Pi.Agent.session(prompt,
      name: step.name || Keyword.get(opts, :name),
      system: step.system || Keyword.get(opts, :system),
      parent_id: step.parent_id || Keyword.get(opts, :parent_id),
      metadata: Map.merge(step.metadata, Keyword.get(opts, :metadata, %{}))
    )
  end

  def to_session(%__MODULE__{} = step, opts) do
    Pi.Agent.session(
      name: step.name || Keyword.get(opts, :name),
      system: step.system || Keyword.get(opts, :system),
      messages: step.messages || Keyword.get(opts, :messages, []),
      parent_id: step.parent_id || Keyword.get(opts, :parent_id),
      metadata: Map.merge(step.metadata, Keyword.get(opts, :metadata, %{}))
    )
  end
end