# Oracles

> What an oracle is, what is available to you, and why the names are what they are.

An oracle takes a request in plain language and returns an artifact that has passed
its checks. It is the unit you buy, the unit you call and the unit you are
billed for. Today `chart`, `vector-graphics` and `coverage-policy` each cost **1 credit** per
answer, `valid` or `invalid`; an `error` is free.

## Available to you

The console lists the oracles your organisation can call, read from the
deployment rather than written here — so it cannot describe an oracle that is
not actually served.

[Browse the oracles](https://app.formalogic.io/oracles)

Real answers from `chart`, as they were returned:


The same list over HTTP is [`GET /v1/oracles`](https://docs.formalogic.io/list-oracles).

## Names are domain words

You ask the `chart` oracle, the `vector-graphics` oracle or the `coverage-policy`
oracle — named for what you want, not for what produces it.

That is a deliberate boundary. You are buying an answer about charts. What sits
behind the name is ours to change, and nothing about it appears in the name or
in the response, so an integration of yours cannot come to depend on it and then
break when we improve it.

## Formats

An oracle declares the formats it can produce. Pass `format` to choose one; omit
it for `default_format`.

Asking for a format an oracle cannot produce is refused **and the refusal names
the ones it can**. That is the one error an agent can act on without a human,
which is why it is shaped that way rather than as a bare rejection.

### A format is not always a superset of the others

Formats of the same oracle are not equally expressive. A request that succeeds for one can be refused for
another — the refusal is about what that format can express, never about whether
your request was reasonable.

`chart` is the live example. `vega-lite` expresses the most; `gnuplot` expresses
less, and refuses rather than quietly dropping what it cannot carry — time axes,
values it would have to fetch rather than be given, and transformations that
happen before plotting rather than during it.

`plotly`, `echarts` and `chartjs` return a document for that library — a Plotly
figure, an ECharts option, a Chart.js configuration — with every number already
computed, so the same chart shows the same values in all of them. Because the numbers are computed
before you receive them, all three need the data inline and refuse a chart whose
values are at an address. Beyond that:

| Format | Also refuses |
|---|---|
| `plotly` | trails, maps, images, small multiples (`row`, `column`) of anything but bars, lines, areas and points or split by dates or numbers, a radial value that is negative or falls outside the circle, an arc layered with another mark or with arcs on other scales, a `shape` on anything but a point or read from an ordered field, a `size` on anything but a point, an `opacity` on anything but a point or a bar, a `size` or `opacity` read from unordered categories, a `range` of colours on a numeric colour, colour on two channels at once, and a range between dates written with a time zone |
| `echarts` | the same, axis `format` strings, a bar or band running between two dates, and a bar ranged along x unless y is a category |
| `chartjs` | the same as `echarts` (except bars ranged along x), and box plots, heatmaps, text labels, a colour scale, a line across the whole plot, a range along a date axis, a second view or a band beside a colour legend (except a layer of arcs), an `rgb()` or `hsl()` colour on anything drawn below full opacity, and small multiples |

Where a format cannot carry something without refusing the whole chart, the
answer says so as a warning: `echarts` and `chartjs` take their width and height
from the page, and `chartjs` draws a time axis as categories.

**A refusal of this kind still costs a credit.** Deciding that something cannot
be rendered is the work — the same work as deciding it can. The table above is
here so that is a known limit rather than something you discover one credit at a
time.

`vector-graphics` has the same shape across its three formats. `tikz` and `metapost`
express the most; `pictex` expresses less and refuses rather than approximating
— it has no arbitrary stroke colours, no fills, and no coloured text, and it
names which of those you asked for.

### `vector-graphics` computes the geometry

Describe a figure by what it is, not by where each mark goes: the midpoint of
two points, where two lines meet, a point partway along a ray, a circle tangent
to a line, a line perpendicular to another, a length equal to a distance. The
coordinates in the TikZ you get back are computed for you.

Say in your intent what the figure must show — three points collinear, two
lines perpendicular — and it is checked. A figure that cannot show it comes back
`invalid`, naming what failed and by how much, rather than looking right and
being wrong.

A returned figure is fitted to its canvas, with labels clear of the lines, trees
and rows of boxes laid out from their text, and one consistent style:
construction lines in grey, extensions dashed, the subject in a heavier line,
fills tinted with matching outlines.

Some figures need things `vector-graphics` does not draw: colour gradients, embedded
images, animation, fill patterns, transparency or shading. A request that cannot
be drawn without one is answered `invalid` with `detail` naming the feature,
instead of an approximation. See [Verdicts](https://docs.formalogic.io/verdicts).

### `vector-graphics` returns source, not a picture

The `chart` formats return a document something else can draw. The
`vector-graphics`
formats return **TeX-family source** — `tikz`, `pictex` and `metapost` are all
things you typeset. You get a fragment to place in a document, not an image. To
see it drawn, a `valid` answer carries a `figure` token — see
[Draw a figure](https://docs.formalogic.io/figures).

Two real answers from `vector-graphics`, with the TikZ each one returned:


### `coverage-policy` answers questions about a coverage policy, as JSON

`coverage-policy` takes a question about the Medicare Local Coverage
Determination for total knee arthroplasty (LCD L36575, version 21), such as
"Can I get a knee replacement if I'm under 65?", and returns the parts of that
policy that answer it: the rules, the named conditions and statements they rely
on, and the policy passage each part comes from. Every entry in an answer is
taken from the policy; nothing is written for the question. It records what the
policy says; it does not decide whether a patient qualifies.

Its format, `lookup`, is a document: `result` is an object, and a `valid` answer
carries no `figure` block. The object's keys are `policy`, `review`, `asked`,
`not_found`, `scope`, `rules`, `conditions`, `statements`, `reason_topics`,
`notes`, `passages`, `examples`, `open_questions`, `other_documents` and
`source_passages`.

- `asked` says, in words, which parts of the policy were looked up.
- `not_found` lists the searches that found no entry and the questions no rule
  answers. Search matches exact words, so a search for "X-rays" finds nothing
  in a policy that says "radiography". A question about something the policy is
  silent on, such as an age limit, gets an entry here rather than a guess.
- An entry names the passages it cites under `sources`, by id, and
  `source_passages` holds each passage's pages, section and words.
- `review` says how far the policy's representation has been reviewed. Today it
  is a draft that has not had clinical review.

Send `"views": ["summary"]` with the question to also get the answer as text
a person can read, in `views.summary`. It is built from `result` alone: each
rule and condition by name with its pages, the lists of what must be true, the
policy's own wording quoted, what the policy does not mention, and the review
note. It never names an entry by its id. For "Can I get a knee replacement if
I'm under 65?" it begins:

```text
From Total Knee Arthroplasty (LCD L36575 version 21; revision effective 2025-11-06).

The policy does not mention "age" or "65".

Indications for a first total replacement (indication, pages 7, 8)
The policy says: "Noridian will consider total knee replacement surgery medically necessary in the following circumstances:"
For a first total knee replacement.
It applies when any one of these is true:
- The advanced joint disease pathway (explained below)
- Failure of a previous osteotomy
- Distal femur fracture
```

A question about another policy or another procedure is still answered from
this one policy: the answer is what LCD L36575 says, which may not be what was
asked about.

Callers that send `format: "json"` still get the earlier answer for a passage of
a policy (its scope, rules, conditions and statements). That format is no longer
listed and will be retired.

### What `result` holds

A format that produces a document returns it decoded — `result` is an object.
A format that produces a **script or plain text** returns it as a string. You
already know which, because you named the format and the response echoes it back
beside the result.

## Every plan, every oracle

Nothing is gated by tier. An agent that cannot rely on a tool being there stops
reaching for it, so what a plan buys is [volume](https://docs.formalogic.io/limits), never access.

## New oracles

They appear in `GET /v1/oracles` and in the console — with no change on
your side. If your integration reads the catalogue at start-up rather than
hardcoding names, that is all it takes.
