Source code for tensorrt_llm.sampling_params

# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import functools
import json
import operator
import os
from abc import ABC, abstractmethod
from collections.abc import Iterable, Mapping
from dataclasses import dataclass, field, fields, make_dataclass
from typing import Any, List, NamedTuple, Optional, Tuple, Union

import torch
from pydantic import BaseModel
from strenum import StrEnum

from tensorrt_llm.bindings import executor as tllme
from tensorrt_llm.logger import logger

MAX_TOP_LOGPROBS = 100

_GENERATION_CONFIG_SAMPLING_FIELDS = frozenset(
    {
        "early_stopping",
        "length_penalty",
        "min_p",
        "no_repeat_ngram_size",
        "repetition_penalty",
        "temperature",
        "top_k",
        "top_p",
    }
)


def validate_thinking_token_budget(value: Optional[Union[int, float, bool]]) -> Optional[int]:
    """Validate ``thinking_token_budget``; return ``None`` if unset."""
    if value is None:
        return None
    if isinstance(value, bool) or not isinstance(value, int):
        raise ValueError("thinking_token_budget must be a non-negative integer or -1 for unlimited")
    if value == -1:
        return None
    if value < 0:
        raise ValueError("thinking_token_budget must be a non-negative integer or -1 for unlimited")
    return value


def check_logprobs_limit(
    name: str, value: Optional[int], max_value: int = MAX_TOP_LOGPROBS
) -> None:
    if value is None:
        return
    if value < 0:
        raise ValueError(f"{name} must be positive, zero or None")
    if value > max_value:
        raise ValueError(f"{name} must be less than or equal to {max_value}")


