Skip to content

DocsFeatures

Decision models

Run decision policies, rerank, classifier guardrails, the route classifier and evals on the decision model of your choice, on your own account with its provider.


INFRO is in private early access. Running these examples requires an early-access account, an active trial or subscription entitlement, and your own provider connection. Self-serve purchasing is not generally open. See Early access.

A decision model answers typed questions — the probability that a statement is true, one option of a closed set, a level on a rubric — and generates no text. TypeSafe's Jev was the first; OpenAI, Cloudflare, Perplexity, Inception, Liquid AI, Upstage and Microsoft now ship one. INFRO runs every decision feature on whichever of them you connect, translating each provider's wire into one shape, so a policy, a guardrail check or a route classifier names a model and nothing else changes.

Every call runs on your own account with that provider, which bills it. INFRO charges nothing per decision; the estimated provider cost of each call is on its row in the request log, separate from your INFRO subscription.

The models

ModelConnectImages
typesafe/jev-1.13 — Jev 1.13TypeSafeText and JSON only
openai/gpt-6-luna-decisions — GPT-6 Luna (Decisions)OpenAI (the Decisions API)Data URLs
cloudflare/clef — ClefCloudflare Workers AI (API token and account ID)Data URLs, up to 4
cloudflare/clef-flash — Clef FlashCloudflare Workers AIData URLs, up to 4
cloudflare/clef-omni — Clef OmniCloudflare Workers AIData URLs, up to 4; audio and video are not offered yet
perplexity/pplx-decider-v1.1-27b — pplx-decider v1.1PerplexityData URLs
inception/mercury-decide — Mercury DecideInceptionText and JSON only
liquid/d1 — d1Liquid AIData URLs, up to 8
upstage/solar-decide — Solar DecideUpstage (in beta at Upstage)Text and JSON only
upstage/solar-decide-flash — Solar Decide FlashOpenRouter onlyText and JSON only
microsoft/microsoft-decision-1 — Microsoft-Decision-1Azure OpenAI (a Foundry deployment, mapped to its deployment name)Text and JSON only

An OpenRouter connection reaches every one of them too, on OpenRouter's own System One endpoint. Connect both a provider and OpenRouter, or two providers' models in a route, and a decision fails over between them like any other request.

Name a model by its slug in a decision policy, a guardrail's classifier checks, a route's prompt classifier, POST /v1/rerank, POST /v1/systemone or POST /v1/decisions. On a decision endpoint, gpt-6-luna and openai/gpt-6-luna mean GPT-6 Luna's decisions; on chat completions they mean the chat model. A decision model named on chat completions is refused with 400 decision_model.

POST /v1/decisions in OpenAI's dialect

OpenAI's SDK (client.decisions.create) works against INFRO with the base URL and the key changed. The request runs on your own connections with every INFRO policy applied — key scopes, budgets, routes and failover, the request log — and the model need not be OpenAI's: any decision model answers in OpenAI's shape.

Python
import os
from openai import OpenAI

# The INFRO gateway key, not the OpenAI key: that stays in your INFRO connection.
client = OpenAI(base_url="https://api.infro.io/v1", api_key=os.environ["INFRO_API_KEY"])

decision = client.decisions.create(
    model="gpt-6-luna",  # or any decision model: "cloudflare/clef-flash", "typesafe/jev-1.13"
    input="I was charged twice for my order.",
    questions=[
        {
            "type": "choice",
            "name": "department",
            "instructions": "Which department should handle this complaint?",
            "choices": [
                {"value": "billing", "description": "Payments, invoices, and refunds."},
                {"value": "technical", "description": "Problems using the product."},
                {"value": "other", "description": "Requests outside these categories."},
            ],
        }
    ],
)
print(decision.answers[0])
Response
{
  "id": "req_…",
  "object": "decision",
  "model": "gpt-6-luna",
  "answers": [
    {
      "type": "choice",
      "name": "department",
      "choice": "billing",
      "probabilities": [
        { "value": "billing", "probability": 0.95 },
        { "value": "technical", "probability": 0.03 },
        { "value": "other", "probability": 0.02 }
      ],
      "confidence": 0.93
    }
  ],
  "usage": {
    "input_tokens": 61,
    "input_tokens_details": { "cached_tokens": 0, "cache_write_tokens": 0 },
    "output_tokens": 0,
    "output_tokens_details": { "reasoning_tokens": 0 },
    "total_tokens": 61,
    "cost": 0.0000061,
    "price_source": "list"
  },
  "route": "primary",
  "route_name": null,
  "provider": "openai",
  "connection": "conn_…"
}
  • input is a string, or user messages of input_text and input_image parts (at most 128 images). An image reaches only a model that sees images; a request whose model reads text only moves to its next fallback, and with none left is refused with 400 images_unsupported. Every decision model takes images only as inline data URLs: Cloudflare, Perplexity and Liquid AI are refused a link with 400 image_url_unsupported before any call, and OpenAI's Decisions API refuses one with its own 400.
  • questions are predicate, choice and score, as OpenAI documents them. On a System One model a predicate is a yes/no question, a choice's options are keyed by their text (a boolean value comes back as the boolean), and a score's level reads label: description.
  • A question no model answered readably comes back as OpenAI's {"type": "refusal"}; an unnamed question is answered with name: null.
  • safety_identifier is passed to OpenAI and to no other provider.
  • Responses add id, usage.cost, usage.price_source, route, route_name, provider and connection to OpenAI's fields.
  • POST /v1/decisions/{policy} is a saved decision policy: a different route under the same path.

Images on POST /v1/systemone

POST /v1/systemone takes TypeSafe's body and, for the models that see images, a top-level images array — data URLs, or {content_type, base64} — the extension Cloudflare and Liquid AI add to it. Each provider receives the images in its own form.

Errors

StatusCodeWhen
400decision_modelA decision model was named on chat completions or another endpoint that is not a decision endpoint.
400images_unsupportedThe request carries an image and no model it could reach sees images.
400image_url_unsupportedThe model takes images only inline, and the request linked one.
400invalid_requestThe provider rejected the question set; the message carries its words. Not retried on another key: every key of a provider validates alike.
503no_provider_connectedNo connection serves the model. Connect its provider, or OpenRouter.
↑ ↓ navigate↵ open/ anywhere in docs