Skip to main content

AgniPod Python SDK

AgniPod is a fully managed internal LLM inference platform. You name a model; the platform provisions GPUs, schedules the work, routes it, retries on failure, records metrics and reclaims idle capacity. There is no "pick a provider", no "start an instance", no infrastructure surface.

pip install agnipod
import agnipod

client = agnipod.AgniPod()          # reads AGNIPOD_API_KEY

r = client.generate.create(
    model="qwen3:4b",
    prompt="Name three primary colours.",
    max_tokens=512,
    enable_thinking=False,          # fast, direct answer
)
print(r.content)                    # the answer
print(r.reasoning)                  # thinking (empty when disabled)
print(r.finish_reason, r.truncated) # "stop" / "length", bool
print(r.metrics["tokens_per_second"])

Credentials

Two, and they are not interchangeable.

Credential Grants Used by
API token (the fixed internal token) inference, models, batches every program
Admin token (a console JWT) everything under client.admin operations tooling

There is no username and password. The token is issued out of band; agnipod login verifies it against the platform and stores it at ~/.agnipod/credentials.json with mode 0600, so it never has to live in shell history or a dotfile:

$ agnipod login                 # prompts (no echo), verifies, stores
$ agnipod login --admin         # the console JWT, for client.admin
$ agnipod status                # what is configured, and where it came from
$ agnipod logout                # remove it again

Resolution order, for both credentials:

AgniPod(api_key=…, admin_token=…)      explicit, always wins
AGNIPOD_API_KEY / AGNIPOD_ADMIN_TOKEN  environment
~/.agnipod/credentials.json            agnipod login

The environment deliberately outranks the stored file — otherwise an agnipod login a developer ran months ago would silently override what CI exports, which is exactly the "works here, 401 there" failure the ordering exists to prevent. agnipod status names the winning source for that reason.

client = agnipod.AgniPod(api_key="…", admin_token="…")

# Rotating an admin token mid-process, without rebuilding the client:
client.admin.use_token("<new jwt>")

The separation is the safety property: an API-token holder can run inference and submit batches, and only an admin token can terminate instances, delete models or disable a provider. AGNIPOD_BASE_URL overrides the endpoint.


Inference

# Single prompt
r = client.generate.create(model="qwen3:4b", prompt="…", max_tokens=1024)

# Multi-turn. History is managed for you: if the transcript would exceed the
# instance's context window, the oldest user/assistant pairs are dropped
# (system messages and the latest turn are always kept) so the request fits.
c = client.chat.create(model="qwen3:4b", messages=[
    {"role": "system", "content": "You are helpful."},
    {"role": "user", "content": "Hi"},
])

Sampling parameters (temperature, top_p, top_k, min_p, repeat_penalty, presence_penalty, frequency_penalty, seed, stop) are ordinary keyword arguments.

enable_thinking — reasoning models (Qwen3, DeepSeek-R1) think before answering. False gives a fast, direct answer; omit it for the model's trained default. On a short max_tokens budget the monologue can consume the whole response, so it is worth setting deliberately.

Recoverable conditions arrive as HTTP 200 with an error object, so a caller can retry rather than treat a transient state as a failure:

r = client.generate.create(model="qwen3:4b", prompt="…")
if r.error:
    print(r.error["type"])   # service_unavailable | context_expanding | …

Streaming is not offered, because the platform does not implement it. The parameter used to exist and was silently ignored, which is worse than its absence.


Batches

batch = client.batches.create(
    items=[{"custom_id": "1", "model": "qwen3:4b", "prompt": "…"}],
    batch_name="nightly-scoring",
    priority=5,                      # 1 urgent … 9 whenever
    deadline="2026-08-01 02:00:00",  # escalates priority as it nears
    not_before="2026-07-31 22:00:00" # park for an off-peak window
)

client.batches.wait(batch.batch_id, on_progress=lambda p: print(p.status))

raw = client.batches.results(batch.batch_id)          # bytes
client.batches.results(batch.batch_id, save_to="out.jsonl")   # or stream to disk

Also accepts file_path= (read locally) or url= (fetched server-side). Max 10,000 items inline.

Durability. A batch's input file is its work queue: as each result comes back the item is removed from the input and appended to the output. If the GPU serving it dies, the platform re-provisions and continues from the remaining items — completed work is never re-run, and the delivered file has exactly one result per custom_id.


Administration

client.admin covers every endpoint the operations console uses — the same routes, so the two can never drift.

admin = client.admin

