# Crossroads agent setup

These steps are for a coding agent adding Crossroads to the project it works
in. The person you work with signs in once, adds a payment method if they have
none, and approves; you do the rest. Crossroads answers questions about a
piece of context with values code can use: one option from a list, a boolean
or a number on a scale, each with the model's probabilities, checked against
the questions asked. It
also filters a list by an instruction. It does not write text or hold a
conversation.

## Request access

Crossroads is in early access. If the person has no Crossroads account yet,
ask for their email and request access for them:

```sh
curl -fsS https://smallmodelslab.ai/api/waitlist \
  -H 'Content-Type: application/json' -d '{"email":"<their email>"}'
```

`{"state":"pending"}` puts them on the list; they get an email invitation once
approved. Continue when they have accepted it. HTTP 409 means they already
have an account.

## Start a setup request

```sh
curl -fsS -X POST https://smallmodelslab.ai/api/setup
```

The response has `url`, `userCode`, `deviceCode`, `interval` and `expiresIn`.
Show the person the `url` and the `userCode`, and ask them to open the link.
There they sign in, add a payment method if they have none, and give you
access. Keep `deviceCode` to yourself. HTTP 429: wait for the `Retry-After`
header, then try again.

## Wait for the key

Every `interval` seconds, until `expiresIn` runs out:

```sh
curl -sS https://smallmodelslab.ai/api/setup/key \
  -H 'Content-Type: application/json' -d '{"deviceCode":"<deviceCode>"}'
```

- `{"status":"pending"}`: keep waiting.
- `{"status":"complete","apiKey":"cr_live_…"}`: the person approved. The key
  is returned once. Store it as `CROSSROADS_API_KEY` where the project keeps
  its secrets, such as a gitignored `.env.local`; never commit or print it.
- HTTP 410: the request expired or its key was already collected. Start again
  only if the person asks.
- Any other error has a `message`; show it to the person.

## Make a request

Call Crossroads from the server, never from a browser or app that ships the
key:

```sh
curl -fsS https://smallmodelslab.ai/v1/decisions \
  -H "Authorization: Bearer $CROSSROADS_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: ticket-123-attempt-1' \
  -d '{
    "state": "Hi! We were charged twice for our plan this month.",
    "questions": {
      "route": {
        "type": "choice",
        "instruction": "Which team should handle this request?",
        "options": ["billing", "engineering", "sales"]
      },
      "needs_reply": { "type": "boolean", "instruction": "Does this message need a reply?" },
      "urgency": { "type": "score", "instruction": "How urgent is this request?", "min": 1, "max": 5 }
    }
  }'
```

```json
{
  "id": "opaque-request-id",
  "model": "crossroads-8b",
  "answers": {
    "route": {
      "choice": "billing",
      "probabilities": { "billing": 0.91, "engineering": 0.06, "sales": 0.03 }
    },
    "needs_reply": { "value": true, "probability": 0.94 },
    "urgency": {
      "score": 3.48,
      "probabilities": { "1": 0.02, "2": 0.08, "3": 0.41, "4": 0.38, "5": 0.11 }
    }
  },
  "usage": { "input_tokens": 100, "output_tokens": 0 }
}
```

- `state` is the context: text, or a JSON object or array, up to 48,000
  characters (JSON counted in its compact form).
- `questions` holds 1–16 questions, named with a letter followed by up to 63
  letters, digits or underscores. Each has an `instruction` of up to 2,000
  characters and a `type`:
  - `choice` with 2 or more `options`, a list of unique names or an object mapping
    each name to a description of up to 1,000 characters: `{ "choice", "probabilities" }`,
    the most probable option and every option's probability.
  - `boolean`: `{ "value", "probability" }`, `true` or `false` and the
    probability of `true`.
  - `score` with `min` below `max`, or `levels`, 2–32 ordered descriptions
    scored from 0: `{ "score", "probabilities" }`, the expected score in its
    range and each level's probability.
