Packages
fnord
0.4.23
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: %OpenaiEx{}
}
@api_timeout 45_000
@default_max_attempts 3
@retry_interval 250
@callback new() :: struct()
@callback get_embeddings(struct(), String.t()) :: {:ok, [String.t()]} | {:error, term()}
@callback get_summary(struct(), String.t(), String.t()) :: {:ok, String.t()} | {:error, term()}
@callback get_outline(struct(), String.t(), String.t()) :: {:ok, String.t()} | {:error, term()}
@behaviour AI
@impl AI
@doc """
Create a new AI instance. Instances share the same client connection.
"""
def new() do
api_key = System.get_env("OPENAI_API_KEY")
if is_nil(api_key) do
raise "Missing OpenAI API key. Please set the OPENAI_API_KEY environment variable."
end
openai =
api_key
|> OpenaiEx.new()
|> OpenaiEx.with_receive_timeout(@api_timeout)
%AI{client: openai}
end
def get_completion(ai, options \\ []) do
with {:ok, model} <- Keyword.fetch(options, :model),
{:ok, messages} <- get_messages(options),
{:ok, tools} <- get_tools(options) do
max_attempts = Keyword.get(options, :attempts, @default_max_attempts)
args = [model: model, messages: messages] ++ tools
request = OpenaiEx.Chat.Completions.new(args)
get_completion(ai, request, max_attempts, 1)
end
end
defp get_tools(options) do
case Keyword.get(options, :tools) do
nil -> []
tools -> [tools: tools]
end
|> then(&{:ok, &1})
end
defp get_messages(options) do
if Keyword.has_key?(options, :messages) do
Keyword.get(options, :messages)
else
with {:ok, system_prompt} <- Keyword.fetch(options, :system_prompt),
{:ok, user_prompt} <- Keyword.fetch(options, :user_prompt) do
[
OpenaiEx.ChatMessage.system(system_prompt),
OpenaiEx.ChatMessage.user(user_prompt)
]
end
end
|> case do
{:error, reason} -> {:error, reason}
messages -> {:ok, messages}
end
end
defp get_completion(_ai, _request, max_attempts, current_attempt)
when current_attempt > max_attempts do
{:error, "Request timed out after #{current_attempt} attempts."}
end
defp get_completion(ai, request, max_attempts, current_attempt) do
completion = OpenaiEx.Chat.Completions.create(ai.client, request)
case completion do
{:ok, %{"choices" => [event]}} ->
{:ok, event}
{:error, %OpenaiEx.Error{message: "Request timed out."}} ->
Process.sleep(@retry_interval)
get_completion(ai, request, max_attempts, current_attempt + 1)
{:error, %OpenaiEx.Error{message: msg}} ->
{:error, msg}
end
end
# -----------------------------------------------------------------------------
# Embeddings
# -----------------------------------------------------------------------------
@impl AI
@doc """
See `AI.EmbeddingsAgent.get_embeddings/2`.
"""
defdelegate get_embeddings(ai, text), to: AI.Agent.Embeddings
# -----------------------------------------------------------------------------
# Summaries
# -----------------------------------------------------------------------------
@impl AI
@doc """
See `AI.FileSummaryAgent.get_summary/3`.
"""
defdelegate get_summary(ai, file, text), to: AI.Agent.FileSummary
@impl AI
@doc """
See `AI.Agent.CodeMapperAgent`.
"""
def get_outline(ai, file_path, file_content) do
ai
|> AI.Agent.CodeMapper.new(file_path, file_content)
|> AI.Agent.CodeMapper.get_outline()
end
end