Skip to content

Verify responses

Request a cryptographic proof with a response and verify that the advertised model produced the output.

Engy can attach a cryptographic proof to any response. You can then recompute the proof against the model weights to confirm that the advertised model generated the output. This guide is intended for developers who want to verify individual responses independently.

How verification works

When you request a proof, the GPU worker that serves your request records a fingerprint of the model's internal activations for each generated token. This fingerprint is a TOPLOC proof.

To verify the proof, you run the same model on your own hardware, recompute the activations for the same tokens, and compare them with the proof:

  • If the activations match, the advertised model produced the output.
  • If the worker used a different, smaller, or modified model, the activations do not match, and verification fails.

Engy passes the proof to you unchanged. Engy does not store the proof, so requesting one does not affect zero data retention.

Request a proof

Set provider.require_proof to true in the request body:

curl https://api.engy.ai/v1/chat/completions \
  -H "Authorization: Bearer $ENGY_API_KEY" \
  -H "Content-Type: application/json" \
  -o response.json \
  -d '{
    "model": "qwen3.8-27b",
    "messages": [{"role": "user", "content": "Hello"}],
    "provider": {"require_proof": true}
  }'

Engy routes the request only to workers that can produce proofs. If no such worker is available for the model, the request fails with HTTP status 503.

Where the proof appears

The proof is returned in a top-level proof field. Its location depends on the endpoint and on whether you stream the response:

EndpointNon-streamingStreaming
/v1/chat/completionsTop-level proof fieldFinal chunk, before [DONE]
/v1/completionsTop-level proof fieldFinal chunk, before [DONE]
/v1/messagesTop-level proof field on the messagemessage_delta event
/v1/responsesTop-level proof field on the responseresponse.completed event

If you did not request a proof, or the worker returned none, the proof field is absent.

Proof format

{
  "proof": {
    "model_root": "...",
    "output_token_ids": [...],
    "toploc": {
      "proofs": ["..."],
      "topk": 128,
      "decode_batching_size": 32,
      "skip_prefill": true,
      "n_tokens": 9,
      "prompt_ids": [...]
    }
  }
}
FieldDescription
model_rootThe identifier of the model checkpoint that the worker committed to.
output_token_idsThe token IDs that the model generated.
toploc.proofsThe TOPLOC proof segments, encoded in Base64.
toploc.topkThe number of activation values that each proof segment covers.
toploc.decode_batching_sizeThe number of tokens in each proof segment.
toploc.skip_prefillWhether the proof excludes the prompt positions.
toploc.n_tokensThe number of output tokens that the proof covers.
toploc.prompt_idsThe prompt token IDs, after the chat template was applied.

Verify a proof

Prerequisites

Before you begin, make sure you have:

  • A GPU with enough memory to run the model. For example, qwen3.8-27b runs on a single NVIDIA H200 GPU.
  • The model checkpoint from Hugging Face. Each model is served from one fixed checkpoint, and you must verify against the same checkpoint. For qwen3.8-27b, use Qwen/Qwen3.8-27B. For other models, see Models and pricing.
  • Python 3.10 or later
  • A saved response that contains a proof, such as response.json from the previous section

Steps

Install the dependencies

pip install "sglang[all]" toploc torch httpx numpy

Start a local inference server

Start SGLang with hidden-state output enabled and the prefix cache disabled:

python -m sglang.launch_server \
  --model-path Qwen/Qwen3.8-27B \
  --enable-return-hidden-states \
  --disable-radix-cache \
  --port 30000

You must disable the prefix cache with --disable-radix-cache. Otherwise, the server returns activations for only part of the sequence, and verification fails.

Run the verification script

Save the following script as verify.py:

verify.py
import json
import sys

import httpx
import numpy as np
import torch
from toploc import verify_proofs_base64

SERVER_URL = "http://127.0.0.1:30000"
THRESHOLD = 40

with open(sys.argv[1]) as f:
    proof = json.load(f)["proof"]

toploc = proof["toploc"]
prompt_ids = toploc["prompt_ids"]
n_tokens = toploc["n_tokens"]
output_ids = proof["output_token_ids"][:n_tokens]
full_ids = prompt_ids + output_ids

# Recompute the activations with a single prefill pass.
result = httpx.post(
    f"{SERVER_URL}/generate",
    json={
        "input_ids": full_ids,
        "sampling_params": {"temperature": 0.0, "max_new_tokens": 0},
        "return_hidden_states": True,
    },
    timeout=600,
).json()
result = result[0] if isinstance(result, list) else result
block = np.asarray(result["meta_info"]["hidden_states"][0], dtype=np.float32)

# The returned block covers the last positions of the sequence.
block_start = len(full_ids) - block.shape[0]
prompt_len = len(prompt_ids)
activations = [
    torch.from_numpy(
        np.ascontiguousarray(block[prompt_len - 1 + t - block_start])
    ).to(torch.bfloat16)
    for t in range(n_tokens)
]

results = verify_proofs_base64(
    activations,
    toploc["proofs"],
    decode_batching_size=toploc["decode_batching_size"],
    topk=toploc["topk"],
    skip_prefill=toploc["skip_prefill"],
)

mismatches = float(np.mean([r.exp_mismatches for r in results]))
status = "PASS" if mismatches <= THRESHOLD else "FAIL"
print(f"{status}: {mismatches:.1f} of {toploc['topk']} values mismatched")

Run the script with your saved response:

python verify.py response.json

Interpret the result

The script reports the average number of mismatched activation values per proof segment, out of topk:

ResultTypical mismatches (of 128)Meaning
PASSAbout 14The advertised model produced the output.
FAILAbout 113A different model or modified weights produced the output.

Small mismatches are expected, because different GPUs produce slightly different floating-point results. The default threshold of 40 separates the two cases with a wide margin.

Limitations

  • Requesting a proof adds compute and bandwidth to the request, so proofs are not generated by default.
  • Only workers that can produce proofs serve these requests. As a result, capacity for some models might be limited.
  • Verification requires hardware that can run the full model.