[docs] @dataclass(slots=True, kw_only=True) class GuidedDecodingParams: """Guided decoding parameters for text generation. Only one of the fields could be effective. Args: json (str, pydantic.main.BaseModel, dict, optional): The generated text is amenable to json format with additional user-specified restrictions, namely schema. Defaults to None. regex (str, optional): The generated text is amenable to the user-specified regular expression. Defaults to None. grammar (str, optional): The generated text is amenable to the user-specified extended Backus-Naur form (EBNF) grammar. Defaults to None. json_object (bool): If True, the generated text is amenable to json format. Defaults to False. structural_tag (str, optional): The generated text is amenable to the user-specified structural tag. Structural tag is supported by xgrammar backend only. Defaults to None. """ # noqa: E501 json: Optional[Union[str, BaseModel, dict]] = None regex: Optional[str] = None grammar: Optional[str] = None json_object: bool = False structural_tag: Optional[str] = None def _validate(self): num_guides = 0 for _field in fields(self): num_guides += bool(getattr(self, _field.name)) if num_guides > 1: raise ValueError(f"Only one guide can be used for a request, but got {num_guides}.")
class LogprobParams(NamedTuple): prompt_logprobs: Optional[int] = None # Drop the logits once the logprobs are computed drop_context_logits: bool = False # Return prompt logprobs as `list[float]` instead of the default # `list[dict[int, Logprob]]`. Only valid when prompt_logprobs == 0. prompt_logprobs_simple_format: bool = False class LogprobMode(StrEnum): RAW = "raw" """ Return the raw log probabilities, i.e., the log probabilities calculated directly from the model output logits. """ PROCESSED = "processed" """ Return the processed log probabilities, i.e., the log probabilities after applying sampling parameters, such as temperature, top-k, top-p, etc. """ class LogitsProcessor(ABC): """Base class for logits processor. The recommended way to create a customized logits processor: * Subclass this class and implement the processing logics in the __call__ method. * Create an instance and pass to SamplingParams. Alternatively, you can create any callable with the same signature with the __call__ method. """ @abstractmethod def __call__( self, req_id: int, logits: torch.Tensor, token_ids: List[List[int]], stream_ptr: Optional[int], client_id: Optional[int], ) -> None: """Logits processing callback. The callback is expected to inplace modify the logits. Args: req_id (int): Request id. logits (torch.Tensor): Logits tensor to be modified. token_ids (List[List[int]]): Token ids produced by the request so far. The shape is beam_width * sequence_length. Under beam search on the PyTorch backend, row ``i`` is the prompt plus the hypothesis held by the beam whose logits are row ``i`` of ``logits``; beams that already finished are not included. With the overlap scheduler the history trails the beam's newest token by one step, as for every request. stream_ptr (int, optional): The operation stream used by the logits tensor. Not required for PyTorch backend. client_id (int, optional): An optional client id. """ pass # noqa class BatchedLogitsProcessor(ABC): """Base class for batched logits processor. The recommended way to create a customized batched logits processor: * Subclass this class and implement the processing logics in the __call__ method. * Create an instance and pass to LLM. Alternatively, you can create any callable with the same signature with the __call__ method. """ @abstractmethod def __call__( self, req_ids: List[int], logits: List[torch.Tensor], token_ids: List[List[List[int]]], stream_ptr: int, client_ids: List[Optional[int]], ) -> None: """Batched logits processing callback. The callback is expected to inplace modify the logits. Args: req_ids (List[int]): A batch of request ids. logits (List[torch.Tensor]): A batch of the logits tensors. token_ids (List[List[List[int]]]): A batch of the token ids produced by the requests so far. The shape is batch * beam_width * sequence_length. stream_ptr (int): The operation stream used by the logits tensors. client_ids (List[Optional[int]]): A batch of optional client ids. """ pass # noqa
[docs] @dataclass(slots=True, kw_only=True) class SamplingParams: """Sampling parameters for text generation. Usage Examples: use_beam_search is False: - best_of is None: (top-p/top-k) sampling n responses and return n generations - best_of is not None: (top-p/top-k) sampling best_of responses and return n generations (best_of >= n must hold) use_beam_search is True: - best_of is None: beam search with beam width of n, return n generations - best_of is not None: beam search with beam width of best_of, return n generations (best_of >= n must hold) Args: end_id (int, optional): The end token id. Defaults to None. pad_id (int, optional): The pad token id. Defaults to None. max_tokens (int): The maximum number of tokens to generate. Defaults to 32. bad (str, List[str], optional): A string or a list of strings that redirect the generation when they are generated, so that the bad strings are excluded from the returned output. Defaults to None. bad_token_ids (List[int], optional): A list of token ids that redirect the generation when they are generated, so that the bad ids are excluded from the returned output. Defaults to None. stop (str, List[str], optional): A string or a list of strings that stop the generation when they are generated. The returned output will not contain the stop strings unless include_stop_str_in_output is True. Defaults to None. stop_token_ids (List[int], optional): A list of token ids that stop the generation when they are generated. Defaults to None. include_stop_str_in_output (bool): Whether to include the stop strings in output text. Defaults to False. embedding_bias (torch.Tensor, optional): The embedding bias tensor. Expected type is kFP32 and shape is [vocab_size]. Defaults to None. logits_processor (tensorrt_llm.sampling_params.LogitsProcessor, List[tensorrt_llm.sampling_params.LogitsProcessor], optional): The logits postprocessor callback(s). Defaults to None. If a list, each processor is applied in order during generation (supported in PyTorch backend only). apply_batched_logits_processor (bool): Whether to apply batched logits postprocessor callback. Defaults to False. The BatchedLogitsProcessor class is recommended for callback creation. The callback must be provided when initializing LLM. n (int): Number of sequences to generate. Defaults to 1. best_of (int, optional): Number of sequences to consider for best output. Defaults to None. use_beam_search (bool): Whether to use beam search. Defaults to False. top_k (int, optional): Controls number of logits to sample from. Can assume non-negative values, where 0 means 'all logits'. Defaults to None. The value None is treated as "not specified" in the following. If neither temperature, top_p, nor top_k are specified, sampling is greedy. If temperature > 0 and/or top_p < 1 are specified, sampling will proceed accordingly and top_k will default to top_k = 0. Setting top_k = 1 results in greedy sampling. top_p (float, optional): Controls the top-P probability to sample from. Can have values between 0 and 1. Defaults to None. The value None is treated as "not specified" in the following. If neither temperature, top_p, nor top_k are specified, sampling is greedy. If temperature > 0 and/or top_k > 1 are specified, sampling will proceed accordingly and top_p will default to top_p = 1. Setting top_p = 0 should result in greedy sampling, but is currently disallowed in the backend. top_p_min (float, optional): Controls decay in the top-P algorithm. topPMin is lower-bound. Must be in (0, 1]; invalid values are rejected. None means using C++ runtime default 1.e-6. Defaults to None. top_p_reset_ids (int, optional): Controls decay in the top-P algorithm. The token id which, when sampled, resets the decayed top-P to its initial value. Must be >= 0; invalid values are rejected. None means using C++ runtime default -1 (which never matches a token). Defaults to None. top_p_decay (float, optional): Controls decay in the top-P algorithm. The decay value. Must be in (0, 1]; invalid values are rejected. None means using C++ runtime default 1.f. Defaults to None. seed (int, optional): Controls the random seed used by the random number generator in sampling. None means using C++ runtime default 0. Defaults to None. temperature (float, optional): Controls the modulation of logits when sampling new tokens. It can have values >= 0.f. Defaults to None. The value None is treated as "not specified" in the following. If neither temperature, top_p, nor top_k are specified, sampling is greedy. If top_p < 1 and/or top_k > 1 are specified, sampling will proceed accordingly and temperature will default to temperature = 1. Setting temperature = 0 results in greedy sampling. min_tokens (int, optional): Lower bound on the number of tokens to generate. Values < 1 have no effect. None means using C++ runtime default 1. Defaults to None. beam_search_diversity_rate (float, optional): Encourages beams to diverge from each other by adding diversity_rate * source_beam_index to each candidate's ranking score during beam expansion, boosting candidates that expand from lower-ranked beams. Here source_beam_index is the rank of the beam a candidate expands from among the current step's input beams, ordered by cumulative log-probability (0 for the strongest beam). None means using C++ runtime default 0.f (disabled). Defaults to None. repetition_penalty (float, optional): Used to penalize tokens based on how often they appear in the sequence. It can have any value > 0.f. Values < 1.f encourages repetition, values > 1.f discourages it. None means using C++ runtime default 1.f. Defaults to None. presence_penalty (float, optional): Used to penalize tokens already present in the sequence (irrespective of the number of appearances). It can have any values. Values < 0.f encourage repetition, values > 0.f discourage it. None means using C++ runtime default 0.f. Defaults to None. frequency_penalty (float, optional): Used to penalize tokens already present in the sequence (dependent on the number of appearances). It can have any values. Values < 0.f encourage repetition, values > 0.f discourage it. None means using C++ runtime default 0.f. Defaults to None. prompt_ignore_length (int, optional): Controls how many tokens to ignore from the prompt for presence and frequency penalties. Values <= 0 have no effect. Values > input (prompt) length will be clamped. None means using C++ runtime default 0. Defaults to None. length_penalty (float, optional): Beam-search length penalty exponent. Beams are ranked by cum_log_prob / length**length_penalty, where length counts generated tokens only; the returned cumulative_logprob stays unnormalized. Must be >= 0. 0 disables the normalization. None means using C++ runtime default 0.f. Defaults to None. early_stopping (int, optional): Three-state, following HuggingFace. 1 stops as soon as best_of finished candidates exist. 0 and 2 are exhaustive: they keep a pool of finished candidates and continue while an unfinished beam could still outscore the worst of them, 0 bounding attainability with the beams' current length and 2 ("never") with max_seq_len when length_penalty > 0. Any other integer is treated as 2. None means using C++ runtime default 1. Defaults to None. no_repeat_ngram_size (int, optional): Forbids repeating any n-gram of this size: a token is excluded from sampling if it would recreate an n-gram that already occurs in the sequence (prompt included). None or 0 disables the restriction. Defaults to None. min_p (float, optional): scale the most likely token to determine the minimum token probability. None means using C++ runtime default 0.0. Defaults to None. beam_width_array (List[int], optional): Per-iteration beam widths for Variable-Beam-Width-Search; decoding past the end of the array holds its last entry. Must be non-decreasing -- a narrowing schedule is rejected. beam_width is raised to the array's maximum, which is the number of beams returned. Defaults to None. logprobs (int, optional): Number of log probabilities to return per output token. When set to 0, return only the sampled token's log probability. When set to K>0, return top-K log probabilities + the sampled token's log probability (last entry) if it's not in the Top-K. Defaults to None. logprobs_mode (LogprobMode): The mode of log probabilities to return. Defaults to LogprobMode.RAW. logprobs_simple_format (bool): If True (and `logprobs == 0`), return generation logprobs as a flat `list[float]` (one logprob per generated token) instead of the default `list[dict[int, Logprob]]` format. Reduces per-token allocation overhead when only the sampled-token logprob is needed. Incompatible with `logprobs is None or logprobs > 0` and with beam search. Defaults to False. prompt_logprobs (int, optional): Number of log probabilities to return per prompt token. When set to 0, return only the actual prompt token's log probability. When set to K>0, return top-K log probabilities + the actual prompt token's log probability (last entry) if it's not in the Top-K. Defaults to None. prompt_logprobs_simple_format (bool): If True (and `prompt_logprobs == 0`), return prompt logprobs as a flat `list[float]` instead of the default `list[dict[int, Logprob]]` format. Incompatible with `prompt_logprobs is None or prompt_logprobs > 0`. Defaults to False. return_context_logits (bool): Controls if Result should contain the context logits. Defaults to False. return_generation_logits (bool): Controls if Result should contain the generation logits. Defaults to False. exclude_input_from_output (bool): Controls if output tokens in Result should include the input tokens. Defaults to True. return_encoder_output (bool): Controls if Result should contain encoder output hidden states (for encoder-only and encoder-decoder models). Defaults to False. return_perf_metrics (bool): Controls if Result should contain the performance metrics for this request. Defaults to False. return_routed_experts (bool): Router Replay (R3): return per-token pre-EPLB logical top-k MoE expert ids for train/inference routing alignment in MoE reinforcement learning. Requires the engine-level enable_return_routed_experts. Defaults to False. additional_model_outputs (List[str], optional): The additional outputs to gather from the model. Defaults to None. guided_decoding (tensorrt_llm.sampling_params.GuidedDecodingParams, optional): Guided decoding params. Defaults to None. thinking_token_budget (int, optional): Experimental. Maximum number of tokens allowed inside a reasoning block. Set to -1 or None for unlimited. Defaults to None. ignore_eos (bool): Whether to ignore the EOS token and continue generating tokens after the EOS token is generated. Defaults to False. detokenize (bool): Whether to detokenize the output. Defaults to True. add_special_tokens (bool): Whether to add special tokens to the prompt. Defaults to True. truncate_prompt_tokens (int, optional): If set to an integer k, will use only the last k tokens from the prompt (i.e., left truncation). Defaults to None. skip_special_tokens (bool): Whether to skip special tokens in the output. Defaults to True. spaces_between_special_tokens (bool): Whether to add spaces between special tokens in the output. Defaults to True. """ # noqa: E501 # [TO DEVELOPER] This class provides an interface to LLMAPI users. # Internally, it manages and dispatches fields to Python bindings of C++ objects, currently including: # (1) all fields of tllme.SamplingConfig; # (2) all fields of tllme.OutputConfig; # (3) some fields of tllme.Request. # If you changed the implementation of C++ objects and corresponding Python bindings, please update: # (1) the fields and corresponding docstring of this class, and # (2) the expected_fields defined in _get_xxx_config methods. end_id: Optional[int] = None pad_id: Optional[int] = None max_tokens: int = 32 bad: Optional[Union[str, List[str]]] = None bad_token_ids: Optional[List[int]] = None _bad_word_ids: Optional[List[List[int]]] = field(default=None, init=False, repr=False) stop: Optional[Union[str, List[str]]] = None stop_token_ids: Optional[List[int]] = None include_stop_str_in_output: bool = False _stop_word_ids: Optional[List[List[int]]] = field(default=None, init=False, repr=False) embedding_bias: Optional[torch.Tensor] = None logits_processor: Optional[Union[LogitsProcessor, List[LogitsProcessor]]] = None apply_batched_logits_processor: bool = False n: int = 1 best_of: Optional[int] = None use_beam_search: bool = False logprobs_mode: LogprobMode = LogprobMode.RAW # Keep the below fields in sync with tllme.SamplingConfig or maintin the mapping table. top_k: Optional[int] = None top_p: Optional[float] = None top_p_min: Optional[float] = None top_p_reset_ids: Optional[int] = None top_p_decay: Optional[float] = None seed: Optional[int] = None temperature: Optional[float] = None min_tokens: Optional[int] = None beam_search_diversity_rate: Optional[float] = None repetition_penalty: Optional[float] = None presence_penalty: Optional[float] = None frequency_penalty: Optional[float] = None prompt_ignore_length: Optional[int] = None length_penalty: Optional[float] = None early_stopping: Optional[int] = None no_repeat_ngram_size: Optional[int] = None min_p: Optional[float] = None beam_width_array: Optional[List[int]] = None # Keep the below fields in sync with tllme.OutputConfig logprobs: Optional[int] = None prompt_logprobs: Optional[int] = None logprobs_simple_format: bool = False prompt_logprobs_simple_format: bool = False return_context_logits: bool = False return_generation_logits: bool = False exclude_input_from_output: bool = True return_encoder_output: bool = False return_perf_metrics: bool = False # Router Replay (R3): return per-token pre-EPLB logical top-k MoE expert ids # on this request's output (via additional_generation_outputs["routed_experts"]), # for train/inference routing alignment in MoE reinforcement learning. Requires # the engine-level enable_return_routed_experts. Separated-routing MoE backends # only (fused backends fail closed). return_routed_experts: bool = False additional_model_outputs: Optional[List[str]] = None # Decoder tokens moved from generated output into the input prefix. The # result layer restores them to the user-visible output. _decoder_output_token_prefix: Tuple[int, ...] = field( default_factory=tuple, init=False, repr=False ) # Guided decoding params guided_decoding: Optional[GuidedDecodingParams] = None thinking_token_budget: Optional[int] = None # Tokenizer-related configs ignore_eos: bool = False detokenize: bool = True add_special_tokens: bool = True truncate_prompt_tokens: Optional[int] = None skip_special_tokens: bool = True spaces_between_special_tokens: bool = True # Currently, _stream_interval is only used to pass llm.args.stream_interval to tokenizer. # TODO: make this a per-request parameter. _stream_interval: Optional[int] = field(default=None, init=False, repr=False) # None identifies direct LLM API SamplingParams, where non-None values are # request-provided. Serving adapters set this to preserve which fields were # explicitly present before they materialize their protocol defaults. _request_provided_fields: Optional[frozenset[str]] = field(default=None, init=False, repr=False) def __post_init__(self): if self.pad_id is None: self.pad_id = self.end_id self.best_of = self.best_of or self.n if self.embedding_bias is not None: if isinstance(self.embedding_bias, torch.Tensor): self.embedding_bias = self.embedding_bias.detach().clone() else: self.embedding_bias = torch.tensor(self.embedding_bias, dtype=torch.float32) self._validate() def __reduce_ex__(self, protocol): # Pickle the field values in field order; subclasses may add fields. if type(self) is not SamplingParams: return object.__reduce_ex__(self, protocol) return _restore_sampling_params, (_get_sampling_params_state(self),) def _validate(self): """Verify the sampling parameters. This function verifies the sampling parameters in the LLM API, which may have stricter requirements than the Executor class of C++ runtime. For instance, while the greedy decoding with n > 1 is capable in the Executor class of C++ runtime, the LLM API disallows such combination. """ # These bounds are written as negated range checks rather than as # `value < low or value > high`, so that NaN is rejected too: every # comparison against NaN is False, which lets it slip through the # positive form. The top_p_decay / top_p_min checks below already use # this form. if self.top_p is not None and not 0 <= self.top_p <= 1: raise ValueError(f"require 0 <= top_p <= 1, got top_p={self.top_p}") if self.top_k is not None and self.top_k < 0: raise ValueError(f"require top_k >= 0, got top_k={self.top_k}") if self.min_p is not None and not 0 <= self.min_p <= 1: raise ValueError(f"require 0 <= min_p <= 1, got min_p={self.min_p}") if self.temperature is not None and not self.temperature >= 0: raise ValueError(f"require temperature >= 0, got temperature={self.temperature}") # Top-p decay param ranges mirror the hard checks in the # executor::SamplingConfig constructor (samplingConfig.cpp check* # helpers); rejecting here gives a clear, early error instead of a # RuntimeError from the C++ boundary. Note top_p_min > top_p is # intentionally allowed (the runtime top-p may rise toward top_p_min). if self.top_p_decay is not None and not 0.0 < self.top_p_decay <= 1.0: raise ValueError(f"require 0 < top_p_decay <= 1, got top_p_decay={self.top_p_decay}") if self.top_p_min is not None and not 0.0 < self.top_p_min <= 1.0: raise ValueError(f"require 0 < top_p_min <= 1, got top_p_min={self.top_p_min}") if self.top_p_reset_ids is not None and self.top_p_reset_ids < 0: raise ValueError( f"require top_p_reset_ids >= 0, got top_p_reset_ids={self.top_p_reset_ids}" ) if self.best_of is not None and self.best_of < self.n: raise ValueError(f"best_of ({self.best_of}) cannot be less than n ({self.n})") if ( self.best_of is not None and self.best_of > 1 and self._greedy_decoding and not os.environ.get("TLLM_ALLOW_N_GREEDY_DECODING", None) ): raise ValueError( f"Greedy decoding in the LLM API does not allow multiple " f"returns. Please set to best_of=1, got best_of={self.best_of}. " f"Please set to best_of=1 or set an environment variable " f"TLLM_ALLOW_N_GREEDY_DECODING=1 to allow best_of > 1 " f"under the greedy decoding." ) self.logprobs_mode = LogprobMode(self.logprobs_mode) if self.truncate_prompt_tokens is not None and self.truncate_prompt_tokens < 1: raise ValueError( f"truncate_prompt_tokens must be >= 1, got {self.truncate_prompt_tokens}" ) if self.guided_decoding is not None: self.guided_decoding._validate() self.thinking_token_budget = validate_thinking_token_budget(self.thinking_token_budget) # correct types as users might pass in logprob=True for Top-0 logprobs and logprobs=False for no logprobs if self.logprobs is False: self.logprobs = None if self.logprobs is True: self.logprobs = 0 check_logprobs_limit("logprobs", self.logprobs) check_logprobs_limit("prompt_logprobs", self.prompt_logprobs) if self.logprobs_simple_format and self.logprobs != 0: raise ValueError( f"logprobs_simple_format=True requires logprobs == 0, got logprobs={self.logprobs}" ) if self.prompt_logprobs_simple_format and self.prompt_logprobs != 0: raise ValueError( "prompt_logprobs_simple_format=True requires prompt_logprobs == 0, got " f"prompt_logprobs={self.prompt_logprobs}" ) if self.logprobs_simple_format and self.use_beam_search: raise ValueError("logprobs_simple_format is not supported with beam search") def _set_request_provided_fields(self, field_names: Iterable[str]) -> None: """Record sampler fields explicitly supplied by a serving request.""" self._request_provided_fields = frozenset(field_names) & _GENERATION_CONFIG_SAMPLING_FIELDS def _apply_generation_config_defaults(self, generation_config: Mapping[str, Any]) -> None: """Apply compatible model defaults without overriding request values.""" for field_name in _GENERATION_CONFIG_SAMPLING_FIELDS: if field_name not in generation_config: continue # Direct LLM API calls preserve None as the unset sentinel. Serving # adapters materialize protocol defaults, so they instead record # which fields the request explicitly supplied. if self._request_provided_fields is None: request_provided = getattr(self, field_name) is not None else: request_provided = field_name in self._request_provided_fields if request_provided: continue # A JSON null is also unset and must fall through to the existing # TRT-LLM or serving default. value = generation_config[field_name] if value is None: continue # Hugging Face also permits "never", which the TRT-LLM integer # early-stopping setting cannot represent. if field_name == "early_stopping" and not isinstance(value, (bool, int)): logger.warning( "Ignoring unsupported generation_config.json early_stopping value " f"{value!r}; TRT-LLM supports only boolean or integer values." ) continue setattr(self, field_name, value) self._validate() # NB: The predicates below are static because downstream code (e.g. # sampling_resolution.resolve_row_params) only holds instances of # bindings.SamplingConfig (not SamplingParams). They are the single # source of truth for the greedy / top-p-decay resolution shared by # _greedy_decoding and the torch sampler.
[docs] @staticmethod def params_imply_top_p_decay_active(top_p_decay: Optional[float]) -> bool: """Whether dynamic top-p decay is active. Active iff ``top_p_decay`` is explicitly set and ``< 1.0``; a decay of ``1.0`` (the C++ default) is a no-op. Values outside ``(0, 1]`` are rejected up front (_validate and the executor::SamplingConfig constructor), so they never reach this predicate. """ return top_p_decay is not None and top_p_decay < 1.0
[docs] @staticmethod def params_imply_explicit_greedy( *, temperature: Optional[float], top_p: Optional[float], top_k: Optional[int], min_p: Optional[float], ) -> bool: """Whether the request carries an explicit greedy control. Explicit means top_k == 1, top_p == 0.0, min_p == 1.0, or temperature == 0, as opposed to the implicit "all params unset" greedy default. min_p == 1.0 keeps only tokens whose probability equals the row maximum, so like top_p == 0.0 it collapses sampling to a single token. """ return top_k == 1 or top_p == 0.0 or min_p == 1.0 or temperature == 0
[docs] @staticmethod def params_imply_greedy_decoding( *, temperature: Optional[float], top_p: Optional[float], top_k: Optional[int], use_beam_search: bool | None, min_p: Optional[float] = None, top_p_decay: Optional[float] = None, ) -> bool: """Whether the parameters resolve to greedy decoding. An explicit greedy control always wins. The implicit "all params unset" greedy default is overridden by any active sampling knob: an active top-p decay (which implies top-p sampling so the decayed runtime top-p can take effect) or a ``min_p`` in ``(0, 1)`` (which still selects among multiple tokens); callers that do not support decay may omit ``top_p_decay``. """ if use_beam_search: return False if SamplingParams.params_imply_explicit_greedy( temperature=temperature, top_p=top_p, top_k=top_k, min_p=min_p ): return True implicitly_greedy = temperature is None and top_p is None and top_k is None min_p_active = min_p is not None and min_p > 0.0 decay_active = SamplingParams.params_imply_top_p_decay_active(top_p_decay) return implicitly_greedy and not min_p_active and not decay_active
@property def _greedy_decoding(self) -> bool: return self.params_imply_greedy_decoding( temperature=self.temperature, top_p=self.top_p, top_k=self.top_k, use_beam_search=self.use_beam_search, min_p=self.min_p, top_p_decay=self.top_p_decay, ) def _setup( self, tokenizer, hf_model_config, generation_config, add_special_tokens: bool = False ) -> "SamplingParams": if self.end_id is None: self.end_id = tokenizer.eos_token_id self.pad_id = tokenizer.pad_token_id if self.pad_id is None: self.pad_id = self.end_id def _encode(tokenizer, text, add_special_tokens): try: return tokenizer.encode(text, add_special_tokens=add_special_tokens) except TypeError: # For tiktokenizer, the encode method does not have add_special_tokens argument return tokenizer.encode(text) if self.bad is not None: strs = [self.bad] if isinstance(self.bad, str) else self.bad self._bad_word_ids = [_encode(tokenizer, s, add_special_tokens) for s in strs] if self.stop is not None: strs = [self.stop] if isinstance(self.stop, str) else self.stop self._stop_word_ids = [_encode(tokenizer, s, add_special_tokens) for s in strs] # Add eos_token_id in generation_config to stop_token_ids # The eos_token_id in generation_config are really mean to stop the text generation. if generation_config is not None and generation_config.eos_token_id is not None: if isinstance(generation_config.eos_token_id, int): generation_config.eos_token_id = [generation_config.eos_token_id] # else is always List[int] if not self.stop_token_ids: self.stop_token_ids = [] for stop_token in generation_config.eos_token_id: if stop_token != self.end_id and stop_token not in self.stop_token_ids: self.stop_token_ids.append(stop_token) if not self.stop_token_ids: self.stop_token_ids = None return self def _get_bad_words(self) -> List[List[int]]: words = [] if self.bad_token_ids: words = [[i] for i in self.bad_token_ids] if self.bad is None: return words else: if self._bad_word_ids is None: raise RuntimeError( f"{self.__class__.__name__}.bad ({self.bad}) is not processed by tokenizer, " "please call the setup method." ) return words + self._bad_word_ids def _get_stop_words(self) -> List[List[int]]: words = [] if self.stop_token_ids: words = [[i] for i in self.stop_token_ids] if self.stop is None: return words else: if self._stop_word_ids is None: raise RuntimeError( f"{self.__class__.__name__}.stop ({self.stop}) is not processed by tokenizer, " "please call the setup method." ) return words + self._stop_word_ids def _get_stop_reasons_and_words(self) -> List[Tuple[Union[str, int], List[List[int]]]]: stop_reasons = [] if self.stop_token_ids is not None: stop_reasons.extend(self.stop_token_ids) if self.stop is not None: if isinstance(self.stop, str): stop_reasons.append(self.stop) else: stop_reasons.extend(self.stop) stop_words = self._get_stop_words() return list(zip(stop_reasons, stop_words)) def _get_sampling_config(self) -> tllme.SamplingConfig: # A map from the SamplingConfig fields of the LLM API to their # corresponding field names of the Executor of TRT-LLM C++ runtime. # In sampling, there is no parameter that directly matches 'best_of', # so outputs must be trimmed during postprocessing. # | LLM API | TRT-LLM Executor | # --------------|-----------------|------------------------| # | Beam search | use_beam_search | beam_width > 1 | # | Beam search | n | num_return_sequences | # | Beam search | best_of | beam_width | # |-------------|-----------------|------------------------| # | Sampling | use_beam_search | beam_width == 1 | # | Sampling | n | num_return_sequences | # | Sampling | best_of | no corresponding param | fields = _sampling_config_fields() unmatched_params = [ "num_return_sequences", "beam_width", "n", "best_of", "use_beam_search", ] llmapi_to_rt_param_map = {f: getattr(self, f) for f in fields if f not in unmatched_params} if self.use_beam_search: llmapi_to_rt_param_map["num_return_sequences"] = self.n llmapi_to_rt_param_map["beam_width"] = self.best_of else: llmapi_to_rt_param_map["num_return_sequences"] = self.best_of llmapi_to_rt_param_map["beam_width"] = 1 return tllme.SamplingConfig(**llmapi_to_rt_param_map) def _get_output_config(self) -> tllme.OutputConfig: fields = _output_config_fields() config_kwargs = {f: getattr(self, f) for f in fields} config_kwargs["return_log_probs"] = self.logprobs is not None if self.prompt_logprobs is not None and not self.return_context_logits: logger.info( "Since prompt_logprobs is requested but return_context_logits is False, " "internally enabling context logits for prompt logprobs computation. " "context logits will be dropped after computation as the user didn't explicitly request them." ) # TODO: Find a more elegant way to do this. # NOTE: This is an internal hack, so we can entirely avoid introducing # `prompt_logprobs` into the executor bindings and further into # model engine / sampler. # This is because, prompt_logprobs is a derived quantity from # context logits, and the capability to post-compute it # already exists in the worker. (see _get_logprobs in base_worker.py) config_kwargs["return_context_logits"] = True if config_kwargs.get("additional_model_outputs") is not None: config_kwargs["additional_model_outputs"] = [ tllme.AdditionalModelOutput(name=output_name, gather_context=False) for output_name in config_kwargs["additional_model_outputs"] ] return tllme.OutputConfig(**config_kwargs) def _get_guided_decoding_params(self) -> tllme.GuidedDecodingParams: if self.guided_decoding is None: return None if self.guided_decoding.json_object: return tllme.GuidedDecodingParams(tllme.GuidedDecodingParams.GuideType.JSON) elif self.guided_decoding.json is not None: json_schema = self.guided_decoding.json if isinstance(json_schema, BaseModel): json_schema = json_schema.model_json_schema() if isinstance(json_schema, dict): json_schema = json.dumps(json_schema) return tllme.GuidedDecodingParams( tllme.GuidedDecodingParams.GuideType.JSON_SCHEMA, json_schema ) elif self.guided_decoding.regex is not None: return tllme.GuidedDecodingParams( tllme.GuidedDecodingParams.GuideType.REGEX, self.guided_decoding.regex ) elif self.guided_decoding.grammar is not None: return tllme.GuidedDecodingParams( tllme.GuidedDecodingParams.GuideType.EBNF_GRAMMAR, self.guided_decoding.grammar ) elif self.guided_decoding.structural_tag is not None: return tllme.GuidedDecodingParams( tllme.GuidedDecodingParams.GuideType.STRUCTURAL_TAG, self.guided_decoding.structural_tag, ) else: return None
_SAMPLING_PARAMS_FIELDS = tuple(f.name for f in fields(SamplingParams)) _get_sampling_params_state = operator.attrgetter(*_SAMPLING_PARAMS_FIELDS) # Only its generated ``__init__`` is used: one store per field, no ``__post_init__``. _set_sampling_params_state = make_dataclass( "_SamplingParamsState", _SAMPLING_PARAMS_FIELDS, repr=False, eq=False ).__init__ def _restore_sampling_params(state: tuple) -> "SamplingParams": sampling_params = object.__new__(SamplingParams) _set_sampling_params_state(sampling_params, *state) return sampling_params @functools.cache def _sampling_config_fields() -> frozenset[str]: """Return the ``tllme.SamplingConfig`` field names. They are fixed by the bindings, so the ``dir()`` scan runs once instead of once per request. """ return frozenset(f for f in dir(tllme.SamplingConfig) if not f.startswith("__")) @functools.cache def _output_config_fields() -> tuple[str, ...]: """Return the ``tllme.OutputConfig`` field names that ``SamplingParams`` also defines.""" sampling_param_fields = set(dir(SamplingParams)) return tuple( f for f in dir(tllme.OutputConfig) if not f.startswith("__") and f in sampling_param_fields )