lib/langchain/memory.ex

defmodule LangChain.BaseMemory do
  @moduledoc """
  A GenServer that stores and retrieves memory variables, Memory supports the agent genservers
  """
  use GenServer

  defmacro __using__(_) do
    quote do
      use GenServer

      alias BaseMemory, as: MemoryServer

      # Callbacks
      @impl true
      def init(args) do
        MemoryServer.init(args)
      end

      @impl true
      def handle_call(request, from, state) do
        MemoryServer.handle_call(request, from, state)
      end

      @impl true
      def handle_cast(request, state) do
        MemoryServer.handle_cast(request, state)
      end

      @impl true
      def handle_info(info, state) do
        MemoryServer.handle_info(info, state)
      end

      @impl true
      def terminate(reason, state) do
        MemoryServer.terminate(reason, state)
      end

      @impl true
      def code_change(old_vsn, state, extra) do
        MemoryServer.code_change(old_vsn, state, extra)
      end
    end
  end

  # Server

  def init(_args) do
    state = %{
      input_values: %{},
      output_values: %{},
      memory_variables: %{}
    }

    {:ok, state}
  end

  def handle_call({:load_memory_variables, values}, _from, state) do
    {:reply, state.memory_variables, %{state | input_values: values}}
  end

  def handle_call({:save_context, input_values, output_values}, _from, state) do
    {:reply, :ok, %{state | input_values: input_values, output_values: output_values}}
  end

  def handle_cast(_, state) do
    {:noreply, state}
  end

  def handle_info(_, state) do
    {:noreply, state}
  end

  def terminate(_reason, _state) do
    :ok
  end

  def code_change(_old_vsn, state, _extra) do
    {:ok, state}
  end
end

defmodule Utils do
  @moduledoc """
  Utility functions for memory
  """
  def get_input_value(input_values, input_key \\ nil) do
    case input_key do
      nil ->
        keys = Map.keys(input_values)

        if length(keys) == 1 do
          key = hd(keys)
          Map.get(input_values, key)
        else
          raise ArgumentError,
            message:
              "Input values have multiple keys, memory only supported when one key currently: #{inspect(keys)}"
        end

      key ->
        Map.get(input_values, key)
    end
  end

  # def get_buffer_string(messages, human_prefix \\ "Human", ai_prefix \\ "AI") do
  #   messages
  #   |> Enum.map(fn m ->
  #     # all the messages have a 'text' field:
  #     role = case m.get_type() do
  #       :human -> human_prefix
  #       :ai -> ai_prefix
  #       :system -> "System"
  #       # generics will have a 'role' field:
  #       :generic -> m.get_role()
  #       _ -> raise ArgumentError, message: "Got unsupported message type: #{inspect(m)}"
  #     end

  #     "#{role}: #{m.text}"
  #   end)
  #   |> Enum.join("\n")
  # end
end