sdk-runtime

    API-agnostic client runtime for MoonBit SDKs: error taxonomy, Retry-After and backoff, tri-state JSON fields, open enums. Built on gaato/http; no async runtime dependency.

    sdk
    http
    retry
    rate-limit
    pagination
    json
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    yesterday
    Downloads
    16

    Dependencies

    #gaato/sdk-runtime

    API-agnostic client runtime for MoonBit SDKs, built on gaato/http: an error taxonomy (SdkError), Retry-After / retry-after-ms parsing, backoff and retry policy, a Client that resolves URLs, applies default headers and auth, runs a rate limiter and middleware, classifies responses and retries; a window rate limiter, a paginator, tri-state JSON fields (Presence), open enums and a multipart writer (gaato/sdk-runtime/json, gaato/sdk-runtime/multipart). No async runtime dependency: time comes through the Clock trait.

    Unofficial and experimental. Source, issues and design notes: https://github.com/gaato/mbt-sdk

    RateLimiter

    pub(open) trait RateLimiter {
    async fn acquire(Self, String) -> Unit noraise
    fn observe(Self, String, Int,
    Headers
    , Int64) -> Unit
    }

    A per-bucket admission policy updated from HTTP response headers.

    SdkError

    pub(all) suberror SdkError {
    Transport(
    HttpError
    )
    Status(status~ : Int, headers~ :
    Headers
    , body~ : Bytes)
    RateLimited(retry_after_ms~ : Int?, headers~ :
    Headers
    , body~ : Bytes)
    Decode(message~ : String, body~ : Bytes)
    Config(String)
    } derive(
    Debug
    )

    Failures distinguished by transport, HTTP status, decoding, or configuration.

    SdkError::is_retryable

    fn SdkError::is_retryable(self : SdkError) -> Bool

    Reports transient failures; request idempotency remains the caller's concern.

    SdkError::status

    fn SdkError::status(self : SdkError) -> Int?

    Returns an HTTP status only for failures with an HTTP status.

    SdkError::to_repr

    Formats the classified failure for debugging.

    Auth

    pub(all) enum Auth {
    NoAuth
    Bearer(String)
    Header(String, String)
    }

    Request authentication. Debug output always redacts credential values.
    impl Debug for Auth

    Auth::apply

    Adds authentication only when the request has no header with the same name.

    test {
    let request = @runtime.Bearer("secret").apply(@http.Request::get("/"))
    assert_eq(request.headers.get("authorization"), Some("Bearer secret"))
    }

    Auth::to_repr

    Redacted debug representation of authentication settings.

    Backoff

    pub(all) struct Backoff {
    base_ms : Int
    max_ms : Int
    factor : Double
    jitter : Double
    } derive(Eq,
    Debug
    )

    Parameters for pure exponential backoff with symmetric jitter.

    Backoff::default

    fn Backoff::default() -> Backoff

    Uses 500ms base, 30000ms cap, factor 2, and jitter 0.2.

    Backoff::delay_ms

    fn Backoff::delay_ms(self : Backoff, attempt : Int, random : Double) -> Int

    Computes a capped delay, truncating fractional milliseconds. Negative attempts behave as zero; random is clamped into [0, 1).

    test {
    assert_eq(@runtime.Backoff::default().delay_ms(2, 0.5), 2000)
    }

    Backoff::equal

    fn Backoff::equal(Backoff, Backoff) -> Bool

    Compares backoff parameters.

    Backoff::not_equal

    fn Backoff::not_equal(x : Backoff, y : Backoff) -> Bool

    Compares backoff parameters.

    Backoff::to_repr

    Formats backoff parameters for debugging.

    Client

    pub struct Client {
    // private fields
    }

    A transport-independent SDK client with authentication, retry, and limits.

    Client::new

    Creates a client from injected transport and clock implementations.

    Client::send

    async fn Client::send(self : Client, request :
    Request
    , bucket? : String) ->
    Response
    raise SdkError

    Sends a buffered request, retrying only as directed by RetryPolicy.

    Client::send_json

    async fn Client::send_json(self : Client, request :
    Request
    , bucket? : String) -> Json raise SdkError

    Sends and decodes JSON; an empty successful body is JSON null.

    Client::send_stream

    async fn Client::send_stream(self : Client, request :
    Request
    , bucket? : String) -> (
    ResponseHead
    , &
    BodyStream
    ) raise SdkError

    Sends a streaming request. A successful body is returned to the caller; an unsuccessful body is read up to 1 MiB, closed, classified, and retried.

    NoLimiter

    pub struct NoLimiter {
    }

    A limiter that never delays requests and ignores observations.

    NoLimiter::acquire

    async fn NoLimiter::acquire(_self : NoLimiter, _bucket : String) -> Unit noraise

    Explicit no-op limiter methods.

    NoLimiter::new

    fn NoLimiter::new() -> NoLimiter

    Creates a limiter that admits every request.

    NoLimiter::observe

    fn NoLimiter::observe(_self : NoLimiter, _bucket : String, _status : Int, _headers :
    Headers
    , _now_unix_ms : Int64) -> Unit

    Explicit no-op limiter methods.

    Page

    pub(all) struct Page[T] {
    items : Array[T]
    next : String?
    }

    One decoded page and its optional cursor for the following page.

    Paginator

    pub struct Paginator[T] {
    // private fields
    }

    A stateful async cursor paginator.

    Paginator::collect

    async fn[T] Paginator::collect(self : Paginator[T], max~ : Int) -> Array[T] raise SdkError

    Collects at most max remaining items, discarding the unused page suffix.

    Paginator::each

    async fn[T] Paginator::each(self : Paginator[T], f : async (T) -> Unit raise SdkError) -> Unit raise SdkError

    Visits every remaining item in page order.

    Paginator::new

    fn[T] Paginator::new(fetch : async (String?) -> Page[T] raise SdkError) -> Paginator[T]

    Creates a paginator whose first fetch receives None.

    Paginator::next_page

    async fn[T] Paginator::next_page(self : Paginator[T]) -> Array[T]? raise SdkError

    Fetches the next page. A failed fetch leaves the cursor unchanged.

    RateLimitState

    pub(all) struct RateLimitState {
    remaining : Int
    reset_at_unix_ms : Int64
    } derive(Eq,
    Debug
    )

    Remaining requests and the Unix millisecond instant when a window resets.

    RateLimitState::equal

    Equality for rate-limit states.

    RateLimitState::not_equal

    fn RateLimitState::not_equal(x : RateLimitState, y : RateLimitState) -> Bool

    Equality for rate-limit states.

    RateLimitState::to_repr

    Debug representation of rate-limit states.

    RetryPolicy

    pub(all) struct RetryPolicy {
    max_retries : Int
    backoff : Backoff
    max_retry_after_ms : Int
    retry_non_idempotent : Bool
    } derive(Eq,
    Debug
    )

    Retry limits, backoff, Retry-After cap, and non-idempotent policy.

    RetryPolicy::default

    fn RetryPolicy::default() -> RetryPolicy

    Uses two retries, default backoff, a 60-second server-delay cap, and only retries non-idempotent requests when the failure proves they were not run.

    RetryPolicy::equal

    fn RetryPolicy::equal(RetryPolicy, RetryPolicy) -> Bool

    Equality for retry policy values.

    RetryPolicy::next_delay_ms

    fn RetryPolicy::next_delay_ms(self : RetryPolicy, error : SdkError, request :
    Request
    , attempt : Int, random : Double) -> Int?

    Purely decides whether to retry and, if so, how long to wait.

    RetryPolicy::none

    fn RetryPolicy::none() -> RetryPolicy

    Disables retries while retaining the other default settings.

    RetryPolicy::not_equal

    fn RetryPolicy::not_equal(x : RetryPolicy, y : RetryPolicy) -> Bool

    Equality for retry policy values.

    RetryPolicy::to_repr

    Debug representation of retry policy values.

    WindowLimiter

    pub struct WindowLimiter {
    // private fields
    }

    A mutable per-bucket fixed-window limiter driven by an injected clock.

    WindowLimiter::acquire

    async fn WindowLimiter::acquire(self : WindowLimiter, bucket : String) -> Unit noraise

    Explicit fixed-window limiter methods.

    WindowLimiter::new

    Creates a limiter using an API-specific response-header parser.

    WindowLimiter::observe

    fn WindowLimiter::observe(self : WindowLimiter, bucket : String, status : Int, headers :
    Headers
    , now_unix_ms : Int64) -> Unit

    Explicit fixed-window limiter methods.

    WindowLimiter::state

    fn WindowLimiter::state(self : WindowLimiter, bucket : String) -> RateLimitState?

    Returns the currently recorded state for a bucket.

    classify

    fn classify(response :
    Response
    , now_unix_ms? : Int64) ->
    Response
    raise SdkError

    Returns successful responses unchanged and classifies all other statuses.

    test {
    let response : @http.Response = {
    status: 204,
    headers: @http.Headers::new(),
    body: b"",
    }
    assert_eq(@runtime.classify(response), response)
    let error = @runtime.Transport(@http.Timeout(500))
    assert_eq(error.status(), None)
    assert_true(error.is_retryable())
    }

    is_idempotent

    fn is_idempotent(request :
    Request
    ) -> Bool

    Reports whether the method is idempotent or an idempotency key is present.
    fn parse_link_next(headers :
    Headers
    ) -> String?

    Returns the target of the first RFC 8288 Link value whose relation includes next, across repeated headers and comma-separated link values.

    parse_retry_after

    fn parse_retry_after(value : String, now_unix_ms? : Int64) -> Int?

    Parses nonnegative seconds (including decimals) or an IMF-fixdate with a clock. Results are truncated to milliseconds and saturated at Int max.

    test {
    assert_eq(@runtime.parse_retry_after("1.2349"), Some(1234))
    }

    rate_limit_headers

    fn rate_limit_headers(remaining~ : String, reset_after_seconds? : String, reset_unix_seconds? : String) -> ((
    Headers
    , Int64) -> RateLimitState?)

    Builds a parser for conventional remaining/reset response headers.

    retry_after_ms

    fn retry_after_ms(headers :
    Headers
    , now_unix_ms? : Int64) -> Int?

    Prefers the first retry-after-ms value; falls back to retry-after when that header is absent or unparseable, as the official OpenAI SDKs do.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io