probably∿A small language for uncertain times.
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 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.
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.
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.
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. #
Run the live interpreter locally, or deploy the cached playground to Cloudflare Workers. Local model calls use AI Gateway.
-
Get the code and connect Cloudflare.Install Bun , 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 aJev provider key with aliasdefaultso Jev can run.
cd probably
bun install
-
Run locally.Copy the example settings, then open
.envand fill in your Cloudflare account ID and API token. Use the permissions in theGateway setup guide . KeepGATEWAY_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.