Skip to main content

intura-banner

intura-ai

PyPI version License: MIT

Run the Intura AI models you own on your own CPU.

Intura makes lightweight fine-tuned models for specific tasks, like routing a customer-support ticket to the right team or tagging what a social post is asking for. You buy a model once, which gets you every version of it. This package downloads the model and runs it in your process. After the download there is no network call, no per-request price, and your text never leaves your machine.

from intura_ai import Intura

model = Intura().models.load("chat-customer-support-ticket-en")
model.predict("I was charged twice, please refund me")
# {'labels': ['billing_payment', 'refund_return'],
#  'scores': {'billing_payment': 0.6956, 'refund_return': 0.8482, ...}}

Install

pip install intura-ai
# or
uv add intura-ai

Python 3.10–3.13. This installs everything a prediction needs: onnxruntime (CPU), tokenizers, numpy and httpx for the download. The old local extra still resolves, so pip install "intura-ai[local]" keeps working, but it adds nothing.

Get a download key

Get a model from https://ai.intura.co/models, then create a download key on your account page. Give it to the client as an argument, or in INTURA_API_KEY:

export INTURA_API_KEY=sk_...
from intura_ai import Intura

client = Intura()                  # reads INTURA_API_KEY
client = Intura(api_key="sk_...")  # or pass it explicitly

The key is only used to download. Intura() works without one, and so does loading a model that is already downloaded. The first call that needs the API raises MissingAPIKey and tells you where to get a key.

Load a model, then predict

model = client.models.load("chat-customer-support-ticket-id")

model.predict("kak, saya sudah bayar tapi pesanan belum dikirim")
# {'labels': ['order_delivery'], 'scores': {'billing_payment': 0.0056, 'order_delivery': 0.9932, ...}}

[r["labels"] for r in model.predict_batch(["tidak bisa login, kode OTP tidak masuk", "terima kasih kak"])]
# [['account_access'], []]

On first use, load downloads the model into a local cache. After that it loads from disk in well under a second. Load once per process and keep the model. A loaded model holds no connection and no key, and you can share it across threads.

predict_batch(texts) gives the same result as [model.predict(t) for t in texts], in the same order. It runs one text per forward pass on purpose. The published models are INT8 graphs with dynamic quantization, which scales each activation tensor by its own range. A padded batch is one tensor, so batching would shift every item's logits, sometimes by more than a whole logit, and that can flip a decision near a cut. For throughput, call predict_batch from several threads (onnxruntime releases the GIL while it computes).

You can find each model's slug in client.library.list(), on its page on the website, or by running intura-ai library.

What predict returns

Each model carries its own decision rule and cut-offs in its serving.json. predict applies them, so a new version that moves a threshold gives the right answer as soon as it is downloaded. These are the same five rules as the plain-Python script on each model page:

