Python SDK
The official Python client. Sync and async clients (UniqOS / AsyncUniqOS), typed
errors, automatic retries with backoff, automatic idempotency keys, SSE streaming as a
plain iterator, fully type-hinted (py.typed), one runtime dependency (httpx),
Python 3.10+.
The PyPI package is uniqos.
Install
pip install uniqos==0.4.0
Configure
from uniqos import UniqOS
client = UniqOS(
api_key="uniq_test_...",
base_url="https://api.uniqos.ai", # host only; do NOT include /v1
timeout=90, # seconds; default 90 (0 disables)
max_retries=3, # 0 disables retries
log_level="warning", # debug | info | warning | error | silent
)
:::note Default timeout is 90s
The default timeout is 90 seconds, deliberately above the server's turn ceiling
(~75s), so a slow turn ends in the server's clean, unbilled 504 turn_timeout rather than
the client abandoning a turn the server may still bill. Lowering it below the server budget
takes on that risk. See 504 turn_timeout.
:::
The SDK detects the environment from the key prefix (uniq_live_ / uniq_test_) and
rejects a malformed key immediately. It never logs the full API key (only the prefix) nor
message content beyond 50 characters.
Call
result = client.respond(personality_id="pers_...", message="Hello!")
result["response"] # the agent's reply (parsed JSON, snake_case, mirroring the API)
result["inferred_emotional_state"]
The namespaces mirror the API: client.personalities, client.catalog,
client.vocabularies, client.end_users, client.relationships (+ .memory),
client.interactions, client.me, client.organizations, client.api_keys,
client.billing. For stateful turns, pass mode="stateful" and a user_id — see
Stateless vs stateful.
Async
from uniqos import AsyncUniqOS
async with AsyncUniqOS(api_key="uniq_test_...") as client:
result = await client.respond(personality_id="pers_...", message="Hello!")
Both clients expose the identical surface; the async one just awaits.
Stream
stream = client.respond(personality_id="pers_...", message="tell me a story", stream=True)
for event in stream:
if event.type == "text":
print(event.delta, end="", flush=True)
elif event.type == "completion":
print(f"\n[{event.latency_ms}ms]")
The five event types (metadata, text, guardrail_modulation, completion, error)
are typed dataclasses with attribute access. The async client uses async for.
Errors
from uniqos import RateLimitError, QuotaExhaustedError, TurnTimeoutError
try:
result = client.respond(personality_id="pers_...", message="hi")
except RateLimitError as err:
print(f"Rate limited. Retry after {err.retry_after_seconds}s")
except QuotaExhaustedError as err:
print(f"Quota exhausted: {err.details}")
except TurnTimeoutError:
print("Turn timed out (not billed); not auto-retried")
Every error carries code, message, request_id, http_status, and details. Catch
UniqOSError for anything the SDK can raise. It retries transient failures
(429 rate_limit_exceeded, 500 / 502 / 503, network) automatically with backoff; it
never retries quota_exhausted, 401, 403, or 504 turn_timeout. See
Errors.