Skip to content

Completers

The raw SamplingClient deals in token IDs. Two small helper classes wrap it so you can work at the level your loop actually needs:

  • TokenCompleter — feeds and returns token IDs (plus optional log-probs). The natural fit for RL rollouts and any code that already speaks tokens.
  • MessageCompleter — feeds and returns chat messages (role/content). The natural fit for evaluators, LLM judges, and multi-turn chat. It needs a renderer to convert between messages and tokens.

What they share

Both completers are thin: they take a SamplingClient, apply your SamplingParams, stop at the configured stop sequences, and can attach log-probs to every emitted token. The only difference is the input/output shape — tokens versus messages.

Pattern

from mint.completers import TinkerTokenCompleter, TinkerMessageCompleter
from mint.renderers import get_renderer

service_client = mint.ServiceClient()
sampling_client = service_client.create_sampling_client(base_model="Qwen/Qwen3-0.6B")
tokenizer = sampling_client.get_tokenizer()
renderer = get_renderer("qwen3", tokenizer)

# Example 1: TokenCompleter for RL rollouts
token_completer = TinkerTokenCompleter(sampling_client=sampling_client)

prompt_ids = tokenizer.encode("The capital of France is")
prompt = mint.types.ModelInput.from_ints(prompt_ids)
sampling_params = mint.types.SamplingParams(
    max_tokens=16,
    temperature=0.7,
    stop=renderer.get_stop_sequences(),
)

token_result = token_completer.complete(
    prompt=prompt,
    sampling_params=sampling_params,
)
print(f"Tokens: {token_result.tokens}")
print(f"Logprobs: {token_result.logprobs}")

# Example 2: MessageCompleter for evaluation
message_completer = TinkerMessageCompleter(
    sampling_client=sampling_client,
    renderer=renderer,
)

messages = [
    {"role": "system", "content": "You are a math tutor."},
    {"role": "user", "content": "What is 7 * 8?"},
]

message_result = message_completer.complete(
    messages=messages,
    sampling_params=mint.types.SamplingParams(max_tokens=32, temperature=0.0),
)
print(f"Response: {message_result}")  # {"role": "assistant", "content": "..."}

View full source: https://github.com/MindLab-Research/mint-quickstart/blob/main/concepts/completers.py

API Surface

Class Input Output Use case
TinkerTokenCompleter ModelInput (tokens) TokensWithLogprobs RL loops, token-level analysis
TinkerMessageCompleter list[Message] (role/content) Message (role/content) Evaluation, LLM-as-judge, chat

Common parameters:

  • sampling_client — The underlying SamplingClient instance.
  • renderer (MessageCompleter only) — Handles message → tokens and tokens → message conversion.
  • sampling_paramsSamplingParams(max_tokens, temperature, top_p, stop, ...).

Return types:

  • TokensWithLogprobs — Namedtuple: (tokens: list[int], logprobs: list[float]).
  • Message — Dict: {"role": str, "content": str}.

Caveats & Pitfalls

  • Stop sequences: Always pass stop=renderer.get_stop_sequences() to prevent over-generation. Missing stop tokens can cause the model to continue past the intended message boundary.
  • MessageCompleter needs a renderer: A MessageCompleter without a renderer will fail on the first .complete() call. Always initialize with the correct renderer for your model family.
  • Logprobs cost: Requesting logprobs adds a small computational overhead. In large-scale RL, consider batching completions or filtering logprob retrieval to important trajectories.
  • Async variants: Use complete_async() for concurrent completions. Always gather futures before .result() to maximize throughput.
  • Sampler desync: After saving and reloading model weights, create a new SamplingClient and a new Completer. A stale completer silently samples from old weights.