---
title: Typed decisions
description: Use bun:yolo to turn named yes/no, choice and score judgments into validated JavaScript values.
---

`bun:yolo` turns named Noul (yes/no), Choice and Score answers into JavaScript primitives. `bun:Yolo` resolves to the same module. Bun validates question types, answer names, choice keys and numeric bounds natively before returning a value.

```ts
import Yolo, { code, noul } from "bun:yolo";

const provider = code(request => ({
  model: "public-access-rule",
  answers: {
    answer: { type: "noul", noul: (request.state as { visibility: string }).visibility === "public" ? 1 : 0 },
  },
  usage: null,
}));

if (await Yolo({ visibility: "public" }, noul("Is this resource public?"), { provider })) {
  console.log("read allowed");
}
```

The result is a primitive `boolean`. A Noul becomes `true` only when its probability is **greater than** `threshold`, which defaults to `0.5`. Use `inspect` when the probability itself matters. A judgment does not authorize side effects on its own: keep deterministic access rules in application code.

## Named batches

```ts
import { decide, noul, choice, score, type Provider } from "bun:yolo";

export async function decideSources(provider: Provider) {
  return await decide(
    {
      state: { query: "How does Bun.Video resize frames?", passage: "Bun.Video provides transformed frames." },
      questions: {
        relevant: noul("Does `passage` help answer `query`?"),
        route: choice("Which public source fits `query`?", {
          docs: "API documentation",
          graph: "Relationships between source symbols",
          none: "Neither source fits",
        }),
        evidence: score("How much support does `passage` provide for `query`?", [
          "No supporting information",
          "Information about the API, without the requested details",
          "Information directly answering the requested details",
        ]),
      },
    },
    { provider, timeout: 10000, maxRetries: 0 },
  );
}
// relevant: boolean; route: "docs" | "graph" | "none"; evidence: number in 0..2
```

All questions for one state share one provider request. Omitting `provider` selects `remote()`. The `code` provider above is deterministic; it demonstrates the protocol without performing a language judgment.

Limits:

- A batch contains 1–64 questions.
- A Choice has 2–255 named options.
- A Score has 2–10 ordered criteria and may return a fractional value between zero and the final index.
- Request and response JSON are each limited to 1 MiB.
- Model names must contain a non-whitespace character and fit within 256 UTF-8 bytes.

Unknown answer names, choice keys, wrong legends and numbers outside their range throw.

`inspect(request, options)` returns the validated `{ model, answers, usage }` response. `systemOne` is an alias of `inspect`. `ask(state, question, options)` answers a single question. `value(answer, question, threshold?)` validates one answer and returns its primitive.

## Providers

A `Provider` owns its transport, credentials and model. `code(evaluate)` adapts a synchronous or asynchronous callback to this contract. The callback returns the raw named response; Bun still validates the request, answer distributions and primitive conversion. Keep provider credentials on the server.

`remote({ baseURL?, model?, client? })` uses native HTTP by default:

| Setting  | Resolution order                                                                           |
| -------- | ------------------------------------------------------------------------------------------ |
| API key  | `TYPESAFE_API_KEY`                                                                         |
| Endpoint | `baseURL`, then `TYPESAFE_BASE_URL`, then `https://api.typesafe.ai`                        |
| Model    | the request's `model`, the provider's `model`, `TYPESAFE_DEFAULT_MODEL`, then `jev-latest` |

Requests use the `/v1/systemone` path. Endpoints require HTTPS; plain HTTP is accepted only for loopback addresses. URL credentials, query strings, fragments and redirects are rejected.

Supplying `client` delegates transport and retries to its `systemOne` method. Configure the endpoint on that client; `baseURL` cannot be combined with `client`. Native HTTP retries status codes 408, 429 and 5xx within `maxRetries` and the operation deadline. Authentication failures reject without retrying. Response bodies are bounded while streaming and must contain valid UTF-8 JSON.

This interface returns structured decisions, not generated text. See [typed decision boundaries](/docs/runtime/decisions) for confidence, abstention and the Python SDK.

## Timeouts and cancellation

`timeout` bounds the whole operation, including serialization and pending retries, to 1–120000 ms (default 10000). Synchronous callbacks cannot be interrupted; their result rejects once control returns after the deadline. `maxRetries` is an integer from 0 to 3, default 0. Providers receive the deadline, retry limit and cancellation signal. An `AbortSignal` cancels the request and settles the caller even when a custom provider does not complete.

## Local models

`local({ context, model, maxTokens? })` accepts a loaded `bun-llama` context. The caller selects the GGUF model file and owns the context and model lifetime. No weights are downloaded or bundled.

The adapter requests strict JSON at temperature zero, caps generation at 1–4096 tokens (default 1024), and sends every answer through the same native validator. Markdown, explanatory text and responses outside the schema are rejected. Local adapters sharing a context accept one active request at a time; after cancellation or timeout, the context remains busy until its `complete` call settles. Token usage is `null` because the context does not report exact token counts. Local probabilities are raw model outputs and are not calibrated.

Optional providers require their credentials, native library or model to be available. Validation and `code` providers work without them.
