ainative-ai-sdk-provider
Python SDK for multi-provider AI inference — chat completions, streaming, and agentic tool-calling over one interface, with server-side routing and automatic fallback across Cerebras, Meta Llama, NVIDIA NIM & DigitalOcean.
AINative's backend routes every chat completion across Cerebras, Meta Llama, NVIDIA NIM, DigitalOcean and BYOK based on your plan tier, live per-provider rate-limit state, daily token and cost caps, and model availability — then transparently falls back through its chain when a provider is exhausted or geo-blocked. This SDK is the client for that. You ask for a model; the platform picks the upstream.
pip install ainative-ai-sdk-provider
Quick Start
from ainative_ai_sdk_provider import AINativeProvider
provider = AINativeProvider() # reads AINATIVE_API_KEY
result = provider.complete(
messages=[{"role": "user", "content": "Explain SSE in one sentence."}],
model="deepseek-4-flash",
)
print(result.text)
print(result.total_tokens, "tokens")
Why there is no provider= parameter
This is the deliberate design of the SDK, not a missing feature.
The server-side router picks an upstream per request. Pinning a provider from
the client would switch that off — no tier-aware selection, no rate-limit
awareness, no cost-aware ordering, no automatic fallback when an upstream 429s
or geo-blocks you. So the SDK never sends a provider field, which is exactly
the signal the endpoint needs to do its job.
Two consequences worth knowing:
result.provideris informational. The backend intentionally reports several internal upstreams as"ainative". Don't branch on it.- The SDK does not retry rate limits for you. The backend already walked
its own fallback chain before returning a 429 or 503, and tells you how long
to wait. Retrying immediately would fight its circuit breaker — honour
err.retry_afterinstead.
Authentication
# 1. Environment variable (recommended)
# export AINATIVE_API_KEY=your_key
provider = AINativeProvider()
# 2. Explicit
provider = AINativeProvider(api_key="your_key")
# 3. Point at another environment
provider = AINativeProvider(api_key="k", base_url="http://localhost:8000/api/v1")
# or: export AINATIVE_BASE_URL=...
The client sends your key as both X-API-Key and Authorization: Bearer,
since the platform's unified auth accepts either — so the same client works
against the public gateway and a directly-addressed backend without
reconfiguration.
Streaming
for chunk in provider.stream(
messages=[{"role": "user", "content": "Count to five."}],
model="deepseek-4-flash",
):
print(chunk.content, end="", flush=True)
Streaming is served by upstreams that speak OpenAI SSE natively. If the router lands your request on one that doesn't, the API returns a normal JSON body and this method yields a single chunk with the full completion — so your loop keeps working either way.
If an upstream fails mid-stream, the API injects an error frame; the SDK raises
UpstreamUnavailableError rather than silently truncating your output.
Async
Every method has an async counterpart. acomplete mirrors complete,
astream mirrors stream.
import asyncio
from ainative_ai_sdk_provider import AINativeProvider
async def main():
async with AINativeProvider() as provider:
result = await provider.acomplete(
messages=[{"role": "user", "content": "Hello!"}],
)
print(result.text)
async for chunk in provider.astream(
messages=[{"role": "user", "content": "Count to three."}],
):
print(chunk.content, end="")
asyncio.run(main())
Tool calling
The API runs an agentic tool-calling loop server-side, up to max_iterations.
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
result = provider.complete(
messages=[{"role": "user", "content": "Weather in Oakland?"}],
model="deepseek-4-flash",
tools=tools,
tool_choice="auto",
max_iterations=3,
)
if result.tool_calls:
for call in result.tool_calls:
print(call["function"]["name"], call["function"]["arguments"])
else:
print(result.text)
tool_subset=["get_weather"] restricts which registered server-side tools get
loaded for the request.
Error handling
Errors map onto the API's real failure modes, each carrying the payload the API supplies with it.
from ainative_ai_sdk_provider import (
AINativeAPIError,
AuthenticationError,
InsufficientCreditsError,
ModelDeprecatedError,
RateLimitError,
RegionUnavailableError,
UpstreamUnavailableError,
)
try:
result = provider.complete(messages=[{"role": "user", "content": "hi"}])
except InsufficientCreditsError as err:
# 402 — chat completions need a paid plan
print(err.details["upgrade"]["action"])
except RateLimitError as err:
# 429 — the backend already tried its fallback chain
print(f"Retry in {err.retry_after}s")
except RegionUnavailableError as err:
# 403 — model geo-blocked; the API names alternatives
print("Try instead:", err.details["suggested_models"])
except ModelDeprecatedError as err:
# 410 — model decommissioned, migrate
print(err.message)
except UpstreamUnavailableError as err:
# 502/503/504 — upstream overloaded, erroring, or timed out
print(f"Retry in {err.retry_after}s")
except AINativeAPIError as err:
print(err.status_code, err.code, err.message)
| Exception | Status | Meaning |
|---|---|---|
AuthenticationError |
401, 403 | Missing, invalid, or unauthorized API key |
InvalidRequestError |
400, 404, 422 | Malformed or rejected request |
InsufficientCreditsError |
402 | Chat completions require a paid plan |
RegionUnavailableError |
403 | Model geo-blocked; backend fallback also failed |
ModelDeprecatedError |
410 | Model decommissioned — migrate |
RateLimitError |
429 | Rate limited; honour retry_after |
UpstreamUnavailableError |
502, 503, 504 | Upstream overloaded/erroring/timed out |
ServerError |
500 | Unexpected server-side failure |
AINativeConnectionError |
— | API unreachable (DNS/TCP/TLS) |
AINativeTimeoutError |
— | Client-side timeout |
All of them subclass AINativeError; all HTTP ones subclass
AINativeAPIError, which exposes .status_code, .code, .message,
.details, .retry_after and .response.
API reference
AINativeProvider(api_key=None, *, base_url=None, timeout=180.0, client=None, async_client=None)
Pass client / async_client to reuse your own httpx client (connection
pooling, proxies, custom transports). A client you supply is yours to close —
the SDK won't close it for you.
The 180s default outlasts the endpoint's own 150s budget across provider fallbacks, so you receive the API's structured timeout error instead of the client aborting first.
complete(messages, *, model=None, temperature=None, max_tokens=None, system=None, tools=None, tool_choice=None, tool_subset=None, max_iterations=None, user_id=None, extra_body=None) -> ChatCompletion
messages is required and non-empty. temperature is bounded 0.0-2.0 and
max_iterations 1-10, both validated locally so mistakes fail before a round
trip. Unset parameters are omitted entirely, letting the API's own defaults
apply. extra_body merges extra fields for endpoint parameters newer than this
SDK version.
stream(...) -> Iterator[StreamChunk]
Same parameters minus the tool-calling ones — use complete for agentic loops.
acomplete(...) / astream(...)
Async equivalents.
ChatCompletion
| Field | Description |
|---|---|
text |
Assistant text from the first choice ("" on a tool-call-only reply) |
tool_calls |
Tool calls the model requested, or [] |
id, model, created |
Completion identity |
provider |
Provider label — informational only, see above |
finish_reason |
stop, tool_calls, … |
prompt_tokens, completion_tokens, total_tokens |
Usage |
iterations, total_tokens_used |
Tool-loop metadata, when reported |
model_substituted |
Set when the backend swapped your model for an equivalent |
choices, usage, raw |
Unprocessed response data |
StreamChunk
content (text fragment), finish_reason (final frame only), raw.
Scope (v0.1)
Implemented: chat completions — non-streaming and streaming, sync and async, server-side tool calling, the full error surface of the completions endpoint, and typed results.
Not yet: embeddings, the chat session endpoints (/chat/sessions/...
persistence and history), client-side BYOK key passthrough, and a published
model catalog. Those are separate endpoints and a separate surface — see
core#8066 for what a v2 would cover.
Development
pip install -e ".[dev]"
pytest tests/ -v --cov=ainative_ai_sdk_provider --cov-report=term-missing
Tests are fully mocked with respx — no live API calls and no API key needed.
License
MIT
Metadata
Release files for ainative-ai-sdk-provider 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ainative_ai_sdk_provider-0.1.0.tar.gz | 27.0 kB | Without provenance | Details for ainative_ai_sdk_provider-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ainative_ai_sdk_provider-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for ainative_ai_sdk_provider-0.1.0-py3-none-any.whl |
Total release size: 45.1 kB
Release files / ainative_ai_sdk_provider-0.1.0.tar.gz
| Download URL | ainative_ai_sdk_provider-0.1.0.tar.gz |
|---|---|
| Size | 27.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
71d68555f74f9d4c041b7311e6cae2e16eaf750d1992f20923a980ea43300fd4
|
|
BLAKE2b-256 checksum How to use checksums |
d30656c0ec914f71af46784fae867e552f2442572e2a88a8c9b535b7a2493812
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.2
|
Release files / ainative_ai_sdk_provider-0.1.0-py3-none-any.whl
| Download URL | ainative_ai_sdk_provider-0.1.0-py3-none-any.whl |
|---|---|
| Size | 18.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4d96985417a275215e5fdf1c55b34472046297ec866dcbc4d0067c344a16f147
|
|
BLAKE2b-256 checksum How to use checksums |
adbb522c7b272a3c1989082fb7b7a5bf3ce0bbdea80ecfd8643aa08e39271a1c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.2
|
Security for ainative-ai-sdk-provider 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.