Current section

Files

Jump to
codex_sdk lib codex transport support.ex
Raw

lib/codex/transport/support.ex

defmodule Codex.Transport.Support do
@moduledoc false
alias Codex.RateLimit
alias Codex.Retry
alias Codex.Thread.Options, as: ThreadOptions
@spec normalize_turn_opts(map() | keyword() | nil) :: map()
def normalize_turn_opts(%{} = opts), do: opts
def normalize_turn_opts(list) when is_list(list), do: Map.new(list)
def normalize_turn_opts(_), do: %{}
@spec with_retry_and_rate_limit((-> term()), ThreadOptions.t(), map()) :: term()
def with_retry_and_rate_limit(fun, %ThreadOptions{} = thread_opts, %{} = turn_opts)
when is_function(fun, 0) do
{retry?, retry_opts} = resolve_retry(thread_opts, turn_opts)
{rate_limit?, rate_limit_opts} = resolve_rate_limit(thread_opts, turn_opts)
base_fun = fn -> fun.() end
fun_with_rate_limit =
if rate_limit? do
fn -> RateLimit.with_rate_limit_handling(base_fun, rate_limit_opts) end
else
base_fun
end
if retry? do
Retry.with_retry(fun_with_rate_limit, adjust_retry_opts(retry_opts, rate_limit?))
else
fun_with_rate_limit.()
end
end
defp resolve_retry(%ThreadOptions{} = thread_opts, %{} = turn_opts) do
retry = fetch_opt(turn_opts, :retry)
retry_opts = fetch_opt(turn_opts, :retry_opts)
retry =
if is_nil(retry) do
thread_opts.retry
else
retry
end
retry_opts =
retry_opts ||
thread_opts.retry_opts ||
[]
{retry_enabled?(retry, retry_opts), normalize_opts(retry_opts)}
end
defp resolve_rate_limit(%ThreadOptions{} = thread_opts, %{} = turn_opts) do
rate_limit = fetch_opt(turn_opts, :rate_limit)
rate_limit_opts = fetch_opt(turn_opts, :rate_limit_opts)
rate_limit =
if is_nil(rate_limit) do
thread_opts.rate_limit
else
rate_limit
end
rate_limit_opts =
rate_limit_opts ||
thread_opts.rate_limit_opts ||
[]
{rate_limit_enabled?(rate_limit, rate_limit_opts), normalize_opts(rate_limit_opts)}
end
defp retry_enabled?(true, _opts), do: true
defp retry_enabled?(false, _opts), do: false
defp retry_enabled?(nil, opts), do: opts != []
defp retry_enabled?(_other, _opts), do: false
defp rate_limit_enabled?(true, _opts), do: true
defp rate_limit_enabled?(false, _opts), do: false
defp rate_limit_enabled?(nil, opts), do: opts != []
defp rate_limit_enabled?(_other, _opts), do: false
defp normalize_opts(nil), do: []
defp normalize_opts(list) when is_list(list), do: list
defp normalize_opts(%{} = map), do: Map.to_list(map)
defp normalize_opts(_), do: []
defp adjust_retry_opts(opts, false), do: opts
defp adjust_retry_opts(opts, true) do
default_retry_if = fn reason -> Retry.retryable?(reason) and not rate_limit_error?(reason) end
Keyword.update(opts, :retry_if, default_retry_if, fn user_fun ->
fn reason -> user_fun.(reason) and not rate_limit_error?(reason) end
end)
end
defp rate_limit_error?(reason) do
case RateLimit.detect({:error, reason}) do
{:rate_limited, _info} -> true
:ok -> false
end
end
defp fetch_opt(%{} = opts, key) do
case Map.fetch(opts, key) do
{:ok, value} ->
value
:error ->
case Map.fetch(opts, Atom.to_string(key)) do
{:ok, value} -> value
:error -> nil
end
end
end
end