- `model` is optional: `crossroads-8b` by default, or `crossroads-m`, which
  also accepts one `image` (JPEG, PNG or WebP, at most 4,194,304 pixels) or
  one `audio` recording (WAV or FLAC, at most 30 seconds) as
  `{"media_type": …, "data": <padded base64>}`, each at most 1 MiB, within
  8,192 tokens per question. With a recording, `state` may be empty.
- `Idempotency-Key` takes 8–128 visible ASCII characters, unique per distinct
  request. The same input with the same key returns the saved response and is
  not billed again.
- Send UTF-8 JSON with `Content-Type: application/json`; unknown fields are
  rejected. Requests are limited to 1.5 MiB and, by default, 120 per minute and
  four at a time per account.

Errors are `{"error": {"code", "message", "request_id"}}`: 400 invalid input or
idempotency key, 401 missing or revoked key, 402 usage billing not active, 409
the key's first attempt is still running, failed or had different input, 413
body over 1.5 MiB or a question over the model's tokens, 415 wrong
`Content-Type`, 429 rate or concurrency limit or every GPU busy, 502 a model backend error, 503
invalid model answers, a required service unavailable or the request still
running.

Retrying with the same key is always safe. Retry a 429 after `Retry-After`,
and a 502 or 503 after a short backoff, with the same key. 409
`request_in_progress`: retry the same key a few seconds later. 409
`previous_request_failed`: it was not billed; send it again with a new key.
409 `idempotency_conflict`: the key was used for different input. Fix a 400,
401, 402, 413 or 415 before sending again.

Usage costs $0.040 USD per million input tokens for `crossroads-8b` and $0.080
for `crossroads-m`; output is free, and successful requests are billed monthly. Each question is scored against the
whole `state`, so input tokens count the state once per question: sixteen
questions in one request cost what sixteen requests would, in one round trip.
The person manages keys, usage and billing at
https://smallmodelslab.ai/console.

## Design the questions

- Make `state` an object whose names say what each part is, and use those
  names in the instructions. Put everything a question compares in it, and
  leave out what no question needs.
- Act on an answer only when its probability clears a bar set by what a wrong
  answer costs: act when high, ask for confirmation or review in between, hand
  it to a person or another system when low. Probabilities close together, or
  a score spread over several levels, usually mean overlapping options or
  ambiguous levels; rewrite them before lowering the bar. Probabilities are
  not guaranteed to be calibrated, so choose bars from labelled examples.
- Common shapes: one `choice` to route a request, with an option for none of
  the destinations; narrow `score`s with described levels, combined with
  weights in code; `boolean`s that screen what goes into and out of a
  language model; `/v1/filter` to keep only the retrieved passages that
  answer the user's question.

## Filter a list

`POST /v1/filter` takes the same headers and keeps the items of a list that
match an instruction:

```sh
curl -fsS https://smallmodelslab.ai/v1/filter \
  -H "Authorization: Bearer $CROSSROADS_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: billing-filter-1' \
  -d '{
    "items": [{ "title": "Fix invoices" }, { "title": "Update landing page" }],
    "instruction": "Keep billing work",
    "cutoff": 0.8
  }'
```

```json
{
  "items": [{ "title": "Fix invoices" }],
  "indices": [0],
  "probabilities": [0.97],
  "id": "opaque-request-id",
  "model": "crossroads-8b",
  "usage": { "input_tokens": 180, "output_tokens": 0 }
}
```

- `items` holds 1–256 JSON values, each at most 48,000 characters as JSON, or
  16,000 on `crossroads-m`. `instruction` has up to 1,800 characters.
- The response keeps the items whose probability is at least `cutoff` (0.5 by
  default), highest first; `indices` are their positions in your list.
- `model`, `image` and `audio` work as for decisions: every item is judged
  against the image or recording.
- The same key with only a new `cutoff` re-ranks the saved probabilities
  without another charge. A long list runs in batches billed one by one, so a
  new key after a failed filter bills its finished batches again.
