Write a production-quality Python module for integrating the ScaleDown decisions API.
API details:
- Endpoint: POST https://api.scaledown.xyz/v1/scaledown
- Auth: HTTP header `x-api-key: <your key>`, read from the DECISIONS_API_KEY environment
variable if not passed explicitly.
- Request body: { "model"?: str, "state": { "text"?: str, "document"?: str,
"document_mime_type"?: str }, "questions": { name: { "type": "choice"|"noul"|"score",
"instructions"?: str, "criteria"?: { key: str } } } }. "criteria" is required for "choice"
questions and ignored otherwise. Either state.text or state.document is required. "model"
defaults to "classify-1" and is the ONLY accepted value - any other value is a 422.
- Response: { "model": str, "answers": { name: ChoiceAnswer | NoulAnswer | UnsupportedAnswer },
"usage": { "input_tokens": int, "output_tokens": int, "cost": float } }
- ChoiceAnswer: { "type": "choice", "choice": str, "probabilities": dict[str, float],
"confidence": float }
- NoulAnswer: { "type": "noul", "noul": float } # probability 0-1
- UnsupportedAnswer: { "type": "unsupported", "reason": str } # returned for a "score"
question ONLY when the request also has a choice/noul question - there is no
ordered-scale scoring in this API today. If EVERY question in the request is "score",
the whole request fails with 422 instead of returning any UnsupportedAnswer.
- Errors: 422 malformed/validation (including an unsupported "model" value, or a request made
entirely of "score" questions), 402 insufficient credits, 502 upstream model error,
504 timeout. Non-2xx responses include a JSON body with a "detail" field.
Requirements:
- Use dataclasses (or Pydantic if available) to model the request questions (Choice, Noul,
Score variants) and the typed answers (ChoiceAnswer, NoulAnswer, UnsupportedAnswer) so
callers get type-safe access rather than raw dicts, with a discriminator on "type".
- Implement retry with exponential backoff (starting at 1s, capped at 30s, with jitter) on
429 and 5xx responses. Do not retry on 4xx other than 429.
- Raise a typed exception hierarchy: DecisionsAuthError (401/403), DecisionsQuotaError (402),
DecisionsValidationError (422), DecisionsServerError (502/504/other 5xx).
- Log each request's latency and usage.cost at debug level.
- Include a synchronous class `DecisionsClient` with a method
`ask(state: dict, questions: dict, model: str | None = None) -> DecisionsResponse`.
- Include type hints throughout and a minimal usage example in a `if __name__ == "__main__":`
block showing a mixed choice + noul request.