# Is anything wrong right now? One database round trip; no provider API is
# called, so this stays fast when a provider is not.
o = admin.overview()
for a in o.critical:
    print(a["message"])

# Can the platform actually serve a request? Distinct from a liveness check:
# a revoked provider credential leaves /health green while provisioning is
# impossible.
for check in admin.diagnostics().failures:
    print(check["name"], check["message"])
Namespace What it reaches
admin.overview() .diagnostics() .metrics() .analytics() .flush_metrics() aggregate views
admin.instances list, detail, metrics, compare, provider info, capabilities, directives, events, model change, destroy, stuck, sweep, blacklist
admin.models full CRUD plus upload
admin.providers list, configure, enable/disable, test credential, reset circuit breaker
admin.benchmarks learned GPU profiles, facets, reset
admin.requests request history, facets, terminations
admin.events operational event log and summary
admin.batches reschedule, requeue, operational detail
admin.config effective configuration

A few behaviours worth knowing:

# Serving, billed, and excluded from routing — the state no status column
# reveals and the one most worth acting on.
for i in admin.instances.list():
    if i.stranded:
        admin.instances.destroy(i.id)

# Enabled is not the same as usable: a row with no driver, an open circuit
# breaker or an exhausted balance is enabled and still never rented from.
for p in admin.providers.list():
    if not p.usable:
        print(p.name, p.health)

# Batch traffic is sampled, so a row count is not a request count. Both are
# reported rather than one being passed off as the other.
page = admin.requests.list(success=False, window_hours=24)
print(page.total, "rows /", page.executions, "executions")

# Only what you pass is written — an absent key means "leave alone", so this
# cannot clobber a concurrent edit.
admin.models.update(7, context_length=16384, has_template=False)

Runnable scripts are in examples/: inference, batches, operations, and a model lifecycle walkthrough.


CLI

agnipod status         # which credentials are configured
agnipod models         # what can be named as `model`
agnipod health         # fleet, spend and anomalies      (admin token)
agnipod diagnostics    # every dependency, with remedies (admin token)

Errors

Everything derives from AgniPodError, so one except catches the lot.

Exception When
AuthenticationError 401 — credential missing, wrong, or the wrong kind
PermissionDeniedError 403 — signed in without llm.manage
NotFoundError 404
ConflictError 409 — e.g. a model whose upload never finished
RateLimitError 429
ServiceUnavailableError 503 — no worker available yet, retry
ServerError 5xx
APIConnectionError / APITimeoutError network
ValidationError rejected client-side, never sent
ConfigurationError missing credential or bad base URL

Transient statuses (429, 5xx) are retried automatically with exponential back-off; max_retries controls how many times.


What the platform does for you

  • Provisioning & selection. GPUs are chosen by measured throughput-per-dollar (memory bandwidth is the decode bottleneck), preferring cheap community-cloud capacity, tuned by each GPU's historical efficiency and startup reliability — so scheduling improves over time.
  • Context sizing. Instances are provisioned at the model's context and each request uses only what it needs; the instance grows its window on demand.
  • Concurrency. Each instance runs exactly its GPU's decode slots; interactive requests always keep a reserved slot so a large batch never blocks them.
  • Failure handling. Stuck or dead instances are detected and replaced; in-flight batches resume; nothing is left billing.
  • Cost control. Idle instances are reclaimed lowest-performer-first; no persistent storage is rented; a per-hour price ceiling is enforced.

See ARCHITECTURE.md in the platform repository for the internals.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agnipod-0.5.0.tar.gz (50.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agnipod-0.5.0-py3-none-any.whl (47.9 kB view details)

Uploaded Python 3

File details

Details for the file agnipod-0.5.0.tar.gz.

File metadata

  • Download URL: agnipod-0.5.0.tar.gz
  • Upload date:
  • Size: 50.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for agnipod-0.5.0.tar.gz
Algorithm Hash digest
SHA256 bb23946d24014a82e6626f7ec0746dcfd428b6b596f73c709f6411f2cf9b74c6
MD5 04b645fb57a2a4a4527df9d0e36a2336
BLAKE2b-256 f545f9420816ed428ac3f9114745651170a93cdb76d6b90ce631b22119c53e47

See more details on using hashes here.

File details

Details for the file agnipod-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: agnipod-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 47.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for agnipod-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ed62ed575ade0ee116e395d039b7da899efc62b1409cfd37e54827059b3ba660
MD5 df50164f08ff72e2bff3099cb3fb5121
BLAKE2b-256 0f511ef778e3bc7516c79f4e2a3d66581169e036bcc01d62c1f8076b989ce88b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page