Packages

fnord

0.9.34

AI code archaeology

Current section

Files

Jump to
fnord lib ai endpoint openai.ex
Raw

lib/ai/endpoint/openai.ex

defmodule AI.Endpoint.OpenAI do
@moduledoc """
OpenAI-specific endpoint implementation.
Provides the endpoint path and error classification used by AI.Endpoint's
centralized retry/backoff logic.
"""
@behaviour AI.Endpoint
@base_url "https://api.openai.com"
@impl AI.Endpoint
@spec endpoint_path() :: String.t()
def endpoint_path, do: "#{@base_url}/v1/chat/completions"
@doc """
Provider-specific error classifier for OpenAI/Cloudflare style responses.
See `c:AI.Endpoint.endpoint_error_classify/4` for contract details.
"""
@impl AI.Endpoint
@spec endpoint_error_classify(integer | nil, binary | nil, list | nil, term | nil) ::
:ok | {:retry, atom, non_neg_integer | nil} | {:fail, atom, binary}
def endpoint_error_classify(status, body, _headers, transport_reason) do
case {status, body, transport_reason} do
{nil, nil, :timeout} ->
{:retry, :network_glitch, nil}
{nil, nil, :closed} ->
{:retry, :network_glitch, nil}
{nil, nil, {:tls_alert, _}} ->
{:retry, :network_glitch, nil}
{nil, nil, {:ssl, _}} ->
{:retry, :network_glitch, nil}
{429, b, _} when is_binary(b) ->
cond do
throttled_json?(b) -> {:retry, :throttled, parse_try_again_ms(b)}
cloudflare_plaintext?(b) -> {:retry, :intermediary, parse_try_again_ms(b) || 300}
true -> {:retry, :throttled, nil}
end
{s, _b, _} when is_integer(s) and s >= 500 and s < 600 ->
{:retry, :server_error, nil}
{401, _b, _} ->
{:fail, :unauthorized, "Unauthorized"}
{403, _b, _} ->
{:fail, :forbidden, "Forbidden"}
_ ->
:ok
end
end
defp throttled_json?(body) do
case SafeJson.decode(body) do
{:ok, %{"error" => %{"code" => code}}} when code in ["rate_limit_exceeded", "rate_limit"] ->
true
_ ->
false
end
end
defp cloudflare_plaintext?(body) when is_binary(body) do
Regex.match?(~r/cloudflare|please\s+try\s+again/i, body)
end
defp parse_try_again_ms(body) when is_binary(body) do
case Regex.run(~r/try\s+again\s+in\s+(\d+)ms/i, body) do
[_, ms] -> max(1, String.to_integer(ms))
_ -> nil
end
end
end