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 underlyingSamplingClientinstance.renderer(MessageCompleter only) — Handles message → tokens and tokens → message conversion.sampling_params—SamplingParams(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
MessageCompleterwithout 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
SamplingClientand a newCompleter. A stale completer silently samples from old weights.