Decision Models¶
Since v1.3.0, LatteReview can review with decision models (also called System One models) in addition to LLMs. v1.3.0 added TypeSafe's Jev; v1.4.0 adds Perplexity's pplx-decider and OpenAI's gpt-6-luna through their Decisions APIs. This page explains what decision models are, how they differ from LLM reviewers, how the three models compare, and how to use them well.
What is a decision model?¶
An LLM reviewer reads a prompt and generates text: LatteReview asks it for JSON with a score and a written reasoning. A decision model generates nothing. It reads a text state (here, an article's title and abstract) and a set of typed questions, and returns a probability for every allowed answer. Three question types exist:
| Question | You define | The answer |
|---|---|---|
Noul (yes/no) |
an instruction, optionally what "true" and "false" mean | the probability of "yes" |
Choice |
named options (up to 255 with Jev), optionally with descriptions | the chosen option, a probability per option, and a confidence |
Score |
2-10 ordered levels | the expected level, the most likely level, a probability per level, and a confidence |
Three decision models are supported:
| Model | Maker | How to use it | Price (input tokens; output is free) | Screening cost per 1,000 abstracts* |
|---|---|---|---|---|
Jev (jev-latest) |
TypeSafe AI | SystemOneProvider() or backend="openrouter" |
$0.042 per million | about $0.06 |
pplx-decider (pplx-decider-v1.1-27b) |
Perplexity | backend="perplexity", or backend="openrouter", model="perplexity/pplx-decider-v1.1-27b" |
$0.02 per million | about $0.05 |
| gpt-6-luna | OpenAI | backend="openai" (OpenAI's Decisions API) |
$0.10 per million | about $0.16 |
* Measured with DecisionTitleAbstractReviewer on the evaluation below. For comparison, TitleAbstractReviewer with
the gpt-6-luna LLM costs about $0.10-0.13 per 1,000 abstracts, and larger LLMs one to two orders of magnitude more.
All three answer in well under a second per article. In our evaluation, pplx-decider ranked articles best and cost least; gpt-6-luna's Decisions API ranked worse than the LLM baseline on long, multi-part criteria (see Comparing the three models).
Decision models vs LLM reviewers¶
LLM reviewers (TitleAbstractReviewer, ...) |
Decision reviewers (DecisionTitleAbstractReviewer, ...) |
|
|---|---|---|
| Output | JSON generated by the model | probabilities; answers can never be off-schema |
| Written reasoning | yes (reasoning="brief" or "cot") |
no. The reasoning column is generated by LatteReview from the probabilities |
| Certainty | the model's self-reported number | a probability, which can be thresholded for a target recall |
| Inputs | text and images | text only in LatteReview (Jev: English-optimized, up to 32k tokens for the state plus the longest question) |
| Multi-step reasoning | yes | no multi-hop reasoning; keep hard, multi-step criteria for an LLM |
| Prompting | prompt templates, examples, model_args |
typed questions only; examples, reasoning, model_args and prompt templates raise an error |
| Cost per 1,000 abstracts | about $0.13 (gpt-6-luna) to several dollars (gpt-6-sol, gpt-6-astra) |
about $0.05-0.16 |
| Latency per article | one to several seconds | 0.2-0.8 seconds (Jev and pplx-decider are paced to about 16 articles per second by default) |
| Repeatability | answers can change between runs | pplx-decider and gpt-6-luna repeated identical requests exactly; Jev's uncertain answers vary (we saw 0.62-0.84 for one article) |
| Refusals | the model may refuse in text | OpenAI may decline single questions; the answer is then None (see Refusals) |
Decision reviewers are BasicReviewer subclasses, so they work in any ReviewWorkflow, next to LLM reviewers, with
the usual round-{R}_{name}_{key} columns, filters, cost tracking and memory. backstory is accepted but ignored.
Backends¶
SystemOneProvider works the same way with every backend. Most speak the /v1/systemone protocol; OpenAI's Decisions
API has its own request format, which the provider translates, so reviewers see the same answers either way.
from lattereview.providers import SystemOneProvider
provider = SystemOneProvider() # TypeSafe, jev-latest, reads TYPESAFE_API_KEY
provider = SystemOneProvider(backend="openrouter") # OpenRouter, ~typesafe/jev-latest, reads OPENROUTER_API_KEY
provider = SystemOneProvider(backend="perplexity") # Perplexity, pplx-decider-v1.1-27b, reads PERPLEXITY_API_KEY
provider = SystemOneProvider(backend="openai") # OpenAI, gpt-6-luna, reads OPENAI_API_KEY
provider = SystemOneProvider(backend="openrouter", model="perplexity/pplx-decider-v1.1-27b") # pplx-decider via OpenRouter
provider = SystemOneProvider(model="jev-1.13.0") # pin a version instead of the -latest alias
provider = SystemOneProvider(base_url="http://localhost:3000") # a self-hosted server (see below)
provider = SystemOneProvider(
base_url="https://your-gateway.example/v1/systemone", # any other /v1/systemone gateway
api_key="...",
model="...",
input_price_per_million=0.042, # for cost tracking (custom URLs default to 0)
)
| Backend | How to get access | Default model | Key variable |
|---|---|---|---|
typesafe (default) |
an API key from the TypeSafe console | jev-latest |
TYPESAFE_API_KEY |
openrouter |
an OpenRouter API key; billed through your OpenRouter credits. Also serves perplexity/pplx-decider-v1.1-27b |
~typesafe/jev-latest |
OPENROUTER_API_KEY |
perplexity |
an API key from the Perplexity console (Decisions API) | pplx-decider-v1.1-27b |
PERPLEXITY_API_KEY (or PERPLEXITYAI_API_KEY) |
openai |
an OpenAI API key (Decisions API, public beta since October 2026) | gpt-6-luna |
OPENAI_API_KEY |
custom base_url |
your own server or gateway; the URL may end with /v1/systemone or /v1/decisions, or with neither |
the server's default | none (pass api_key= if needed) |
For a custom server that speaks OpenAI's Decisions format (e.g., another region or a gateway), pass
protocol="openai" with base_url.
Other options: timeout (default 60 s), max_retries (default 3; retries 429, 529, 5xx and timeouts with
exponential backoff, honoring Retry-After), and requests_per_minute. Requests are paced below each service's limit
by default: 1,000 per minute for TypeSafe and OpenRouter (Jev allows 1,200) and 500 for Perplexity (10 per second).
OpenAI allows 30,000 per minute, so OpenAI and custom URLs are not paced. Pass requests_per_minute=0 to turn pacing
off.
On OpenRouter, the cost comes from the response; elsewhere it is computed from the reported input tokens and the list price.
Other decision models¶
Newer hosted decision models that speak /v1/systemone work without a named backend:
# Microsoft-Decision-1 through OpenRouter (tested with the live conformance suite in 1.4.1)
provider = SystemOneProvider(backend="openrouter", model="microsoft/microsoft-decision-1")
# Celeris-1 Decision and Liquid AI's d1-ultra through their own APIs (not yet tested with LatteReview)
provider = SystemOneProvider(base_url="https://inference.celeris.ai/celeris-1-decision", model="celeris-1-decision",
api_key="...", input_price_per_million=0.04)
provider = SystemOneProvider(base_url="https://api.liquid.ai/decisions", model="d1-ultra",
api_key="...", input_price_per_million=0.042)
Pass input_price_per_million with a custom base_url so the cost is tracked; otherwise it is reported as 0.
Azure AI Foundry takes its API keys for Microsoft-Decision-1 in an api-key header, which SystemOneProvider does not
send, so use the model through OpenRouter.
Screening titles and abstracts¶
from lattereview.agents import DecisionTitleAbstractReviewer
reviewer = DecisionTitleAbstractReviewer(
provider=SystemOneProvider(),
name="Jev",
inclusion_criteria={1: "The study must involve CT scans.", 2: "The study must use deep learning."},
exclusion_criteria={1: "The study must not include PET scans."},
)
Criteria can be a string (one criterion), a list, or a numbered dict. For each article, the reviewer sends one
request with a 5-level evaluation score (mirroring the 1-5 rubric of TitleAbstractReviewer), an overall
include question, and one yes/no question per criterion. It returns these columns:
| Column | Meaning |
|---|---|
evaluation |
int 1-5, the most likely rubric level; existing filters such as evaluation >= 4 keep working |
include_probability |
the probability that the article meets all inclusion and no exclusion criteria |
confidence |
Jev's confidence in the evaluation score (None if the backend does not report one) |
criteria |
{"inclusion": {criterion: p(met)}, "exclusion": {criterion: p(applies)}} |
reasoning |
a sentence generated by LatteReview from the probabilities, e.g., P(include)=0.03; rated 'absolutely exclude'. Likely fails inclusion 1 (p=0.02: 'The study must involve CT scans.'). |
The raw normalized answers (per-level probabilities, confidence) are kept under _answers in the output column.
Scoring and extraction¶
DecisionScoringReviewer is the counterpart of ScoringReviewer. score_descriptions become the answer levels,
and certainty is the confidence x 100, on the same 0-100 scale:
from lattereview.agents import DecisionScoringReviewer
reviewer = DecisionScoringReviewer(
provider=SystemOneProvider(),
name="Validation",
scoring_task="How strong is the validation of the model in this study?",
scoring_set=[1, 2, 3],
score_descriptions={1: "no validation", 2: "internal validation only", 3: "external validation"},
)
# Columns: score (a value from scoring_set), certainty (0-100), probabilities ({score: p})
For your own questions, use DecisionReviewer. Each question becomes a column: the chosen option for Choice, the
probability of "yes" for Noul, and the expected 0-based level for Score:
from lattereview.agents import DecisionReviewer
from lattereview.providers import Noul, Choice, Score
reviewer = DecisionReviewer(
provider=SystemOneProvider(),
name="Extractor",
questions={
"modality": Choice("What is the main imaging modality?", ["CT", "MRI", "X-ray", "ultrasound"]),
"deep_learning": Noul("Does the study use deep learning?"),
"quality": Score("How rigorous is the evaluation?", ["weak", "moderate", "strong"]),
},
)
This covers categorical extraction. Free-text extraction (e.g., "list the reported complications") still needs an LLM
AbstractionReviewer.
You can also call the provider directly:
result = await provider.decide(state="Title: ...\nAbstract: ...", questions={"rct": Noul("Is this an RCT?")})
result.answers["rct"].value # probability of yes
result.cost, result.input_tokens
Cost model¶
The three services bill a request's questions differently. Input tokens for the same abstract (about 750 tokens) with 1, 2, 4 and 8 short yes/no questions:
| 1 question | 2 | 4 | 8 | Billing | |
|---|---|---|---|---|---|
| Jev | 1,134 | 1,150 | 1,182 | 1,246 | the state once per request; about 16 tokens per extra question |
| gpt-6-luna | 902 | 1,055 | 1,361 | 1,973 | the state once per request; about 150 tokens per extra question |
| pplx-decider | 926 | 1,852 | 3,704 | 7,408 | the state once per question (the same directly and through OpenRouter) |
So:
- Ask all questions about an item in one request. The decision reviewers always do.
- On Jev, one question per criterion is nearly free. On gpt-6-luna each extra question adds a small fixed cost, and on pplx-decider each one costs as much as the first. pplx-decider's low price still made it the cheapest model in our evaluation, but a reviewer with many criteria costs proportionally more.
- Long criteria repeated in several questions cost more than the abstract itself; keep criteria concise.
Refusals¶
OpenAI's Decisions API may decline to answer a single question. In our evaluation, 4 of about 50,000 questions were declined, all on ordinary medical abstracts (e.g., a spine-surgery study). LatteReview does not fail or retry the item:
- The refused answer has
refused=Trueandvalue=None(in_answers), and the matching output column is None. ForDecisionTitleAbstractReviewer, the generatedreasoningnames the unanswered questions. - The other answers of the same request are kept.
- A warning is printed the first time a reviewer gets a refusal.
Send such items on to an LLM or a human. A None value fails comparisons such as 0.1 <= p < 0.9, so check for it
explicitly in workflow filters, as in the hybrid example below.
Confidence-gated hybrid workflows¶
Jev is cheap enough to screen everything, and an LLM can then review only the articles Jev is unsure about. Filters are plain row functions, so no special workflow is needed:
import pandas as pd
from lattereview.providers import SystemOneProvider, OpenAIProvider
from lattereview.agents import DecisionTitleAbstractReviewer, TitleAbstractReviewer
from lattereview.workflows import ReviewWorkflow
jev = DecisionTitleAbstractReviewer(provider=SystemOneProvider(), name="Jev",
inclusion_criteria=inclusion, exclusion_criteria=exclusion)
llm = TitleAbstractReviewer(provider=OpenAIProvider(model="gpt-6-luna"), name="LLM",
inclusion_criteria=inclusion_text, exclusion_criteria=exclusion_text)
workflow = ReviewWorkflow(workflow_schema=[
{"round": "A", "reviewers": [jev], "text_inputs": ["title", "abstract"]},
{"round": "B", "reviewers": [llm], "text_inputs": ["title", "abstract"],
"filter": lambda row: pd.isna(p := row["round-A_Jev_include_probability"]) or 0.1 <= p < 0.9},
])
The pd.isna check also routes articles whose include question was refused (see Refusals).
Jev's probabilities are mostly close to 0 or 1, so a band of [0.1, 0.9) routed about 20% of articles to the LLM in our evaluation (5% to 43% depending on the dataset). Replacing Jev's decision with the LLM's inside the band gave almost the same recall and precision as reviewing everything with the LLM, for about one fifth of the LLM calls. The LLM is most useful on criteria that need an extra reasoning step, such as telling a segmentation study from a classifier study.
See the hybrid tutorial for a complete pipeline with measurements.
Choosing a threshold¶
A probability cutoff fitted on one dataset (or one backend) does not carry over to another. With a labeled sample of
your own data, suggest_threshold finds the highest cutoff that still reaches a target recall:
from lattereview.utils import suggest_threshold
suggest_threshold(labeled_df, "round-A_Jev_include_probability", "label", target_recall=0.95)
# {'threshold': 0.08, 'recall': 0.96, 'precision': 0.41, 'n_included': 212, 'wss': 0.52}
Articles close to the cutoff can land on either side of it when re-run, because uncertain answers vary slightly between identical requests.
wss is the work saved over sampling: the fraction of articles you would not need to read at that cutoff, minus the
recall given up.
Evaluation¶
We ran DecisionTitleAbstractReviewer (Jev 1.13) on all 11,793 articles of LatteReview's v1 evaluation: three
custom searches over 978 radiology AI articles and six SYNERGY systematic-review datasets. The criteria were passed
exactly as the v1 LLM reviewers got them. The comparison is the stored v1 LLM decision (gemini-2.5-flash and
gpt-4o-mini, with gpt-4o resolving disagreements). The full run took 14 minutes and cost $0.71. The notebook and
per-article results are in
evaluation/decision_evaluation.ipynb.
| Dataset | Articles | Included | Jev AUC | LLM AUC | Jev WSS@95 | LLM WSS@95 | Jev recall at p ≥ 0.5 | LLM recall at ≥ 3 |
|---|---|---|---|---|---|---|---|---|
| search1 | 978 | 697 | 0.957 | 0.937 | 0.25 | 0.00 | 0.92 | 0.92 |
| search2 | 978 | 367 | 0.875 | 0.818 | 0.23 | 0.25 | 0.85 | 0.84 |
| search3 | 978 | 55 | 0.865 | 0.789 | 0.32 | 0.08 | 0.56 | 0.78 |
| Appenzeller-Herzog 2019 | 2,873 | 26 | 0.899 | 0.847 | 0.64 | 0.00 | 0.08 | 0.31 |
| Donners 2021 | 258 | 15 | 0.923 | 0.772 | 0.66 | 0.07 | 0.67 | 0.73 |
| Jeyaraman 2020 | 1,175 | 96 | 0.795 | 0.708 | 0.00 | 0.00 | 0.00 | 0.02 |
| Meijboom 2021 | 882 | 37 | 0.941 | 0.902 | 0.76 | 0.54 | 0.14 | 0.84 |
| Muthu 2021 | 2,719 | 336 | 0.735 | 0.728 | 0.17 | 0.27 | 0.08 | 0.24 |
| Oud 2018 | 952 | 20 | 0.969 | 0.955 | 0.87 | 0.80 | 0.70 | 0.80 |
| Mean | 0.884 | 0.828 | 0.43 | 0.22 | 0.44 | 0.61 |
AUC measures how well a score ranks included articles above excluded ones. WSS@95 is the work saved over sampling at 95% recall: the fraction of articles a reviewer would not need to read at the cutoff that still finds 95% of the included ones, minus the 5% recall given up.
What this shows:
- Jev ranks articles better than the v1 LLM reviewers on every dataset (Muthu is a tie), and saves about twice as much screening work at 95% recall.
- A cutoff of 0.5 is too strict for long, multi-part criteria. On the SYNERGY datasets, whose criteria are
paragraphs with many conditions, Jev's
include_probabilityrarely exceeds 0.5 for included articles. The cutoffs that reach 95% recall ranged from 0.01 to 0.23. Rank by the probability, or fit a cutoff withsuggest_thresholdon labeled data, instead of using 0.5. Splitting long criteria into separate items (a list or numbered dict) also gives you one probability per criterion. - Use the probabilities, not the
evaluationinteger, for ranking. The 1-5evaluationcolumn is kept for compatibility with LLM filters, but it is coarse (mean AUC 0.79). - Other signals rank about as well as
include_probability: the expected evaluation score (mean AUC 0.889) and the product of the per-criterion probabilities (0.894, best on 4 of 9 datasets). None was consistently best, soinclude_probabilityis the recommended signal. - The labels of the custom searches come from structured metadata and contain some noise: several articles labeled "CT" are PET/CT studies that both Jev and the LLMs excluded.
Comparing Jev, pplx-decider and gpt-6-luna¶
For v1.4.0 we ran the same DecisionTitleAbstractReviewer, with the same criteria and questions, on all 11,793
articles with each model: Jev through OpenRouter, pplx-decider through OpenRouter (served by Perplexity), and
gpt-6-luna through OpenAI's Decisions API. The notebook and per-article results are in
evaluation/decision_model_comparison.ipynb.
Jev's numbers differ slightly from the table above because v1.4.0 leaves the workflow's task ID out of the model's
input and because Jev's uncertain answers vary between runs.
AUC of include_probability (the LLM column is the stored v1 LLM decision):
| Dataset | Jev | pplx-decider | gpt-6-luna | LLM |
|---|---|---|---|---|
| search1 | 0.960 | 0.951 | 0.946 | 0.937 |
| search2 | 0.874 | 0.886 | 0.886 | 0.818 |
| search3 | 0.863 | 0.864 | 0.832 | 0.789 |
| Appenzeller-Herzog 2019 | 0.888 | 0.947 | 0.582 | 0.847 |
| Donners 2021 | 0.915 | 0.965 | 0.857 | 0.772 |
| Jeyaraman 2020 | 0.762 | 0.777 | 0.500 | 0.708 |
| Meijboom 2021 | 0.939 | 0.945 | 0.881 | 0.902 |
| Muthu 2021 | 0.735 | 0.752 | 0.579 | 0.728 |
| Oud 2018 | 0.968 | 0.970 | 0.952 | 0.955 |
| Mean | 0.878 | 0.895 | 0.779 | 0.828 |
| Jev | pplx-decider | gpt-6-luna | LLM | |
|---|---|---|---|---|
| Mean WSS@95 | 0.38 | 0.51 | 0.12 | 0.22 |
Mean AUC of expected_evaluation / criteria_product |
0.887 / 0.892 | 0.903 / 0.914 | 0.872 / 0.805 | |
| Mean recall / precision at p ≥ 0.5 (LLM: score ≥ 3) | 0.43 / 0.54 | 0.63 / 0.42 | 0.43 / 0.58 | 0.61 / 0.37 |
| Articles with 0.1 ≤ p < 0.9 | 16% | 12% | 4% | |
| Hybrid band [0.1, 0.9) + LLM: routed / recall / precision | 19% / 0.59 / 0.37 | 13% / 0.62 / 0.45 | 6% / 0.49 / 0.47 | 100% / 0.61 / 0.37 |
| Cost of the full run (11,793 articles) | $0.70 | $0.61 | $1.87 | |
| Run time | 12 min (paced) | 13 min (paced) | 3 min (16 concurrent, not paced) | |
| Refused questions | 0 | 0 | 4 |
What this shows:
- pplx-decider was the best decision model: the highest AUC on 7 of 9 datasets and the most screening work saved at 95% recall, at the lowest cost. Its probabilities are less extreme than Jev's, so a 0.5 cutoff kept more included articles (mean recall 0.63 vs 0.43), and its hybrid pipeline matched the recall of reviewing everything with the LLM (0.62 vs 0.61) with better precision (0.45 vs 0.37), while sending only 13% of articles to the LLM.
- gpt-6-luna's Decisions API was the weakest on the SYNERGY datasets, whose criteria are long paragraphs with many
conditions. It returned exactly 0.00 for nearly every article there, included ones too (OpenAI rounds probabilities
to two decimals), which leaves nothing to rank by: AUC 0.50 on Jeyaraman and 0.58 on Appenzeller-Herzog and Muthu.
Its expected evaluation score ranked much better (mean AUC 0.872), so if you use gpt-6-luna, rank by
output["_answers"]["evaluation"]["value"]rather thaninclude_probability, and prefer short, single-condition criteria. It was also the most expensive of the three. - The three models agree on most decisions (94-98% at p ≥ 0.5), but they rank articles differently (rank correlation 0.93 between Jev and pplx-decider, 0.65-0.68 with gpt-6-luna), and their fitted cutoffs differ a lot. Fit a cutoff per model: one fitted for Jev does not carry over to another model.
Self-hosting: OpenJev and Kev¶
Open models that serve the same /v1/systemone API can run on your own hardware. Point base_url at them:
-
OpenJev 27B (openjev), an independent open model. The weights are licensed CC BY-NC 4.0: research and non-commercial use only. The Apache-2.0 server
openjev-serverruns it with vLLM or, on Apple silicon, MLX. On a 32 GB Mac, use the 4-bit MLX build (about 15 GB): -
Kev (jaredpalmer/kev, Apache-2.0): Qwen base models with a LoRA adapter, from 0.8B to 27B parameters.
python -m kev.serve --run jaredpalmer/kev-4b --port 8009, thenSystemOneProvider(base_url="http://127.0.0.1:8009", api_key="local"). - openjev-sglang serves any SGLang model by reading first-token probabilities, which its authors describe as not calibrated.
- pplx-decider: Perplexity publishes the weights under Apache-2.0 on Hugging Face. We have not tested a self-hosted
server; any server that speaks the same protocol works through
base_url.
Known differences between backends¶
A compatible API is not an equivalent model. Before relying on another backend, check it on labeled data.
We ran the same 200 articles (100 each from custom searches 2 and 3, stratified by label) through three backends
with DecisionTitleAbstractReviewer:
| Jev 1.13 (TypeSafe) | OpenJev 27B, 4-bit MLX (openjev-server 0.2.0) |
Kev-4B (bf16, MLX) | |
|---|---|---|---|
| AUC, search 2 / search 3 | 0.873 / 0.838 | 0.872 / 0.826 | 0.854 / 0.814 |
| Same include decision as Jev (p ≥ 0.5) | - | 98% / 92% | 77% / 51% |
| Articles with 0.05 < p < 0.95 | 51% / 66% | 34% / 64% | 91% / 99% |
| Median confidence (evaluation score) | 0.93 / 0.77 | 0.93 / 0.68 | 0.36 / 0.50 |
| Time per article (6-8 questions) | about 0.2 s; about 16 articles/s at the default pace | 9.5-13.5 s | 1.2-2.2 s |
| Cost and license | $0.042 per million input tokens | free on your hardware; weights CC BY-NC 4.0 | free on your hardware; Apache-2.0 |
The local models ran on an Apple M5 with 32 GB of memory.
- OpenJev ranks and decides almost like Jev, but on a laptop it is about 50 times slower. A short one-question request took about 0.9 s, and each question with an abstract-length state added about 0.7 s.
- Kev-4B ranks almost as well, but its probabilities are much less extreme and its confidence values are lower. A cutoff or an uncertain band tuned on Jev would route almost every article to the LLM with Kev. Fit thresholds per backend.
- Answers are not fully deterministic on any backend we tried. Confident answers (probabilities near 0 or 1) repeat, but uncertain ones vary between identical requests: six identical requests for one borderline article returned an include probability of 0.62-0.84 on TypeSafe and 0.60-0.88 on OpenRouter. Save your results instead of re-running, and expect articles near a cutoff to land on either side of it.
- Response details differ. OpenRouter reports the cost in the response. OpenJev returns probabilities with four decimals and
counts tokens differently (its prompt template makes requests look 2-4 times larger). TypeSafe requires a
model; local servers use their own default when it is omitted. Missing fields (a missingconfidence,legendorchoice) are handled bySystemOneProvider, so reviewers see the same answer format everywhere. - Jev limits a choice question to 255 options; OpenJev evaluates up to 52 options in one pass. Validation errors from the server are shown in the error message.
The v1.4.0 backends:
- pplx-decider and gpt-6-luna gave identical answers to identical requests in our tests (six repeats each on
borderline articles). But all three models change their answers on borderline articles when unrelated text in the
input changes. For one article, gpt-6-luna returned an include probability of 0.05 without the workflow's
Review Task IDline, and 0.50 or 0.65 with two different IDs. Since v1.4.0 the decision reviewers leave that line out, so an article gets the same input, and with these two models the same answer, whatever its row or round. - OpenAI rounds probabilities to two decimals, so many articles tie (gpt-6-luna returned 99 distinct
include_probabilityvalues over 11,793 articles, pplx-decider 11,772). Ties make ranking and fitted cutoffs coarse. - OpenAI's yes/no questions have no answer descriptions. LatteReview adds a
Noul'strue/falsedescriptions to the instructions ("Answer true if: ..."). Testing on two datasets showed no difference either way. - OpenAI may refuse single questions (see Refusals); Jev and Perplexity have not refused any in our tests.
- Perplexity accepts up to 128 questions per request and inputs up to 262,144 tokens. Its responses report tokens but not cost, so LatteReview prices them at $0.02 per million input tokens. Through OpenRouter, the cost is reported.