Packages
fnord
0.5.1
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.ex
defmodule AI do
@moduledoc """
AI is a behavior module that defines the interface for interacting with
OpenAI's API. It provides a common interface for the various OpenAI-powered
operations used by the application.
"""
defstruct [
:client,
:api_key
]
@type t :: %__MODULE__{
client: %AI.OpenAI{}
}
@api_timeout 5 * 60 * 1000
@default_max_attempts 3
@retry_interval 250
@doc """
Create a new AI instance. Instances share the same client connection.
"""
def new() do
client = AI.OpenAI.new(recv_timeout: @api_timeout)
%AI{client: client}
end
# -----------------------------------------------------------------------------
# Completions
# -----------------------------------------------------------------------------
def get_completion(ai, model, msgs, tools) do
request = [ai.client, model, msgs, tools]
do_get_completion(ai, request, @default_max_attempts, 1)
end
defp do_get_completion(_ai, _request, max, attempt) when attempt > max do
{:error, "Request timed out after #{attempt} attempts."}
end
defp do_get_completion(ai, request, max, attempt) do
if attempt > 1, do: Process.sleep(@retry_interval)
AI.OpenAI
|> apply(:get_completion, request)
|> case do
{:error, :timeout} -> do_get_completion(ai, request, max, attempt + 1)
etc -> etc
end
end
# -----------------------------------------------------------------------------
# Embeddings
# -----------------------------------------------------------------------------
@embeddings_model "text-embedding-3-large"
# It's actually 8192 for this model, but this gives us a little bit of
# wiggle room in case the tokenizer we are using falls behind.
@embeddings_token_limit 6000
@doc """
Get embeddings for the given text. The text is split into chunks of 8192
tokens to avoid exceeding the model's input limit. Returns a list of
embeddings for each chunk.
"""
def get_embeddings(ai, text) do
text
|> AI.Tokenizer.chunk(@embeddings_token_limit, @embeddings_model)
|> Enum.map(&[ai.client, @embeddings_model, &1])
|> Enum.reduce_while([], fn request, embeddings ->
ai
|> get_embedding(request, @default_max_attempts, 1)
|> case do
{:ok, embedding} -> {:cont, [embedding | embeddings]}
{:error, reason} -> {:halt, {:error, reason}}
end
end)
|> case do
{:error, reason} -> {:error, inspect(reason)}
embeddings -> {:ok, Enum.reverse(embeddings)}
end
end
defp get_embedding(_ai, _request, max, attempt) when attempt > max do
{:error, "Request timed out after #{attempt} attempts."}
end
defp get_embedding(ai, request, max, attempt) do
if attempt > 1 do
Process.sleep(@retry_interval)
end
AI.OpenAI
|> apply(:get_embedding, request)
|> case do
{:error, :timeout} -> get_embedding(ai, request, max, attempt + 1)
etc -> etc
end
end
end