Rule (decision) Models predict(text) returns
Multi-label (sigmoid-per-label) customer-support ticket (id, en), Threads post intent {"labels", "scores"}. Every label whose own sigmoid reaches threshold (or that label's entry in thresholds, when the model has one), plus a score for each label. The scores do not sum to one. No label past the cut gives "labels": []: for a support ticket, a message that asks for nothing.
Argmax with fallback (argmax-with-fallback, argmax) — {"label", "score"}. The top class and its probability, or the model's fallback label (e.g. "other") when the top class scores below threshold.
One positive class (positive-class) — {"label", "score"}. score is always the positive class's probability. label is the positive class at or above threshold, otherwise the other label.
Risk sum with bands (risk-sum-with-bands) — {"label", "decision", "score"}. score is the summed probability of the risk classes. decision is "block" at or above threshold, "review" at or above review_threshold, otherwise "allow".
Token classification — A list of {"type", "start", "end", "value"} spans, in text order. text[start:end] == value.

The rule comes from the model's own files, never from its name, so a model released after your version of this package still works. A decision name the package has never seen is read from the keys the model carries instead (positive_label, risk_labels, otherwise the top class).

Scores are rounded to four decimals. To apply a rule of your own, such as a stricter cut for one label, model.scores(text) gives every label's unrounded probability with no cut applied. model.labels, model.serving (the whole serving.json), model.task and model.version are there too.

posts = client.models.load("chat-threads-post-intent-id")
posts.predict("kecewa banget, paket udah seminggu belum sampai")["labels"]
# ['complaint', 'review_experience']

scores = posts.scores("rekomendasi sunscreen buat kulit berminyak dong")
[label for label, p in scores.items() if p >= 0.9]
# ['ask_recommendation']

Versions and the cache

Buying a model gets you every version of it. load decides which one to use like this:

Call What it loads Network
load(slug) The newest version already in the cache. If none is cached, the recommended version, downloaded first. Only when nothing is cached
load(slug, version="v1.0") Exactly that version, downloaded if it is missing Only when that version is missing
load(slug, refresh=True) Asks which version is recommended now and downloads it if you don't have it Always (one small call, plus the download if the version is new)

So once a model is cached, load(slug) works offline and without a key. Nothing updates behind your back. Run with refresh=True (or intura-ai predict --refresh) when you want the latest. Older versions stay in the cache, so you can pin back to one with version=.

The cache is laid out <cache>/<slug>/<version>/. Its location is the first of these that is set:

  1. load(..., cache_dir="./models")
  2. $INTURA_CACHE_DIR
  3. $XDG_CACHE_HOME/intura
  4. ~/.cache/intura

A partly downloaded model is never mistaken for a complete one. Every download goes into a hidden staging folder next to its destination. That folder is renamed to <version>/ only after every file has arrived at the size the bucket reports. If a download is interrupted (dropped connection, Ctrl-C, full disk), no version folder is left behind, and the next load starts again. To free space, delete a version folder, or the whole cache.

To put a model in a folder of your own choosing, for example in a container image or on a machine with no internet access, use download, then load that folder:

from intura_ai import LocalModel

path = client.models.download("chat-customer-support-ticket-id", "./models", version="v1.0")
# ./models/chat-customer-support-ticket-id/v1.0/
model = LocalModel.load(path)

download is atomic in the same way. It keeps a complete bundle that is already there, unless you pass overwrite=True. client.models.links(slug) returns the signed URLs without downloading anything, if you want to use your own downloader.

The download key only signs the links. It authenticates one call to POST /v1/platform/downloads/models/{slug}/download, which answers with GET URLs on Cloud Storage that are valid for 15 minutes. The files come straight from Cloud Storage, and the key is never sent there. If a link expires partway through a download, it is re-signed once.

Your library

for item in client.library.list():
    print(item.code, item.kind, item.current_version, item.versions)
# chat-customer-support-ticket-id  model     v1.0  ('v1.0',)
# template-support-reply       template  None        ()

This is the same list as the library page on the website: models, templates, courses and apps. For a model, code is the slug you pass to load, and versions lists every build you can pin. Everything the service sent is also on item.data. client.models.list() lists the downloadable models, with the recommended version of each.

Async

from intura_ai import AsyncIntura

async with AsyncIntura() as client:
    model = await client.models.load("chat-customer-support-ticket-id")

model.predict("ongkirnya berapa?")

Only the API calls and the download are awaited. Reading the model from disk happens once and is synchronous, and predict is the same CPU-bound LocalModel method as in the sync client. Inside an event loop, run a large batch with await asyncio.to_thread(model.predict_batch, texts).

CLI

$ export INTURA_API_KEY=sk_...
$ intura-ai library                                   # what your account owns
$ intura-ai predict chat-customer-support-ticket-en "I was charged twice, please refund me"
$ intura-ai predict chat-customer-support-ticket-id "paket belum sampai" "mau refund" --field labels
$ cat posts.txt | intura-ai predict chat-threads-post-intent-id --field labels
$ intura-ai predict ./models/chat-customer-support-ticket-id/v1.0 "..."  # a downloaded folder
$ intura-ai predict chat-threads-post-intent-id "..." --refresh          # fetch the newest
$ intura-ai download chat-customer-support-ticket-id --dir ./models --version v1.0
$ intura-ai models                                    # what you may download

predict loads the model the same way client.models.load does, so the first run downloads it and later runs work offline. With one text, it prints the result as JSON. With several texts, given as arguments or one per line on stdin, it prints one compact JSON value per line. --field labels (or label, decision, 0.type, ...) prints a single value instead. Errors exit non-zero with one line on stderr.

Errors

Everything this package raises descends from InturaError. Once a model is loaded, predict raises nothing from this list, because it runs in your process.

Exception When
MissingAPIKey A download was needed and no key is set
AuthenticationError The key is wrong or revoked
NotFound A model you don't own, or a version that doesn't exist
RateLimited Too many calls in a short time. Retried for you first, and retry_after says when to try again
TransferError Cloud Storage refused or dropped a file. Nothing was cached, so it's safe to retry
APIConnectionError / APITimeoutError The API could not be reached. A model already in the cache still loads

LocalModel.load raises FileNotFoundError for a folder that isn't a downloaded model.

Configuration

Setting Argument Environment
Download key Intura(api_key=...) INTURA_API_KEY
Cache folder load(..., cache_dir=...) INTURA_CACHE_DIR, then XDG_CACHE_HOME
API root Intura(base_url=...) INTURA_BASE_URL (default https://ai.intura.co/api)

For a proxy or a custom transport, bring your own httpx client. The package sets the URL and headers on each request, and never closes a client it did not open:

client = Intura(http_client=httpx.Client(proxy="http://proxy:3128"))

Upgrading from 2.x

3.0 is only "download a model you own and run it". The hosted endpoints are removed, along with their result types:

2.x 3.0
client.guardrails.pii_detection.mask(text) client.models.load("guardrails-pii-detection-id").predict(text) returns the spans; mask them yourself
client.guardrails.prompt_injection.detect(text=...) client.models.load("guardrails-prompt-injection-id").predict(text)
client.fraud.chat_scam.detect(message) / email_phishing.detect(body) load("fraud-chat-scam-id") / load("fraud-email-phishing-en"), then .predict(text)
client.chat.commerce_intent.classify(message) client.models.load("chat-commerce-intent-id").predict(message)
client.documents.*, client.storage.upload removed, no local successor yet
Inference, PromptInjectionVerdict, PIIResult, PhishingVerdict, ScamVerdict, IntentResult, DocumentVerdict, Extraction, EntitySpan removed. predict returns plain dicts (see What predict returns)
verdict.thresholds, thresholds= per call The cut-offs are in model.serving; apply your own rule to model.labels if you need different ones
client.verify_key(), client.info(), client.health(), Intura.endpoints() removed. client.library.list() checks a key
QuotaExceeded, PayloadTooLarge, UnsupportedMediaType removed
Intura() without a key raised constructs. The first call that needs the key raises
pip install "intura-ai[local]" pip install intura-ai (the extra is now empty)
CLI run, info, health, verify, endpoints intura-ai predict, library, models, download

client.models.list(), links(), download(), client.library.list() and LocalModel.load(path).predict(text) work as they did in 2.2. Pin intura-ai<3 if you still need the 2.x client while you move.

Help

MIT licensed.

Metadata

Release files for intura-ai 3.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for intura-ai 3.0.0
File Size Uploaded Provenance
intura_ai-3.0.0.tar.gz 495.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for intura-ai 3.0.0
File Interpreter ABI Platform
intura_ai-3.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 535.7 kB

Release files / intura_ai-3.0.0.tar.gz

Download URL intura_ai-3.0.0.tar.gz
Size 495.1 kB
Tags Source
SHA-256 checksum
How to use checksums
3fcbda03a3126b17bda445ce80c1ec0252c190d908430ac7a7b8f4b64e119ff6
BLAKE2b-256 checksum
How to use checksums
3b35f1e9a5adb1594df9a78b62d62062f094e64d73d927e482abef60a474ceab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / intura_ai-3.0.0-py3-none-any.whl

Download URL intura_ai-3.0.0-py3-none-any.whl
Size 40.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7fcd580e884ac2f537066c916f34cf399a5a88fce7d20f2ee42be7dc6bd5374f
BLAKE2b-256 checksum
How to use checksums
a8bfe581bf6661ef1a0b01fd180693253744b11f7f1595b1e338c53d8f528792
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Security for intura-ai 3.0.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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for intura_ai-3.0.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

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.

Release history Release notifications | RSS feed

This release

3.0.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page