Packages
A well-typed, idiomatic Gleam client for Anthropic's Claude API with streaming support and tool use
Current section
Files
Jump to
Current section
Files
src/anthropic@retry.erl
-module(anthropic@retry).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/anthropic/retry.gleam").
-export([default_retry_config/0, no_retry_config/0, aggressive_retry_config/0, with_max_retries/2, with_base_delay_ms/2, with_max_delay_ms/2, with_jitter_factor/2, with_backoff_multiplier/2, should_retry_error/1, get_retry_after_from_error/1, retries_enabled/1, calculate_delay/2, calculate_delay_with_retry_after/3, retry_with_backoff/2, retry/2, max_total_delay/1]).
-export_type([retry_config/0, retry_result/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Retry logic with exponential backoff for transient failures\n"
"\n"
" This module provides automatic retry functionality for API requests\n"
" that encounter transient errors like rate limits, server overload, or timeouts.\n"
"\n"
" ## Features\n"
"\n"
" - Exponential backoff with configurable base delay\n"
" - Jitter to prevent thundering herd\n"
" - Configurable max retries\n"
" - Respects retry-after headers when available\n"
"\n"
" ## Example\n"
"\n"
" ```gleam\n"
" let retry_config = default_retry_config()\n"
" |> with_max_retries(5)\n"
" |> with_base_delay_ms(500)\n"
"\n"
" let result = retry_with_backoff(retry_config, fn() {\n"
" api.create_message(client, request)\n"
" })\n"
" ```\n"
).
-type retry_config() :: {retry_config,
integer(),
integer(),
integer(),
float(),
float()}.
-type retry_result(IAG) :: {retry_result,
{ok, IAG} | {error, anthropic@error:anthropic_error()},
integer(),
integer()}.
-file("src/anthropic/retry.gleam", 74).
?DOC(
" Create a default retry configuration\n"
"\n"
" Defaults:\n"
" - max_retries: 3\n"
" - base_delay_ms: 1000 (1 second)\n"
" - max_delay_ms: 60000 (60 seconds)\n"
" - jitter_factor: 0.25 (25% randomness)\n"
" - backoff_multiplier: 2.0\n"
).
-spec default_retry_config() -> retry_config().
default_retry_config() ->
{retry_config, 3, 1000, 60000, 0.25, 2.0}.
-file("src/anthropic/retry.gleam", 85).
?DOC(" Create a retry configuration with no retries\n").
-spec no_retry_config() -> retry_config().
no_retry_config() ->
{retry_config, 0, 0, 0, +0.0, 1.0}.
-file("src/anthropic/retry.gleam", 96).
?DOC(" Create an aggressive retry configuration for high availability\n").
-spec aggressive_retry_config() -> retry_config().
aggressive_retry_config() ->
{retry_config, 5, 500, 30000, 0.5, 2.0}.
-file("src/anthropic/retry.gleam", 111).
?DOC(" Set the maximum number of retries\n").
-spec with_max_retries(retry_config(), integer()) -> retry_config().
with_max_retries(Config, Max_retries) ->
{retry_config,
gleam@int:max(0, Max_retries),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config)}.
-file("src/anthropic/retry.gleam", 116).
?DOC(" Set the base delay in milliseconds\n").
-spec with_base_delay_ms(retry_config(), integer()) -> retry_config().
with_base_delay_ms(Config, Base_delay_ms) ->
{retry_config,
erlang:element(2, Config),
gleam@int:max(0, Base_delay_ms),
erlang:element(4, Config),
erlang:element(5, Config),
erlang:element(6, Config)}.
-file("src/anthropic/retry.gleam", 124).
?DOC(" Set the maximum delay in milliseconds\n").
-spec with_max_delay_ms(retry_config(), integer()) -> retry_config().
with_max_delay_ms(Config, Max_delay_ms) ->
{retry_config,
erlang:element(2, Config),
erlang:element(3, Config),
gleam@int:max(0, Max_delay_ms),
erlang:element(5, Config),
erlang:element(6, Config)}.
-file("src/anthropic/retry.gleam", 129).
?DOC(" Set the jitter factor (0.0 to 1.0)\n").
-spec with_jitter_factor(retry_config(), float()) -> retry_config().
with_jitter_factor(Config, Jitter_factor) ->
Clamped = gleam@float:min(1.0, gleam@float:max(+0.0, Jitter_factor)),
{retry_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
Clamped,
erlang:element(6, Config)}.
-file("src/anthropic/retry.gleam", 138).
?DOC(" Set the backoff multiplier\n").
-spec with_backoff_multiplier(retry_config(), float()) -> retry_config().
with_backoff_multiplier(Config, Multiplier) ->
{retry_config,
erlang:element(2, Config),
erlang:element(3, Config),
erlang:element(4, Config),
erlang:element(5, Config),
gleam@float:max(1.0, Multiplier)}.
-file("src/anthropic/retry.gleam", 261).
?DOC(" Check if an error should be retried based on custom logic\n").
-spec should_retry_error(anthropic@error:anthropic_error()) -> boolean().
should_retry_error(Error) ->
anthropic@error:is_retryable(Error).
-file("src/anthropic/retry.gleam", 266).
?DOC(" Get suggested retry delay from an error (if available)\n").
-spec get_retry_after_from_error(anthropic@error:anthropic_error()) -> gleam@option:option(integer()).
get_retry_after_from_error(_) ->
none.
-file("src/anthropic/retry.gleam", 277).
?DOC(" Check if retry configuration allows retries\n").
-spec retries_enabled(retry_config()) -> boolean().
retries_enabled(Config) ->
erlang:element(2, Config) > 0.
-file("src/anthropic/retry.gleam", 150).
?DOC(" Calculate delay for a given attempt number (0-indexed)\n").
-spec calculate_delay(retry_config(), integer()) -> integer().
calculate_delay(Config, Attempt) ->
Base = erlang:float(erlang:element(3, Config)),
Power_result = gleam@float:power(
erlang:element(6, Config),
erlang:float(Attempt)
),
Exponential = case Power_result of
{ok, Value} ->
Base * Value;
{error, _} ->
Base
end,
Capped = gleam@float:min(
Exponential,
erlang:float(erlang:element(4, Config))
),
Jitter_range = Capped * erlang:element(5, Config),
Jitter = ((rand:uniform() * Jitter_range) * 2.0) - Jitter_range,
With_jitter = Capped + Jitter,
erlang:trunc(gleam@float:max(+0.0, With_jitter)).
-file("src/anthropic/retry.gleam", 174).
?DOC(" Calculate delay with optional retry-after header value\n").
-spec calculate_delay_with_retry_after(
retry_config(),
integer(),
gleam@option:option(integer())
) -> integer().
calculate_delay_with_retry_after(Config, Attempt, Retry_after_ms) ->
Calculated = calculate_delay(Config, Attempt),
case Retry_after_ms of
{some, Retry_after} ->
gleam@int:max(Calculated, Retry_after);
none ->
Calculated
end.
-file("src/anthropic/retry.gleam", 203).
?DOC(" Internal retry loop\n").
-spec retry_loop(
retry_config(),
fun(() -> {ok, IAM} | {error, anthropic@error:anthropic_error()}),
integer(),
integer()
) -> retry_result(IAM).
retry_loop(Config, Operation, Attempt, Total_delay) ->
Result = Operation(),
case Result of
{ok, _} ->
{retry_result, Result, Attempt + 1, Total_delay};
{error, Err} ->
Should_retry = ((Attempt < erlang:element(2, Config)) andalso anthropic@error:is_retryable(
Err
))
andalso (erlang:element(2, Config) > 0),
case Should_retry of
true ->
Delay = calculate_delay(Config, Attempt),
timer:sleep(Delay),
retry_loop(
Config,
Operation,
Attempt + 1,
Total_delay + Delay
);
false ->
{retry_result, Result, Attempt + 1, Total_delay}
end
end.
-file("src/anthropic/retry.gleam", 195).
?DOC(
" Execute a function with retry logic\n"
"\n"
" Returns the first successful result, or the last error after all retries\n"
" are exhausted.\n"
).
-spec retry_with_backoff(
retry_config(),
fun(() -> {ok, IAI} | {error, anthropic@error:anthropic_error()})
) -> retry_result(IAI).
retry_with_backoff(Config, Operation) ->
retry_loop(Config, Operation, 0, 0).
-file("src/anthropic/retry.gleam", 248).
?DOC(" Execute with retry and return just the result\n").
-spec retry(
retry_config(),
fun(() -> {ok, IAQ} | {error, anthropic@error:anthropic_error()})
) -> {ok, IAQ} | {error, anthropic@error:anthropic_error()}.
retry(Config, Operation) ->
{retry_result, Result, _, _} = retry_with_backoff(Config, Operation),
Result.
-file("src/anthropic/retry.gleam", 287).
-spec calculate_max_total_delay(retry_config(), integer(), integer()) -> integer().
calculate_max_total_delay(Config, Attempt, Total) ->
case Attempt >= erlang:element(2, Config) of
true ->
Total;
false ->
Delay = calculate_delay(Config, Attempt),
calculate_max_total_delay(Config, Attempt + 1, Total + Delay)
end.
-file("src/anthropic/retry.gleam", 282).
?DOC(" Get the maximum possible delay for a configuration\n").
-spec max_total_delay(retry_config()) -> integer().
max_total_delay(Config) ->
calculate_max_total_delay(Config, 0, 0).