Skip to content

Evaluation and modelsMethodology

Same open-weight model, different hosts: a consistency test

The same model name on two inference hosts is not always the same behaviour. Precision, chat templates, limits and batching all differ. Here is a test protocol, the harness code, and how to read what it finds — with no results we did not measure.

By INFRO Editorial

Published

8 min read

A grid of dark tiles with several lit in amber, compared side by side on a dark ground, representing one model tested across hosts

In short

Treat each host of an open-weight model as a separate system to qualify. Record what the host states about precision and limits, measure run-to-run noise on one host first, then run the same checkable prompts on every host and count only differences larger than that noise.

Open-weight models are attractive partly because many hosts serve them. The same model id on four inference providers looks like four interchangeable suppliers: pick on rate or latency, fail over between them, move when one has a bad week. That is true only if the four behave the same, and nothing guarantees they do.

This article is a test design. It describes what can differ between hosts, a protocol for measuring it, the harness to run it, and how to interpret a difference. It publishes no results, because we did not run it for this article, and any host comparison you read should come with the method and noise band behind it.

What differs when two hosts serve the same weights

  1. Weights

    Precision and quantization
    BF16, FP8, FP4 or a mixed scheme. Hosts choose, and not every host says which.
  2. Prompt

    Chat template
    How messages, tools and reasoning are rendered into tokens. gpt-oss, for one, only works with its harmony format.
  3. Decoding

    Sampler defaults and support
    Default temperature, top_p, penalties, seed; which parameters are honoured or silently ignored.
  4. Limits

    Context and output caps
    A host may serve a shorter window or a lower max output than the model card.
  5. Features

    Tools and JSON modes
    Tool-call parsing and schema enforcement are host code, not model weights.
  6. Runtime

    Batching and kernels
    Output can vary with server load even at temperature 0.
Six places where two hosts serving the same model id can diverge. Only the first is about the weights themselves.

Precision. Hosts routinely serve reduced-precision weights. Together describes quantizing to FP8, FP4 and hybrid precision with calibration and selective mixed precision on sensitive paths, and calls the result near-lossless — its own claim1. Fireworks notes that a deployment uses the FP16 checkpoint by default, that each model lists the precisions it has been prepared for, and that quantization can change model numerics and introduce small changes to the output2. Groq serves models with its TruePoint numerics, which it says reduce precision only where accuracy is not affected3. DeepInfra prints fp8 or fp4 on each model listing4. Some models also ship quantized by their authors: gpt-oss-120b's MoE weights are MXFP4 in the release itself5.

Chat template. Messages, tool definitions and reasoning settings must be rendered into tokens the way the model was trained. The gpt-oss card says the models should only be used with the harmony response format and will not work correctly otherwise5. A host with a subtly different template produces subtly different behaviour, most visibly in tool calls.

Sampler support and defaults. Hosts differ in which parameters they honour, their defaults when you send none, and whether seed does anything.

Limits. A host can serve a shorter context window or lower maximum output than the model card allows. Groq, for example, lists its own context window and maximum output for each model it serves3.

Features. Tool-call parsing and JSON schema enforcement are host software, not weights. One host may support schema-constrained decoding for a model that another serves only in JSON-object mode.

Runtime nondeterminism. Even one host is not deterministic. vLLM states it does not guarantee reproducible results by default, for performance, and only provides reproducibility on the same hardware and version6. Thinking Machines showed the main cause is kernels that are not batch-invariant, so a request's output depends on server load: 1,000 temperature-0 completions of one prompt produced 80 distinct outputs, all identical for the first 102 tokens7.

That last point is why most informal host comparisons mislead. If two runs on the same host already differ, a single run on each of two hosts shows noise, not a host difference.

What hosts publish about precision

What the host publishesTogether AIFireworks AIGroqDeepInfra
Precision stated◐FP8, FP4, hybrid (engine-level)●per model, via firectl◐TruePoint numerics●on each model listing
WhereEngineering blogDocs and CLIModel pagesModel catalog
Accuracy claim"Near-lossless" (vendor)"May introduce small changes"Reduces precision where it does not affect accuracy (vendor)Not stated
What four hosts state publicly about the precision they serve, from their own pages on 2026-09-29. Accuracy claims are the vendors' own.

Record these statements, with the date, before you test. They tell you where to look first, and they change: a host can requantize a model without changing its name.

