turion
The command line and Python SDK for Turion: fine-tune small models on your own data, serve them behind an OpenAI-compatible API, and run agents on them.
Install
pip install turion # or: uv tool install turion
turion --version
Python 3.11 or newer. The package contains both the turion command and the turion Python
module.
Log in
turion login
# Sign in through your browser. Check that it shows this code:
#
# KPRT-7WQM
#
# https://turion.ai/members/cli?code=KPRT-7WQM
#
# opened your browser
# logged in as acme (config: ~/.turion/config.json)
Approve the code in the browser (sign in with Google if asked) and the CLI gets its own API key.
Over SSH, add --no-browser and open the link on any device. In CI, create a key at
turion.ai/members/keys and set TURION_API_TOKEN, or pipe it
in: echo "$TURION_API_TOKEN" | turion login.
Quickstart
A training file is JSONL, one conversation per line that ends with the answer you want:
{"messages": [{"role": "user", "content": "I was charged twice for March."}, {"role": "assistant", "content": "{\"queue\": \"billing\", \"priority\": \"high\"}"}]}
Eval cases are the same, except that they end on the user's turn and put the answer in
expected (formats and matchers). Then:
turion bases # base models you can fine-tune, with methods and GPU memory
turion train --base gemma-4-e4b --data tickets.jsonl --evals evals.jsonl --name tickets
turion deploy acme/tickets # acme is your handle; scales to zero when idle
turion chat acme/tickets "Our card was charged three times this morning."
turion train validates and uploads the files, then follows the job to the end: the base model
and the fine-tune are scored on the same eval cases, so you see whether it got better. Ctrl-C
stops following, not training; turion jobs get ft_… shows where the job is.
A deployed model answers on an OpenAI-compatible endpoint, so the official SDK works as is:
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.turion.ai/v1", api_key=os.environ["TURION_API_TOKEN"])
reply = client.chat.completions.create(
model="acme/tickets",
messages=[{"role": "user", "content": "Our card was charged three times this morning."}],
)
print(reply.choices[0].message.content)
Compare it with a frontier model on the same cases, on your own provider key:
turion providers set openai
turion eval acme/tickets --evals evals.jsonl --against openai/gpt-6-luna
Agents
Start from a blueprint, run it on your machine, deploy it, and later distil its traced model calls into a small fine-tune of its own:
turion agent blueprints # what you can start from
turion agent new support-triage
cd support-triage
turion providers set openai # the blueprint starts on a frontier model
turion agent run --local -i "Where is my order A-1001?"
turion agent deploy
turion agent run support-triage -i "How long does express shipping take?"
turion agent distil support-triage # once enough model calls are traced
Decision models
A decision model answers typed questions about a piece of text, with probabilities:
turion providers set openrouter
turion decide openrouter/typesafe/jev-1.13 \
--state "I was charged twice for October and nobody answers my emails." \
--choice "team: which team should handle this? [billing|tech|sales|other]"
Train your own from decision data with turion train; the
decision models guide has the format.
Python
import turion
job = turion.train(base="gemma-4-e4b", data="tickets.jsonl", evals="evals.jsonl", name="tickets")
job.wait(on_progress=lambda j: print(j.status, j.phase))
job.raise_for_status()
print(job.eval.tuned) # 0.92: the tuned model's pass rate on the 50 eval cases
job.deploy()
client = turion.Client() # reads TURION_API_TOKEN or ~/.turion/config.json
reply = client.chat.completions.create(
"acme/tickets", [{"role": "user", "content": "Where is my order 1042?"}]
)
print(reply["choices"][0]["message"]["content"])
Responses are typed pydantic models. API errors raise turion.TurionError subclasses with a
code, a message and the HTTP status: AuthError (401), ForbiddenError (403),
NotFoundError (404), ConflictError (409), BadRequestError (400, 422), RateLimitError
(429) and ServerError (5xx). Each carries the API's request_id to quote to support, and a
failed chat or decide call the trace_id it was recorded under. A request answered 429 or 503,
or one that never reached the API, is sent again up to twice with backoff (a GET also after any
5xx); turion.Client(max_retries=0) turns that off.
Output and configuration
On a terminal the CLI draws tables, progress bars and a live view of a training job. Piped or in
CI it prints plain lines that grep and cut can read, and most commands take --json.
NO_COLOR=1 asks for the plain lines on a terminal too; TURION_NO_PROGRESS=1 keeps the tables
but drops spinners and bars.
| Variable | What it sets |
|---|---|
TURION_API_TOKEN |
the API key, instead of the one turion login saved |
TURION_BASE_URL |
the API, if not https://api.turion.ai |
TURION_CONFIG_PATH |
the config file, if not ~/.turion/config.json |
TURION_DEBUG |
1: full tracebacks when your agent fails in a local run or eval |
TURION_TRACE |
off: keep a local agent run out of the agent's traces and distil |
Pricing
Early access: sign in with Google and start free with 2 GPU-hours a month. Need more? Email
hello@turion.ai and we raise your limit; usage above the free allowance is invoiced monthly at the
list rates. The rates are on turion.ai/pricing, and turion usage
shows what you have used.
Documentation
Questions: hello@turion.ai.
Metadata
Release files for turion 0.3.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 | |
|---|---|---|---|
| turion-0.3.0.tar.gz | 448.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| turion-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 766.6 kB
Release files / turion-0.3.0.tar.gz
| Download URL | turion-0.3.0.tar.gz |
|---|---|
| Size | 448.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1f49127b21dcf12e707960118e23b2383ed9df8814420f5a7e1413dea56e6ece
|
|
BLAKE2b-256 checksum How to use checksums |
722ad947c5cb9f11e25d112e4c0f2716f2985e02234d789db9d3d2071599ad72
|
| 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 15, 2026.
Transparency logRelease files / turion-0.3.0-py3-none-any.whl
| Download URL | turion-0.3.0-py3-none-any.whl |
|---|---|
| Size | 317.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7720a6b58929fb00e511a7accef4421bbc387df120dfa70657960d6ed4ffcaad
|
|
BLAKE2b-256 checksum How to use checksums |
dd96855f42041d04193347863f2c454cc31bdeac463629f93244a108860d13a1
|
| 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 15, 2026.
Transparency log