Packages
fnord
0.8.17
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/ai/completion_api.ex
defmodule AI.CompletionAPI do
@endpoint "https://api.openai.com/v1/chat/completions"
@type model :: AI.Model.t()
@type msgs :: [map()]
@type tools :: nil | [AI.Tools.tool_spec()]
@type usage :: integer()
@type msg_response :: {:ok, :msg, String.t(), usage}
@type tool_response :: {:ok, :tool, list(map())}
@type response :: msg_response | tool_response | {:error, map}
@spec get(model, msgs, tools) :: response
def get(model, msgs, tools \\ nil) do
api_key = get_api_key!()
headers = [
{"Authorization", "Bearer #{api_key}"},
{"Content-Type", "application/json"}
]
payload =
%{
model: model.model,
messages: msgs
}
|> Map.merge(
case tools do
nil -> %{}
tools -> %{tools: tools}
end
)
|> Map.merge(
case model.reasoning do
:low -> %{reasoning_effort: "low"}
:medium -> %{reasoning_effort: "medium"}
:high -> %{reasoning_effort: "high"}
_ -> %{}
end
)
Http.post_json(@endpoint, headers, payload)
|> case do
{:transport_error, error} ->
get_error(error)
{:http_error, error} ->
UI.error("HTTP error while calling OpenAI API: #{inspect(error)}")
IO.inspect(payload.messages |> Enum.slice(-1, 1), label: "Last message sent")
get_error(error)
{:ok, response} ->
get_response(response)
end
end
# -----------------------------------------------------------------------------
# Private functions
# -----------------------------------------------------------------------------
@spec get_api_key!() :: String.t()
defp get_api_key!() do
["FNORD_OPENAI_API_KEY", "OPENAI_API_KEY"]
|> Enum.find_value(&System.get_env(&1, nil))
|> case do
nil ->
raise "Either FNORD_OPENAI_API_KEY or OPENAI_API_KEY environment variable must be set"
api_key ->
api_key
end
end
defp get_response(%{
"choices" => [%{"message" => response}],
"usage" => %{"total_tokens" => usage}
}) do
response
|> Map.put("usage", usage)
|> get_response()
end
defp get_response(%{"tool_calls" => tool_calls}) do
{:ok, :tool, Enum.map(tool_calls, &get_tool_call/1)}
end
defp get_response(%{"content" => response, "usage" => usage}) do
{:ok, :msg, response, usage}
end
defp get_tool_call(%{"id" => id, "function" => %{"name" => name, "arguments" => args}}) do
%{id: id, function: %{name: name, arguments: args}}
end
defp get_error(:closed), do: {:error, "Connection closed"}
defp get_error(:timeout), do: {:error, "Connection timed out"}
defp get_error({http_status, json_error_string}) do
json_error_string
|> Jason.decode()
|> case do
{:ok, %{"error" => %{"code" => code, "message" => msg}}} ->
{:error, %{http_status: http_status, code: code, message: msg}}
{:ok, error} ->
{:error, %{http_status: http_status, error: inspect(error, pretty: true)}}
{:error, _} ->
{:error, %{http_status: http_status, error: json_error_string}}
end
end
end