Exact totals
Use an arithmetic policy when a model must extract or propose values and a derived value must agree with them. The model handles interpretation; OPA enforces the calculation.
Here the input is “train ticket EUR 12.50; lunch EUR 7.50.” The answer contains the extracted amounts in cents and their total. The policy requires positive amounts and an exact sum.
Define the fields and the rule
The schema bounds the number of items and requests integer cents. Rego's every checks each amount; sum checks their relationship to the total.
{
"type": "object",
"properties": {
"amounts_cents": {
"type": "array",
"minItems": 1,
"maxItems": 8,
"description": "Extract each receipt amount in order as integer cents. EUR 12.50 means 1250. Do not include a separate total as a line item.",
"items": {
"type": "integer"
}
},
"total_cents": {
"type": "integer",
"description": "The sum of the extracted amounts, in integer cents."
}
},
"required": [
"amounts_cents",
"total_cents"
],
"additionalProperties": false
}
package totals
default allow := false
allow if {
every amount in input.output.amounts_cents { amount > 0 }
input.output.total_cents == sum(input.output.amounts_cents)
}
Extract and check
This complete request needs only an API key, the schema and the policy. It has no state or permission dependency.
Before you run this example
Call https://api.blatent.ai from your server. Set your API key in the same terminal where you run the example:
export BLATENT_API_KEY='YOUR_SERVICE_KEY'
Python requires Python 3.10+ and HTTPX:
python -m pip install "httpx>=0.28,<1"
TypeScript requires Node.js 24+ and uses its built-in fetch. The cURL examples require curl and jq.
Save the displayed files and the selected client in one directory. Run the client there with python example.py, node example.ts or bash example.sh. Downloads use the example's name in place of example.
- Python
- TypeScript
- cURL
import json
import os
from pathlib import Path
import httpx
request = {
"input": "Extract the amounts from this receipt: train ticket EUR 12.50; lunch EUR 7.50. Return the line amounts and their total in cents.",
"policy": {
"engine": "opa",
"entrypoint": "data.totals.allow",
"modules": {
"checked-sum.rego": Path("checked-sum.rego").read_text(encoding="utf-8"),
},
},
"policy_max_retries": 8,
"schema": json.loads(Path("checked-sum.schema.json").read_text(encoding="utf-8")),
}
with httpx.Client(
base_url="https://api.blatent.ai",
headers={"Authorization": f"Bearer {os.environ['BLATENT_API_KEY']}"},
timeout=195,
) as client:
response = client.post("/v1/resolve", json=request)
response.raise_for_status()
print(json.dumps(response.json()["output"]))
import { readFileSync } from "node:fs";
const request = {
"input": "Extract the amounts from this receipt: train ticket EUR 12.50; lunch EUR 7.50. Return the line amounts and their total in cents.",
"policy": {
"engine": "opa",
"entrypoint": "data.totals.allow",
"modules": {
"checked-sum.rego": readFileSync("checked-sum.rego", "utf8"),
},
},
"policy_max_retries": 8,
"schema": JSON.parse(readFileSync("checked-sum.schema.json", "utf8")),
};
const response = await fetch(
"https://api.blatent.ai" + "/v1/resolve",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BLATENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(request),
signal: AbortSignal.timeout(195_000),
},
);
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
console.log(JSON.stringify((await response.json()).output));
set -eu
body=$(jq -n --argjson body '{"input":"Extract the amounts from this receipt: train ticket EUR 12.50; lunch EUR 7.50. Return the line amounts and their total in cents.","policy":{"engine":"opa","entrypoint":"data.totals.allow","modules":{}},"policy_max_retries":8}' \
--slurpfile f0 'checked-sum.schema.json' \
--rawfile f1 'checked-sum.rego' \
'$body | setpath(["schema"]; $f0[0]) | setpath(["policy","modules","checked-sum.rego"]; $f1)')
curl --fail-with-body --silent --show-error \
"https://api.blatent.ai/v1/resolve" \
-H "Authorization: Bearer $BLATENT_API_KEY" \
-H 'Content-Type: application/json' \
--data-binary "$body"
Illustrative output: {"amounts_cents":[1250,750],"total_cents":2000}.
| Candidate | Policy result |
|---|---|
[1250, 750], total 2000 | Allowed |
[1250, 750], total 1999 | Denied |
[-1, 2001], total 2000 | Denied: a line is not positive |
What this does and does not check
The policy guarantees the arithmetic relationship. It does not prove that every receipt amount was extracted correctly. A different set of positive amounts could also satisfy the sum. Evaluate extraction separately, or add checks against trusted source values when those are available.
For an example that checks extracted literals against the original prose and lets the model construct a formula, see Extract facts and calculate.
If all amounts are already known and summing is the entire task, calculate the answer in ordinary code. This pattern is useful when interpretation is necessary and calculation errors must not reach the caller.
Struct may revisit a failed candidate within its retry budget. If it cannot find a compliant result, it returns an error. See policy recovery.