Typed decisions for text, documents, and images.
Documentation · Image examples · Get started · API · Examples
Send text, JSON, a document, or an image with typed questions. OpenDecision runs locally and returns structured answers.
OpenDecision provides:
Choiceto select one option from a list.Noulto score a statement or compare explicit true/false descriptions.Scoreto place state on an ordered scale.Relationto reportsupports,contradicts,unknown, orconflicted.- Document processing with evidence retrieval and source passages.
- Image decisions with optional SigLIP2 support and the same typed questions.
- A Python API, a FastAPI server, and a TypeSafe SDK compatible endpoint.
The default backend is MoritzLaurer/ModernBERT-large-zeroshot-v2.0. MoritzLaurer/deberta-v3-large-zeroshot-v2.0 is an optional model. Image decisions use google/siglip2-base-patch16-224. See model selection and evaluation for the measured tradeoffs.
OpenDecision is licensed under Apache 2.0.
| Question | Answer |
|---|---|
| Is an animal visible? | Yes |
| What kind of animal is visible? | bear |
Animal present and type bear returns sound_alarm.
API call: 318 ms for both questions.
| Question | Answer |
|---|---|
| Is a bin visible? | Yes |
| What condition is the bin in? | overflowing |
| Is litter scattered on the floor? | Yes |
Bin present and condition overflowing returns request_collection.
API call: 234 ms for all three questions.
| Question | Answer |
|---|---|
| Is a fence visible? | Yes |
| What condition is the fence in? | damaged |
Fence present and condition damaged returns create_repair_task.
API call: 155 ms for both questions.
These are recorded SigLIP2 results. The actions are simulated. Call times are medians of five local HTTP requests on an Apple M3 with the models loaded. The rules use a yes/no threshold of 0.5 and require a winning Choice probability of at least 0.6.
More images and answers · Image API and setup · Run the demo
I was charged twice and I need this fixed before my meeting this afternoon.
| Question | Answer |
|---|---|
| Which department should handle this? | billing |
| Is this request urgent? | Yes |
API call: 231 ms for both questions, measured with the same method as the image examples.
OpenDecision accepts plain text, JSON, or other Python values as state. The document API splits long documents, finds relevant passages, answers each question, and returns the passages used.
The sample claim contains a demand letter, policy facts, a police report, medical records, bills, and employer records.
| Question | Answer |
|---|---|
| Are the medical expenses documented? | established |
| Is a serious injury documented? | refuted |
| Is the rental need fully supported? | unknown |
| Are documents still outstanding? | established |
The saved synthetic claim benchmark retrieves 17 required facts and matches 10 composed decisions. See the insurance claim benchmark.
The GDPR example uses a 54,171-character document split into 31 sections.
| Question | Answer |
|---|---|
| Must a personal data breach be reported within 72 hours? | true |
| Must every organization appoint a Data Protection Officer? | false |
| Can pre-ticked boxes count as valid consent? | false |
| What is the maximum fine for serious infringements? | EUR 20 million or 4% of worldwide turnover |
The saved retrieval benchmark answers 9 of 10 questions correctly. See the questions and saved results.
POST /v1/documents/decide has one noul_mode setting:
| Mode | Result |
|---|---|
binary |
Always chooses true or false. |
three_way |
Returns supports, contradicts, or unknown. |
both |
Runs both evaluations. This is the default. |
In both mode, OpenDecision reports confirmed, tentative, or conflicted. A conflict has answer: null. Both score distributions remain in the response.
The noul_mode setting applies to document Noul questions.
OpenDecision requires Python 3.13 or later.
pip install OpenDecisionOr add it to a uv project:
uv add OpenDecisionRun a decision in Python:
from opendecision import OpenDecisionEngine
engine = OpenDecisionEngine()
result = engine.choice(
state="The customer was charged twice for one subscription.",
instructions="Which team should handle this request?",
criteria={
"billing": "Payments, invoices, refunds, and duplicate charges",
"technical": "Software bugs and integration problems",
"sales": "Pricing and new purchases",
},
)
print(result["choice"])
# billingStart the API:
opendecision serveIn a uv project, run uv run opendecision serve.
Open http://127.0.0.1:8000/docs to send requests from the interactive API page.
The model downloads from Hugging Face on first use.
For image support:
pip install 'OpenDecision[images]'from PIL import Image, ImageOps
from opendecision import ImageDecisionEngine
engine = ImageDecisionEngine()
with Image.open("photo.jpg") as source:
image = ImageOps.exif_transpose(source).convert("RGB")
result = engine.choice(
state=image,
instructions="What kind of animal is visible?",
criteria={"cat": "A photo of a cat", "bear": "A photo of a bear"},
)
print(result["choice"])The API accepts the same question types with an image state:
{
"state": {"type": "image", "data": "BASE64_IMAGE_BYTES"},
"questions": {
"animal_type": {
"type": "choice",
"instructions": "What kind of animal is visible?",
"criteria": {"cat": "A photo of a cat", "bear": "A photo of a bear"}
}
}
}Images must be single-frame JPEG, PNG, or WebP, up to 10 MiB and 20 megapixels. The server loads SigLIP2 on the first valid image request.
- Documentation index
- Quick start and API
- Choice, Noul, Score, and Relation
- Documents, evidence, and yes/no modes
- Image examples
- Image decisions and SigLIP2
- Evidence relations and rules
- Benchmarks
- Model selection and evaluation
GET /health
POST /v1/systemone
POST /v1/documents/decide
GET /docs
GET /openapi.json
POST /v1/systemone accepts the core TypeSafe request shape. A local OpenDecision server can be used as the base_url for compatible clients.
OpenDecision v0.2.0 is a developer preview.
Model scores are uncalibrated. Test captions and thresholds on your own images and documents. Counting and negated image captions produced errors in the sample tests.


