# SDKs and CLI

> The Python and TypeScript SDKs, the command line tool and the VS Code extension, with downloads, configuration, retries, idempotency and errors.

Dex has two SDKs, one for Python and one for TypeScript, a command line tool and a VS Code extension. They are thin clients over the same API contract: requests look like the JSON you would send with curl, and answers come back as typed objects.

The packages are coming soon to PyPI, npm and the VS Code Marketplace. Until then, install the interim builds from [Downloads](#downloads): they are built from the same code and work the same way.

**Licence.** The Python SDK, the TypeScript SDK, the CLI and the VS Code extension are open source under the Apache-2.0 licence. You can use them in commercial products, change them and ship them.

## Install

Once the packages are published (coming soon), these are the commands. Until then, use [Downloads](#downloads).

Python 3.10 or newer:

```bash
pip install thinqit-dex
```

TypeScript and JavaScript, for Node.js 18 or newer, Deno, Bun and edge runtimes. The package has no runtime dependencies and ships both ES modules and CommonJS:

```bash
npm i @thinqit/dex
```

The command line tool:

```bash
npm i -g @thinqit/dex-cli
```

## Downloads

The packages are not on PyPI, npm or the VS Code Marketplace yet. Until they are, install these interim builds from this site. They are built from the same commit as the site you are reading, so they match these docs.

| Client | File |
| --- | --- |
| Python SDK | [`thinqit_dex-0.1.1-py3-none-any.whl`](/downloads/thinqit_dex-0.1.1-py3-none-any.whl) (wheel) and [`thinqit_dex-0.1.1.tar.gz`](/downloads/thinqit_dex-0.1.1.tar.gz) (source) |
| TypeScript SDK | [`thinqit-dex-0.1.1.tgz`](/downloads/thinqit-dex-0.1.1.tgz) |
| CLI | [`thinqit-dex-cli-0.1.1.tgz`](/downloads/thinqit-dex-cli-0.1.1.tgz) |
| VS Code extension | [`thinqit-dex-vscode-0.1.1.vsix`](/downloads/thinqit-dex-vscode-0.1.1.vsix) |

The file names carry the version. When a new version is built, this page links the new files.

The Python SDK, for Python 3.10 or newer:

```bash
pip install https://thinqit.ai/downloads/thinqit_dex-0.1.1-py3-none-any.whl
```

The TypeScript SDK, in your project folder:

```bash
npm i https://thinqit.ai/downloads/thinqit-dex-0.1.1.tgz
```

The CLI, for Node.js 18 or newer. It gives you the `dex` command:

```bash
npm i -g https://thinqit.ai/downloads/thinqit-dex-cli-0.1.1.tgz
dex --version
```

The VS Code extension. Download the file, then install it:

```bash
curl -fLO https://thinqit.ai/downloads/thinqit-dex-vscode-0.1.1.vsix
code --install-extension thinqit-dex-vscode-0.1.1.vsix
```

You can also install the file from inside VS Code. Open the Extensions view, open the **...** menu at the top, choose **Install from VSIX...** and pick the file.

### Check a download

Every file has a `.sha256` file next to it, and [SHA256SUMS.txt](/downloads/SHA256SUMS.txt) lists them all. Put the file and its `.sha256` in one folder and run the check there. This example checks the extension:

```bash
curl -fLO https://thinqit.ai/downloads/thinqit-dex-vscode-0.1.1.vsix.sha256
sha256sum -c thinqit-dex-vscode-0.1.1.vsix.sha256       # Linux
shasum -a 256 -c thinqit-dex-vscode-0.1.1.vsix.sha256   # macOS
```

Both print `OK` when the file is intact. In PowerShell on Windows, these two lines must print the same hash:

```powershell
(Get-FileHash .\thinqit-dex-vscode-0.1.1.vsix -Algorithm SHA256).Hash.ToLower()
(Get-Content .\thinqit-dex-vscode-0.1.1.vsix.sha256).Split(" ")[0]
```

### Licence and executables

All four clients are open source under the Apache-2.0 licence. Every package holds its LICENSE and NOTICE files, and both are also here: [LICENSE.txt](/downloads/LICENSE.txt) and [NOTICE.txt](/downloads/NOTICE.txt).

The CLI's single executables are not offered for download, because each one is about 100 MB. Use the npm package with Node.js 18 or newer instead.

## Configuration

| Setting | Python | TypeScript | Environment variable | Default |
| --- | --- | --- | --- | --- |
| API key | `api_key` | `apiKey` | `DEX_API_KEY` | None: required |
| Base URL | `base_url` | `baseUrl` | `DEX_BASE_URL` | `https://api.thinqit.ai` |
| Timeout per attempt | `timeout` (seconds) | `timeout` (milliseconds) | | 30 seconds |
| Retries | `max_retries` | `maxRetries` | | 3 |
| Extra headers | `default_headers` | `defaultHeaders` | | None |

A value passed to the constructor wins over the environment variable. To run against the mock server, set `DEX_BASE_URL=http://127.0.0.1:4010`. The base URL must use `https://`, so your key is never sent in clear text; `http://` is accepted only for `localhost`, `127.0.0.1` and `[::1]`, and the SDKs, the CLI and the VS Code extension refuse any other `http://` address before sending a request.

## A first call

This sends the support ticket from the [Quickstart](/docs/quickstart/#make-your-first-call) and acts only on an answer that did not abstain.

```python tab="Python"
from thinqit_dex import Client, pick, rate, check

client = Client()  # reads DEX_API_KEY

decision = client.decide(
    state={
        "ticket": {
            "channel": "email",
            "subject": "Charged twice, still no answer",
            "body": "This is the third time I am writing. You charged my card twice "
            "for order 4471 and nobody replies. Fix it today or I cancel my subscription.",
        }
    },
    questions={
        "team": pick(
            "Which team should handle {{ticket.body}}?",
            {
                "billing": "Payments, refunds and invoices",
                "technical": "Bugs, outages and sign-in problems",
                "shipping": "Delivery and returns",
                "other": None,
            },
            min_confidence=0.5,
        ),
        "urgency": rate(
            "How fast must we reply to {{ticket.body}}?",
            ["Can wait", "This week", "Within two working days", "Today"],
        ),
        "angry": check("Is the writer of {{ticket.body}} angry?", min_confidence=0.6),
    },
)

team = decision.pick("team")
if not team.abstained:
    print(team.choice)
```

```ts tab="TypeScript"
import { Client, pick, rate, check } from "@thinqit/dex";

const client = new Client(); // reads DEX_API_KEY

const decision = await client.decide({
  state: {
    ticket: {
      channel: "email",
      subject: "Charged twice, still no answer",
      body: "This is the third time I am writing. You charged my card twice for order 4471 and nobody replies. Fix it today or I cancel my subscription.",
    },
  },
  questions: {
    team: pick(
      "Which team should handle {{ticket.body}}?",
      {
        billing: "Payments, refunds and invoices",
        technical: "Bugs, outages and sign-in problems",
        shipping: "Delivery and returns",
        other: null,
      },
      { min_confidence: 0.5 },
    ),
    urgency: rate("How fast must we reply to {{ticket.body}}?", [
      "Can wait",
      "This week",
      "Within two working days",
      "Today",
    ]),
    angry: check("Is the writer of {{ticket.body}} angry?", { min_confidence: 0.6 }),
  },
});

if (!decision.answers.team.abstained) {
  console.log(decision.answers.team.choice);
}
```

### What the SDKs cover

| Operation | Python | TypeScript |
| --- | --- | --- |
| [decide](/docs/api/decide/) | `client.decide(state, questions, *, model=None, fallback=None, idempotency_key=None, ...)` | `client.decide(request, options?)` |
| [listModels](/docs/api/listModels/) | `client.list_models()` | `client.listModels()` |
| [getCurrentKey](/docs/api/getCurrentKey/) | `client.get_key()` | `client.getKey()` |
| [getUsage](/docs/api/getUsage/) | `client.get_usage(start, end, *, granularity=None, key_id=None)` | `client.getUsage({ start, end, granularity?, key_id? })` |
| [getBalance](/docs/api/getBalance/) | `client.get_balance()` | `client.getBalance()` |

Every method takes a client request id (`client_request_id` in Python, `clientRequestId` in TypeScript), which the API echoes in `x-client-request-id`. See [Headers](/docs/reference/headers/#request-ids).

The console routes are not part of the SDKs. Python also has `AsyncClient`, with the same arguments and `await`-able methods:

```python
from thinqit_dex import AsyncClient

async with AsyncClient() as client:
    decision = await client.decide(state=state, questions=questions)
```

### Builders and typed answers

- `pick(instructions, options, ...)`, `rate(instructions, levels, ...)` and `check(instructions, ...)` return plain question objects. `rate` is in beta: see [Questions](/docs/concepts/questions/#rate). Each also takes `criteria` and `min_confidence`: as keyword arguments in Python, and in a final `{ criteria, min_confidence }` object in TypeScript.
- In Python, `decision.pick(id)`, `decision.rate(id)` and `decision.check(id)` return typed answers. A wrong id raises `KeyError`, and a wrong type raises `TypeError`.
- In TypeScript, `decision.answers.<id>` is typed from your request. The labels of a pick become a union type for `choice` and `probabilities`.
- Every answer type lists `abstained` first. Check it before you use an answer. See [Abstention](/docs/concepts/abstention/).
- `ref(...)` and `escape_braces` (`escapeBraces` in TypeScript) build [field references](/docs/concepts/field-references/#sdk-helpers).

### Response details

Every result has an `http` property with the status, the request id, your client request id, the idempotency key, whether it was a replay, the rate limit headers, how many retries it took, and all headers. Python uses snake_case names, such as `result.http.request_id`, and TypeScript uses camelCase, such as `result.http.requestId`.

## Retries and idempotency

The SDKs retry for you:

- **What they retry:** HTTP 429, 500, 502, 503 and 504, 409 `idempotency_in_progress`, connection errors and timeouts. Never any other 4xx, and never 429 `test_daily_quota`, which lasts until 00:00 UTC. A load balancer can return 502 or 504 during a restart, which is why those are retried although the API itself does not send them.
- **How often:** up to `max_retries` retries (3 by default), so at most 4 attempts.
- **How long they wait:** the `retry-after` header when there is one. Otherwise a random delay between 0 and `min(8, 0.25 * 2^n)` seconds before retry `n`, counting from 0.
- **No double charges:** every `decide` call sends an `idempotency-key`, yours or a new UUID v4, and reuses it on every retry. A retry can never charge twice. Read-only calls send none. See [Idempotency](/docs/reference/idempotency/#sdks).

Set `max_retries=0` (or `maxRetries: 0`) on one call to turn retries off for it.

## Errors

Each error `type` has its own class, such as `RateLimitError` for a 429 and `RequestValidationError` for a 422. All of them carry `status`, `type`, `code`, `message`, `param`, `request_id` and `retry_after`. The full table is in [Errors](/docs/reference/errors/#sdk-error-classes).

The SDKs are built and tested never to write your API key to logs, error messages or object representations.

## The CLI

Install the CLI from [Downloads](#downloads) until it is on npm. The command is `dex`.

```bash
dex login                       # checks the key's checksum, then stores it in the OS keychain
dex whoami                      # which key and account you are using (GET /v1/key)
dex decide --file request.json  # sends a request file, such as the one from the Quickstart
dex models                      # lists models
dex usage --since 7d            # shows your usage
dex balance                     # shows your balance
```

- `dex decide` also takes inline flags for quick checks, such as `--state @ticket.txt` for a state read from a file.
- Output is a table by default, or JSON with `--json`.
- The exit code tells scripts what happened: 0 success, 2 validation, 3 authentication, 4 balance, 5 rate limit, 6 unavailable.
- `dex login` reads the key from standard input, so it never appears in your shell history. It refuses a mistyped key offline, from the key's [checksum](/docs/reference/authentication/#key-format-and-checksum).
- `dex decide --file` keeps the key order of your file exactly, rejects duplicate keys, and removes a top-level `$schema` line before sending.

## VS Code extension

The VS Code extension validates request files against the contract, runs them from the editor and shows the answers inline. Its status bar shows which key you use, from `GET /v1/key`. Install it from [Downloads](#downloads) until it is on the Marketplace.

## JSON schema for request files

The request schema is published at https://thinqit.ai/schemas/dex-request.schema.json, generated from the API contract. Add `"$schema": "https://thinqit.ai/schemas/dex-request.schema.json"` at the top of a request file to get completion and validation in any editor that reads JSON schemas. The CLI and the VS Code extension remove `$schema` before sending. The API itself rejects it as an unknown field, so leave it out of bodies you send with curl or an SDK.
