defmodule ExOpenAi.Request do @moduledoc """ The base module for all requests in the ExOpenAi API client. Can be used to prepare, validate and update requests. ## Examples iex> params = %{ ...> model: "text-model", ...> messages: [ ...> %{content: "hello"} ...> ] ...> } iex> Request.do_change(params, promp: "this is a prompt") %{model: "text-model", messages: [%{content: "hello"}], prompt: "this is a prompt"} """ @doc false defmacro __using__(_options) do quote do @doc """ Builds the map by adding to the existing map and updates the map with the attributes passed in. ## Examples iex> params = %{ ...> model: "text-model", ...> messages: [ ...> %{content: "hello"} ...> ] ...> } iex> Request.do_change(params, promp: "this is a prompt") %{model: "text-model", messages: [%{content: "hello"}], prompt: "this is a prompt"} """ @spec do_change(map, attributes :: list) :: map def do_change(map, attributes) do incoming_map = attributes |> Enum.into(%{}) Map.merge(map, incoming_map) end @doc """ Ensures that the map has the required fields. ## Examples iex> params = %{model: "text-model", messages: [%{content: "hello"}]} iex> Request.has_required_fields?(params, [:model, :messages]) true iex> params = %{model: "text-model"} iex> Request.has_required_fields?(params, [:model, :messages]) false """ @spec has_required_fields?(map, fields :: list) :: boolean def has_required_fields?(map, fields) do keys = map |> Map.keys() Enum.all?(fields, fn field -> field in keys end) end @doc """ Prepares the map by removing any fields that are not in the list of fields. and ensures required fields are present. ## Examples iex> params = %{ ...> model: "text-model", ...> messages: [ ...> %{content: "hello"} ...> ] ...> } iex> Request.prepare(params, [], [:model, :messages]) %{model: "text-model", messages: [%{content: "hello"}]} iex> params = %{ ...> model: "text-model", ...> messages: [ ...> %{content: "hello"} ...> ] ...> } iex> Request.prepare(params, [], [:model, :messages, :prompt]) ** (RuntimeError) Required fields are missing """ @spec prepare(map, fields :: list, required_fields :: list) :: map def prepare(map, fields, required_fields) do if has_required_fields?(map, required_fields) do map |> delete_invalid_fields(fields ++ required_fields) else raise "Required fields are missing" end end @doc """ Deletes any fields that are not in the list of fields. ## Examples iex> params = %{ ...> model: "text-model", ...> messages: [ ...> %{content: "hello"} ...> ] ...> } iex> Request.delete_invalid_fields(params, [:model, :messages]) %{model: "text-model", messages: [%{content: "hello"}]} """ @spec delete_invalid_fields(map, fields :: list) :: map def delete_invalid_fields(map, fields) do map |> Enum.filter(fn {key, _value} -> key in fields end) |> Enum.into(%{}) end end end end