Errors and Reliability

Exceptions

Every exception that the SDK raises is a SmarterError, so one except clause catches them all. The SDK follows the platform’s own rule that exceptions subclass one hierarchy (ADR-012).

SmarterError
├── SmarterConfigurationError        the client has no api key or base url
├── SmarterValueError                an argument is invalid, before any request is sent
├── ResourceNotFoundError            a resource was not found, by the API or by find()
├── SmarterPromptError               a streamed prompt failed
└── APIError
    ├── APIConnectionError           the request got no response
    │   └── APITimeoutError
    ├── APIResponseValidationError   the response doesn't match the SDK's model of it
    └── APIStatusError               the API returned a 4xx or 5xx status
        ├── BadRequestError          400
        ├── AuthenticationError      401
        ├── PermissionDeniedError    403
        ├── NotFoundError            404, also a ResourceNotFoundError
        ├── ConflictError            409
        ├── UnprocessableEntityError 422
        ├── RateLimitError           429
        └── InternalServerError      5xx

An APIStatusError carries the response’s status_code, a readable message, the parsed body, and, when the platform sent a journaled error, its error, whose errorClass (also error_class) is the name of the platform’s exception, e.g. SAMBrokerErrorNotFound.

from smarter import NotFoundError, SmarterError

try:
    client.manifests.describe("LLMClient", "no_such_client")
except NotFoundError as e:
    print(e.status_code, e.message, e.error_class)
except SmarterError as e:
    print("something else went wrong:", e)

An APIResponseValidationError usually means that the SDK and the platform are of incompatible versions. Compare client.version().api.version with the SDK’s smarter.__version__.

Retries and timeouts

A request that failed for a transient reason is retried, by default twice, with exponential backoff and jitter, honoring the response’s Retry-After. Transient means: no response, a timeout, or a 408, 429, 502 or 504 status. A 503 is retried only when it didn’t come from the platform itself, e.g. from a load balancer, because the platform answers 503 for a resource that isn’t ready, which a retry won’t change.

client = Smarter(max_retries=0, timeout=120)

Retrying a prompt can bill a prompt twice, if the first attempt reached the LLM. Set max_retries=0 where that matters more than resilience.

Logging

The SDK logs to the smarter logger hierarchy, and, as a library should, adds no handlers unless you ask it to. Set SMARTER_LOG=info to log each request’s method, url, status and duration to stderr, or SMARTER_LOG=debug to log the bodies too. Or configure the logger as any other:

import logging

logging.getLogger("smarter.client").setLevel(logging.DEBUG)

API keys are never logged: they are masked to their last four characters.