The protocol

  1. 1 · Setup

    Pin the model

    One model card, one revision, the same prompt set and parameters on every host.

  2. 2 · Setup

    Record what each host states

    Precision, context, max output, feature support — from the host's own page, dated.

  3. 3 · Baseline

    Measure noise first

    Repeat each prompt on one host. Differences between hosts only count beyond this spread.

  4. 4 · Measure

    Run the suite per host

    Same prompts, interleaved in time, several repeats, every response stored with its host.

  5. 5 · Score

    Score with checks, not taste

    Exact answers, schema validity, tool-call parse rate, truncation, length.

  6. 6 · Report

    Publish the method with the result

    Dates, host settings, sample sizes, and the noise band beside every number.

The six steps. The baseline step is the one most comparisons skip.
  1. Pin the model. One model card, one revision where hosts expose it, and identical request parameters everywhere: temperature 0, the same max_tokens, the same system prompt, no host-specific extras.
  2. Record each host's stated configuration: precision, context window, maximum output, tool and JSON support. If a host does not state precision, record that.
  3. Measure noise first. Send every prompt five to ten times to one host, spread over time. Compute, per prompt, how often the repeats agree and how the checkable score varies. This is your noise band.
  4. Run the suite on every host, interleaving hosts in time so a bad hour at one does not look like a quality gap, with the same number of repeats.
  5. Score with checks, not opinions. Use tasks with a verifiable answer: extraction with a known result, arithmetic, classification with labels, JSON that must validate, tool calls that must name the right function with parseable arguments. Also record finish_reason, output tokens and latency.
  6. Report with the method. Dates, host settings, sample sizes, the noise band and the raw outputs.

Fireworks makes a related point about evaluating quantization: task accuracy scores correctness as a step function, so small shifts in the token distribution are hidden by benchmark noise, and it measures divergence in token probabilities instead8. You usually cannot read logits from a hosted API, which is another reason to use many repeats and checkable tasks rather than a single benchmark number.

The harness

The harness below sends each prompt to several targets, repeats it, checks each answer, and writes one JSON line per response. It uses the OpenAI SDK, so it works against any OpenAI-compatible endpoint; here each target is an INFRO route pinned to one host, described in the next section.

host_consistency.py
import json
import os
import time
from collections import defaultdict

from openai import OpenAI

client = OpenAI(base_url="https://api.infro.io/v1", api_key=os.environ["INFRO_API_KEY"])

# One route per host, each with a single target: the same model on one connection.
TARGETS = ["qwen-host-a", "qwen-host-b", "qwen-host-c"]
REPEATS = 8
RUN_ID = time.strftime("%Y%m%dT%H%M%S")

# Every case has a checker, so scoring needs no judgement.
CASES = [
    {"id": "sum-1", "prompt": "What is 1847 + 2395? Reply with the number only.",
     "check": lambda text: text.strip() == "4242"},
    {"id": "label-1", "prompt": "Classify the sentiment as positive, negative or neutral. "
     "Reply with one word.\n\nThe refund arrived a week late and nobody answered my emails.",
     "check": lambda text: text.strip().lower() == "negative"},
    {"id": "json-1", "prompt": 'Return JSON only: {"city": string, "year": integer} for '
     '"The treaty was signed in Lisbon in 2007."',
     "check": lambda text: _json_equals(text, {"city": "Lisbon", "year": 2007})},
]


def _json_equals(text: str, expected: dict) -> bool:
    try:
        return json.loads(text) == expected
    except json.JSONDecodeError:
        return False


def run_once(target: str, case: dict, repeat: int) -> dict:
    started = time.monotonic()
    completion = client.chat.completions.create(
        model=target,
        messages=[{"role": "user", "content": case["prompt"]}],
        temperature=0,
        max_tokens=256,
        extra_body={"metadata": {"tags": {"run": RUN_ID, "case": case["id"]}}},
    )
    body = completion.model_dump()
    text = completion.choices[0].message.content or ""
    return {
        "run": RUN_ID, "target": target, "case": case["id"], "repeat": repeat,
        "provider": body.get("provider"), "connection": body.get("connection"),
        "request_id": completion.id, "finish_reason": completion.choices[0].finish_reason,
        "output_tokens": completion.usage.completion_tokens if completion.usage else None,
        "latency_s": round(time.monotonic() - started, 3),
        "passed": case["check"](text), "text": text,
    }


def main() -> None:
    rows = []
    with open(f"consistency-{RUN_ID}.jsonl", "w") as out:
        for repeat in range(REPEATS):
            for case in CASES:
                for target in TARGETS:  # interleave hosts in time
                    row = run_once(target, case, repeat)
                    rows.append(row)
                    out.write(json.dumps(row) + "\n")

    by_target = defaultdict(list)
    agreement = defaultdict(set)
    for row in rows:
        by_target[row["target"]].append(row["passed"])
        agreement[(row["target"], row["case"])].add(row["text"])

    for target, results in by_target.items():
        distinct = [len(agreement[(target, c["id"])]) for c in CASES]
        print(f"{target}: pass {sum(results)}/{len(results)}, "
              f"distinct outputs per case {distinct}")


