Probabilities you can trust
Send the true answer whenever you learn it. From 30 labels per question, Curva calibrates the answers when that helps, so a 0.9 means right about 90% of the time on your data. Calibration →


You give Curva some data and typed questions. It returns typed answers with probabilities, and never free text.
Curva means “curve”: the calibration curve is what keeps its probabilities honest. Once calibrated on your data, when Curva says 90%, it is right about 90% of the time.
Curva is a small Rust server with Python and TypeScript SDKs. It works with any OpenRouter or OpenAI-compatible model, free ones included, using your own key. Curva is free to use and runs on your own servers.
pip install curva-aiexport OPENROUTER_API_KEY=sk-or-v1-... # any OpenRouter key; free models workimport curvafrom curva import Choice, Score, Noul
client = curva.local() # starts a private server for this processd = client.decide( state={"ticket": "I was charged twice for order A-104. Please refund the duplicate!"}, questions={ "team": Choice("Which team should handle this?", {"billing": "payments, refunds", "technical": "bugs", "sales": "pricing"}), "frustration": Score("How frustrated is the customer?", ["calm", "annoyed", "angry"]), "refund": Noul("The customer explicitly asks for a refund"), },)
print(d["team"].choice, d["team"].confidence) # billing 0.9999print(d["frustration"].score) # 0.65 (expected level, 0 = calm)print(d["refund"].noul) # 0.999 (P(yes))npm install curva-aiimport { Curva, choice, score, noul } from "curva-ai";
const curva = new Curva(); // CURVA_BASE_URL (default http://localhost:7777), CURVA_API_KEYconst d = await curva.decide( { ticket: "I was charged twice for order A-104. Please refund the duplicate!" }, { team: choice("Which team should handle this?", { billing: "payments, refunds", technical: "bugs", sales: "pricing" }), frustration: score("How frustrated is the customer?", ["calm", "annoyed", "angry"]), refund: noul("The customer explicitly asks for a refund"), },);
d.answers.team.choice; // "billing" | "technical" | "sales" | "none_of_these"d.answers.team.confidence;d.answers.frustration.score;d.answers.refund.noul; // P(yes)The TypeScript SDK is a client for a running server. Start one with curva serve or with Docker. See TypeScript.
curl -s localhost:7777/v1/decide -H 'content-type: application/json' -d '{ "state": {"ticket": "The app crashes on launch"}, "questions": {"team": {"type": "choice", "instructions": "Which team?", "options": {"billing": "", "technical": ""}}}}'Any language can call the server directly. The full contract is in the HTTP API reference.
docker run --rm -p 127.0.0.1:7777:7777 -e OPENROUTER_API_KEY ghcr.io/itsmohitrohilla/curva \ serve --addr 0.0.0.0:7777 --no-authThis runs a local server without API keys, for trying things out. For production, create a key first: see Self-hosting.
teamChoicefrustrationScorerefundNoulEvery answer is one of the labels you declared, with a probability for each label. There is no reply text to parse.
Probabilities you can trust
Send the true answer whenever you learn it. From 30 labels per question, Curva calibrates the answers when that helps, so a 0.9 means right about 90% of the time on your data. Calibration →
Never free text
Every answer is a typed Choice, Score, Noul (yes/no) or Multi, mapped only onto the labels you declared and validated before it reaches you. Questions →
Any model
Any OpenRouter or OpenAI-compatible model, free or paid, with your own key. Curva reads probabilities from logprobs when the model has them, and asks for them otherwise.
Stable and consistent
Options are asked in both orders and averaged, which cancels position bias. An escape option means the model is never forced into a wrong pick. Trust →
Fast and cheap on repeats
Repeat decisions come from the cache in about 0 ms at $0. All questions for one state go in a single request.
More than one model
Blend models in a council, escalate only unsure answers in a cascade, or race models for the fastest valid answer. Multi-model →
Yours to run
API keys, an audit log that never stores your data, privacy: strict, Prometheus metrics and a small Docker image. Self-hosting →
v1 contract, for any language.Looking for Crowkis? Its documentation lives at crowkis.com/docs.