Metadata-Version: 2.5
Name: turion
Version: 0.2.2
Summary: The Turion CLI and Python SDK: train small models on your data, serve them behind an OpenAI-compatible API, and run agents on them
Project-URL: Homepage, https://turion.ai
Project-URL: Documentation, https://turion.ai/docs/
Author-email: TURION UAB <hello@turion.ai>
License-Expression: LicenseRef-Proprietary
Keywords: agents,fine-tuning,gpu,inference,llm,machine-learning,openai-compatible,turion
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: openai>=1.40
Requires-Dist: pydantic>=2.7
Requires-Dist: rich>=13
Requires-Dist: typer>=0.12
Description-Content-Type: text/markdown

# turion

The command line and Python SDK for [Turion](https://turion.ai): fine-tune small models on your
own data, serve them behind an OpenAI-compatible API, and run agents on them.

## Install

```bash
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

```bash
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](https://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:

```json
{"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](https://turion.ai/docs/guides/evals/)). Then:

```bash
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:

```python
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:

```bash
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:

```bash
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:

```bash
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](https://turion.ai/docs/guides/decision-models/) has the format.

## Python

```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](https://turion.ai/pricing/), and `turion usage`
shows what you have used.

## Documentation

- [Quickstart](https://turion.ai/docs/getting-started/quickstart/)
- [CLI reference](https://turion.ai/docs/reference/cli/)
- [Python SDK](https://turion.ai/docs/reference/python-sdk/)
- [Agents](https://turion.ai/docs/guides/agents/)

Questions: hello@turion.ai.
