# Probably – a programming language for LLM workflows, powered by Jev

> Source: <https://probably-lang.southpolesteve.workers.dev/>
> Published: 2026-09-18 09:17:41+00:00

[probably∿](https://probably-lang.southpolesteve.workers.dev/)A small language for uncertain times.

# A programming language for LLM workflows, powered by [Jev](https://typesafe.ai/blog/introducing-system-one-models-and-jev). Determinism is overrated.

Replay repeats the last completed run without calling a model. Download run JSON saves its source, input and decisions.

Your output will appear here.

Each judgment shows its probabilities and the exact value it saw.

## The language, in a few minutes.

Probably 0.1
Probably is its own small language. A TypeScript interpreter executes the code; [Jev](https://typesafe.ai/blog/introducing-system-one-models-and-jev) supplies judgments and probabilities. A separate text model generates prose. `feels` asks a question, `match` picks a description, and `llm` generates text. Run is a playground button, not a language keyword.

**Try the inbox department:** Jev checks urgency and routes the email. Each branch gives the LLM a different reply strategy. A loop edits the tone, a confidence check flags uncertainty, and a final LLM call writes a subject from the finished reply. The last output is your draft. When running locally, change the input to a sales pitch or meeting request to see the route change. This example drafts replies; it does not send email.

## 1. Values, input and output

`let` declares a variable. Assign again to update it. Values are strings, nonnegative numbers or booleans; comments start with `//`. `input()` reads the input box and `print()` adds a line to output.

``` js
let message = input()
let attempts = 3
let optimistic = true
message = "Everything is probably fine."
print(message)
```

Variables belong to their block. Assignment can update a variable in an outer block. Strings use double quotes and escapes such as `\n`. Semicolons are optional.

## 2. Call a language model with llm

`llm` sends an instruction to a text-generation LLM through Cloudflare AI Gateway and returns its response as a string. `using` passes the value to work on. Only that value is passed as context; the model does not see all your variables.

``` js
let draft = llm "Explain this in one sentence." using input()
print(draft)
```

You can omit `using` when the instruction needs no context. `print` only displays a value; it never calls a model. Older programs can still use `write` as an alias for `llm`.

## 3. Make a judgment with if … feels

`feels` compares a value with a quoted description. Normally the higher-probability yes/no answer wins. Add a confidence threshold to handle ambiguity explicitly.

```
if message feels "genuinely urgent" with confidence 80% {
  print("Drop everything.")
} otherwise maybe {
  print("Ask a follow-up question.")
} else {
  print("It can wait.")
}
```

At 80%, a 90% yes takes the first branch, a 90% no takes `else`, and a 60/40 split takes `otherwise maybe`. The threshold uses the winning answer’s probability. It is a model estimate, not a guarantee of truth. Thresholds range from 50–100%; the default is 50%. If you omit the maybe block, an uncertain result runs neither branch.

## 4. Choose a branch with match

`match` asks Jev to choose among 2–8 distinct descriptions. The highest-probability label wins, even when no label is a great fit. Include an “other” label when useful.

``` js
match message {
  "a bug report" => { print("Investigate.") }
  "a feature request" => { print("Add to the wishlist.") }
  "something else" => { print("Ask a human.") }
}
```

There is no confidence gate on `match` in 0.1. Ties use the first label; yes wins a tied `if`.

## 5. Keep trying with while and repeat

`while … feels` asks Jev before every iteration, including after the last rewrite. `repeat` runs a fixed number of times without asking a model.

```
while draft feels "full of corporate jargon" {
  draft = llm "Rewrite plainly." using draft
}
print(draft)

repeat 3 {
  print("This is definitely helping.")
}
```

While loops allow at most five iterations and report an error if the condition is still true. `repeat` accepts a literal integer from 1 to 5. The complete run also has a 12-model-call budget, so elaborate programs can hit that limit first.

## 6. Embrace chaos, then replay it

Inside `chaos`, judgments sample from their probabilities. A 20% answer has a 20% chance of being chosen. Any confidence gate is checked before sampling.

```
chaos {
  if input() feels "a sensible business idea" {
    print("Fund it.")
  } else {
    print("Fund it, but ironically.")
  }
}
```

Hosted examples stream the same cached results on each run. Locally, Run again makes fresh calls and may produce the same result. Replay uses the last completed run’s original source, input, model results and random draws. It makes no model calls. Download run JSON saves that recording; replay a saved file locally with `bun run run --replay recording.json`. Recordings contain your input and generated text.

## What 0.1 supports—and where it stops

This is an experimental language for playful workflows. It has no arithmetic, arrays, objects, functions, imports, network tools or JavaScript execution. `feels` is a condition inside `if` or `while`, not a standalone expression. The `.prob` extension simply identifies a Probably source file; filenames do not affect execution.

Runs stop at 12 model calls, 200 statements or 90 seconds. Source is limited to 12,000 characters, input to 6,000 and nesting to 12 blocks. Errors stop the program; earlier output remains visible. Stop cancels interpretation, although an already-dispatched model request may still finish remotely.

The hosted Worker streams previously recorded Jev judgments and generated text for the exact bundled examples. Custom code or inputs have no cached result and are rejected without calling a model. Run locally to execute your own programs with live providers.

## Run it yourself.

[Download source ↓](https://probably-lang.southpolesteve.workers.dev/probably-source.zip)

Run the live interpreter locally, or deploy the cached playground to [Cloudflare Workers](https://developers.cloudflare.com/workers/). Local model calls use [AI Gateway](https://developers.cloudflare.com/ai-gateway/get-started/).

1. ### Get the code and connect Cloudflare.Install [Bun](https://bun.sh/) , download and unzip the source, then open its folder in your terminal. In your Cloudflare account, create an AI Gateway called`default` . Add Gateway credits or a[Jev provider key](https://developers.cloudflare.com/ai-gateway/configuration/bring-your-own-keys/) with alias`default` so Jev can run.

```
cd probably
bun install
```

2. ### Run locally.Copy the example settings, then open `.env` and fill in your Cloudflare account ID and API token. Use the permissions in the[Gateway setup guide](https://developers.cloudflare.com/ai-gateway/get-started/) . Keep`GATEWAY_ID=default` .

```
cp .env.example .env
bun run dev
```

 Open the localhost URL printed in your terminal. The interpreter runs on your machine; the models run remotely.
3. ### Deploy to Cloudflare.Choose a unique Worker name in `wrangler.jsonc` , then run:

```
bunx wrangler login
bun run deploy
```

 This deploys the cached playground. The Worker streams bundled recordings and requires no model bindings or provider credentials. Use the local server or CLI for live model execution.

The source includes the interpreter, playground, examples, tests and a full README. Model calls use your Cloudflare or provider account’s quota and billing.