if __name__ == "__main__":
    main()

Three cases are a sketch; a real suite has a hundred or more drawn from production traffic, grouped by task type so a failure can be traced to a capability. Add cases for tool calls with a checker that parses the arguments, and long-context cases that sit near each host's stated limit.

Reading the results

A host scores differently on your suite. What does it mean?

  • Difference is inside the same-host noise bandNo finding. Increase repeats before concluding anything.
  • Failures cluster in truncation or refused parametersA limit or parameter difference. Fix the request or the connection, then re-run.
  • Tool calls or JSON fail to parse on one hostHost feature code. Test that path separately; do not blame the weights.
  • Scores differ on exact-answer tasks beyond noiseA real behaviour difference. Route that workload to the hosts that pass.
How to interpret a difference once you have a noise band. Most differences fall into the first three rows.

Look for concentration before concluding anything. Failures that cluster in one task type point at a feature or limit difference you can often fix. Failures spread thinly across every task type, beyond the noise band, point at the weights or the template, and are a reason to keep that workload on the hosts that pass. Rerun the suite whenever a host announces an engine change, and on a schedule, because hosts change precision and runtime without changing model names.

Running the test through INFRO

In INFRO each host is a provider connection on your own account — Together, Fireworks and Groq are connection kinds, and any OpenAI-compatible host can be added as an OpenAI-compatible endpoint. Together and Fireworks model ids carry host-specific prefixes, so each model is named explicitly in the connection's model map. See providers.

To pin a request to one host, create one route per host, each with a single target: the model and the connection that should serve it. The harness then calls qwen-host-a, qwen-host-b and so on. Every response names the provider and connection that served it, and route reads primary when no failover happened — check it, because a failover inside a single-target route cannot happen, but a misconfigured route with two targets would silently mix hosts.

The metadata.tags in the harness are recorded on each request and never sent to the provider, so the run can be found again in the request log with GET /v1/requests, or exported as CSV with GET /v1/requests/export, including latency, token counts and estimated provider cost per request. See request traces. Estimated provider cost is what your host will bill you, at its list rate or the rate you entered; INFRO's own subscription is separate and does not vary with the host.

Once the test is done, put only the hosts that passed on the production route, in priority order or by weight. INFRO's traffic changes also flag when a model on a connection starts being answered by a model id that connection had not reported in the previous 14 days — a useful signal to rerun the suite, though it reads metadata only and cannot tell whether answers got worse.

Questions readers ask

Is an FP8 or FP4 model worse than the BF16 original?
Not necessarily on your task, and not measurably on every task. Hosts describe their low-precision serving as close to the original, but only a test on your own prompts, beyond the run-to-run noise, tells you whether a difference matters for you.

From the team that builds it

Where this shows up in INFRO

INFRO is a control plane in front of the provider accounts you already hold. The capabilities below are the parts of it this article touches, each with the documentation that defines its behaviour.

INFRO is in private early access. Provider usage is billed to you by your providers; INFRO charges a software subscription and takes 0% of model spend. See plans.

Sources

Numbered in order of first citation. Each was opened and checked against the claim on the date shown.

  1. 1.Together AI delivers fastest inference for the top open-source models (opens in a new tab) — Together AI. Accessed Sep 29, 2026.
  2. 2.Quantization (opens in a new tab) — Fireworks AI. Accessed Sep 29, 2026.
  3. 3.OpenAI GPT-OSS 120B (opens in a new tab) — Groq. Accessed Sep 29, 2026.
  4. 4.Models (opens in a new tab) — DeepInfra. Accessed Sep 29, 2026.
  5. 5.openai/gpt-oss-120b model card (opens in a new tab) — OpenAI on Hugging Face. Accessed Sep 29, 2026.
  6. 6.Reproducibility (opens in a new tab) — vLLM. Accessed Sep 29, 2026.
  7. 7.Defeating Nondeterminism in LLM Inference (opens in a new tab) — Thinking Machines Lab. Accessed Sep 29, 2026.
  8. 8.How Fireworks evaluates quantization precisely and interpretably (opens in a new tab) — Fireworks AI. Accessed Sep 29, 2026.

About this article

A test design, not a test result. The protocol and harness below were written for this article from host documentation, model cards and published work on inference nondeterminism, opened on 2026-09-29, and from INFRO's public routing and request-log documentation, which the authors build. No host was measured for this article, and no host is ranked.

Last checked against its sources on . The next review is due by . No separate named reviewer is recorded for this article.

How articles are written, sourced and corrected is set out in the editorial standards.