Codex.RateLimit (Codex SDK v0.7.2)

Copy Markdown View Source

Rate limit detection and handling utilities.

Detection

Rate limits are detected from:

  • Codex.Error structs with :rate_limit kind
  • HTTP 429 status codes
  • Error messages containing "rate_limit"
  • Retry-After headers in responses

Handling

When rate limited, the SDK:

  1. Extracts retry-after hint if available
  2. Backs off for the specified duration (or default)
  3. Emits telemetry event
  4. Retries the request

Configuration

config :codex_sdk,
  rate_limit_default_delay_ms: 60_000,
  rate_limit_max_delay_ms: 300_000,
  rate_limit_multiplier: 2.0

Example

Codex.RateLimit.with_rate_limit_handling(fn ->
  make_api_call()
end, max_attempts: 3)

Summary

Functions

Calculates delay for rate limit backoff.

Detects rate limit error from response or error.

Handles rate limit by waiting and emitting telemetry.

Parses Retry-After header from response.

Wraps function with rate limit handling.

Types

rate_limit_info()

@type rate_limit_info() :: %{
  optional(:retry_after_ms) => non_neg_integer() | nil,
  optional(:message) => String.t(),
  optional(:details) => map(),
  optional(:source) => atom(),
  optional(:body) => map()
}

Functions

calculate_delay(rate_limit_info, attempt \\ 1)

@spec calculate_delay(rate_limit_info(), pos_integer()) :: non_neg_integer()

Calculates delay for rate limit backoff.

If the rate limit info contains an explicit retry_after_ms, that value is used. Otherwise, exponential backoff is applied based on the attempt number.

Examples

iex> Codex.RateLimit.calculate_delay(%{retry_after_ms: 45_000}, 1)
45_000

iex> delay1 = Codex.RateLimit.calculate_delay(%{}, 1)
iex> delay2 = Codex.RateLimit.calculate_delay(%{}, 2)
iex> delay2 > delay1
true

detect(arg1)

@spec detect(term()) :: {:rate_limited, rate_limit_info()} | :ok

Detects rate limit error from response or error.

Returns {:rate_limited, info} if a rate limit is detected, or :ok if not rate limited.

Examples

iex> error = Codex.Error.rate_limit("Rate limited", retry_after_ms: 30_000)
iex> {:rate_limited, info} = Codex.RateLimit.detect({:error, error})
iex> info.retry_after_ms
30_000

iex> Codex.RateLimit.detect({:ok, :success})
:ok

handle(rate_limit_info, opts \\ [])

@spec handle(
  rate_limit_info(),
  keyword()
) :: :ok

Handles rate limit by waiting and emitting telemetry.

Sleeps for the calculated delay and emits a [:codex, :rate_limit, :rate_limited] telemetry event.

Options

  • :attempt - Current attempt number (default: 1)

Examples

info = %{retry_after_ms: 1000}
Codex.RateLimit.handle(info, attempt: 1)
# Sleeps for 1000ms and emits telemetry

parse_retry_after(arg1)

@spec parse_retry_after(map()) :: non_neg_integer() | nil

Parses Retry-After header from response.

Handles both numeric seconds and HTTP-date formats.

Examples

iex> Codex.RateLimit.parse_retry_after(%{headers: %{"retry-after" => "60"}})
60_000

iex> Codex.RateLimit.parse_retry_after(%{headers: %{"Retry-After" => "120"}})
120_000

iex> Codex.RateLimit.parse_retry_after(%{})
nil

with_rate_limit_handling(fun, opts \\ [])

@spec with_rate_limit_handling(
  (-> term()),
  keyword()
) :: term()

Wraps function with rate limit handling.

Automatically detects rate limit responses and retries with appropriate backoff. Uses exponential backoff by default, or respects explicit retry-after hints from the API.

Options

  • :max_attempts - Maximum number of attempts (default: 3)

Examples

result = Codex.RateLimit.with_rate_limit_handling(fn ->
  make_api_call()
end, max_attempts: 3)