A server is a JSON API, so Python needs nothing but its standard library. TypeSafe’s SDK, which speaks the same wire format, works too once it is pointed at the server.
Figure · A Python client
One POST out, one JSON answer back; the rest is reading it.
The numbers are the recorded decision's; a server on another base answers with its own.
Recorded decision: Gemma 4 31B, decisio 0.9.0, RTX PRO 6000 at 600 W, 2026-10-08; record 1 of 60 of decisio-examples' support-routing run; the probabilities are rounded to two places.
With a server running (Get started starts one), this script sends the recorded support ticket and prints each question’s likeliest option:
import jsonimport urllib.request
body = { "state": "I was charged twice for October. Both charges are 49.00 EUR, same day. Please refund one.", "questions": { "urgent": { "type": "noul", "instructions": "Does this ticket need a response within the hour?" }, "queue": { "type": "choice", "instructions": "Which team should handle this ticket?", "criteria": { "billing": "Invoices, payments, refunds, prices, VAT", "access": "Login, passwords, two-factor, SSO, permissions, invitations, account security", "bug": "The product behaves wrongly: errors, crashes, wrong numbers, lost data", "feature": "A request for something the product does not do yet", "cancellation": "Cancelling, pausing or closing an account, deleting data", "other": "Press, partners, jobs, sales pitches, wrong address, thanks, general questions" } }, "impact": { "type": "score", "instructions": "Rate the business impact using only the reported facts.", "criteria": [ "No function impaired", "One user impaired, with a workaround", "Many users blocked from a core function", "Data loss or legal exposure" ] } }}
request = urllib.request.Request( "http://127.0.0.1:8000/v1/systemone", data=json.dumps(body).encode(), headers={"Content-Type": "application/json"},)with urllib.request.urlopen(request) as response: answers = json.load(response)["answers"]
for name, answer in answers.items(): if answer["type"] == "noul": print(name, round(answer["noul"], 3)) else: top = max(answer["probabilities"], key=answer["probabilities"].get) print(name, top, round(answer["probabilities"][top], 3))# urgent 0.159# queue billing 0.962# impact 1 0.648Run against the CPU stand-in at v0.11.1 on 2026-10-14: the answers are the stand-in's, not the measured system's.
The comment lines show what the measured system answered (recorded; the stand-in prints other numbers).
Recorded decision: Gemma 4 31B, decisio 0.9.0, RTX PRO 6000 at 600 W, 2026-10-08; record 1 of 60 of decisio-examples' support-routing run; the printed probabilities are rounded to three places.
| Word | Meaning |
|---|---|
noul | a yes/no question; its answer is the probability of yes |
probabilities | a probability for each option of a choice, or each level of a score; they add up to 1 |
| the SDK | TypeSafe’s Python SDK, published under the MIT licence; Decisio does not include it |
1. With the standard library
Section titled “1. With the standard library”The script posts the body with urllib.request and reads answers, which holds one answer per question under the name you gave it.
A choice’s answer names its likeliest option (choice) and gives every option’s probability; read the probabilities, not only the name, to see how sure it is.
2. With TypeSafe’s SDK
Section titled “2. With TypeSafe’s SDK”The SDK is TypeSafe’s and installs on its own (pip install typesafe-sdk); the block below was run with version 0.7.2, against a decisio server.
Give it the server’s address, a key (it requires one; the server ignores it) and the served model’s name:
from typesafe_sdk import TypeSafeClient
client = TypeSafeClient( base_url="http://127.0.0.1:8000", # the SDK requires one; the server ignores it api_key="unused", # the name GET /v1/models lists model="decisio-gemma-4-31b-it-letters",)body = { "state": "I was charged twice for October. Both charges are 49.00 EUR, same day. Please refund one.", "questions": { "urgent": { "type": "noul", "instructions": "Does this ticket need a response within the hour?" }, "queue": { "type": "choice", "instructions": "Which team should handle this ticket?", "criteria": { "billing": "Invoices, payments, refunds, prices, VAT", "access": "Login, passwords, two-factor, SSO, permissions, invitations, account security", "bug": "The product behaves wrongly: errors, crashes, wrong numbers, lost data", "feature": "A request for something the product does not do yet", "cancellation": "Cancelling, pausing or closing an account, deleting data", "other": "Press, partners, jobs, sales pitches, wrong address, thanks, general questions" } }, "impact": { "type": "score", "instructions": "Rate the business impact using only the reported facts.", "criteria": [ "No function impaired", "One user impaired, with a workaround", "Many users blocked from a core function", "Data loss or legal exposure" ] } }}
response = client.system_one(state=body["state"], questions=body["questions"])
for name, answer in response.nouls.items(): print(name, round(answer.noul, 3))for name, answer in response.choices.items(): print(name, answer.choice, round(answer.probabilities[answer.choice], 3))for name, answer in response.scores.items(): top = max(answer.probabilities, key=answer.probabilities.get) print(name, top, round(answer.probabilities[top], 3))# The server's name for the model, and the tokens it counted:print(response.model, response.usage.input_tokens)Run against the CPU stand-in at v0.11.1 on 2026-10-14: with TypeSafe's Python SDK 0.7.2, the version the page names. Its comments are shown above their commands.
3. When the server says no
Section titled “3. When the server says no”A request that fails validation gets 422 and one the engine cannot answer gets 400, each with a detail naming the problem.
With the standard library these are an HTTPError whose body holds the detail; the SDK raises its own types (TypeSafeUnprocessableEntityError, TypeSafeBadRequestError).
A server sets no rate limit, so the SDK’s retries on 429 never fire; it still retries 408 and 5xx.
import jsonimport urllib.errorimport urllib.request
request = urllib.request.Request( "http://127.0.0.1:8000/v1/systemone", data=json.dumps({"state": "No questions asked.", "questions": {}}).encode(), headers={"Content-Type": "application/json"},)try: urllib.request.urlopen(request)except urllib.error.HTTPError as error: print(error.code, "detail" in json.load(error))# 422 TrueRun against the CPU stand-in at v0.11.1 on 2026-10-14.
What next
Section titled “What next”- JavaScript and TypeScript: the same call from Node.
- System One clients: point a client you already have at the server.
- The HTTP API: every route, request field and response field.