Build an OpenAI-compatible client that stops at the right time
An OpenAI-compatible route is only the beginning. A production client still needs a retry boundary, a response-shape check, and a receipt that can be reconciled without leaking a key.
This is a small Python pattern for https://aiwave.live/v1/chat/completions. It intentionally focuses on the part most integrations skip: deciding when to stop.
The recovery contract
| Condition | Action |
|---|---|
401, 402, malformed request, unknown status |
Stop. Do not guess or retry. |
408, 429, 500, 502, 503, 504
|
Retry with a strict attempt cap. |
200 without choices
|
Reject it as incomplete. |
usable 200 with usage |
Return a sanitized receipt. |
RETRYABLE = {408, 429, 500, 502, 503, 504}
def request_chat(transport, payload, credential, max_attempts=3):
if not credential:
raise RuntimeError("missing credential")
headers = {
"Authorization": f"Bearer {credential}",
"Content-Type": "application/json",
}
for attempt in range(1, max_attempts + 1):
response = transport(payload, headers)
status = response.get("status")
body = response.get("body") or {}
if status == 200:
choices = body.get("choices")
if not isinstance(choices, list) or not choices:
raise RuntimeError("response missing choices")
return {
"model": body.get("model"),
"content": (choices[0].get("message") or {}).get("content", ""),
"usage": body.get("usage") or {},
"attempts": attempt,
}
if status not in RETRYABLE or attempt == max_attempts:
raise RuntimeError(f"request stopped at HTTP {status}")
raise AssertionError("unreachable")
Keep transport injected. You can now test failure behavior without a live credential:
$env:PYTHONPATH = (Resolve-Path 'api-recovery').Path
python -m unittest discover -s api-recovery/tests -v
The test suite covers a transient 503, immediate 401, incomplete 200, and an unknown status. The important property is not the number of retries. It is that each branch has an explicit terminal behavior.
A bounded live receipt
On 2026-10-19, one revocable-key non-streaming request was recorded in the AIWave Console with deepseek-v4-pro, 91 input / 16 output tokens, 2.0 seconds elapsed, and a $0.00024 request charge. That verifies one route and accounting path, not every model, account group, or workload.
The reply parsed, but a deliberately strict literal-content assertion did not match. The client stopped rather than retrying to obtain a prettier result. That is the behavior you want when a test tells you the response contract is not the contract you expected.
What to retain
Store only a small, sanitized receipt: model ID, status, attempt count, usage fields, elapsed time, and the request-level charge boundary. Keep API keys, full headers, private prompts, and account identifiers out of logs.
For async media routes, use a different contract: submission success is not terminal success. Poll, verify the stored result, and reconcile settlement before marking work complete.
Start with one request you can explain, then read the AIWave Chat Completions docs.



Top comments (0)