CoolFace
Modelpublic

hymenjj/llama-cpp-python-prebuilt

sourceHugging Faceupdated 7mo agoView on Hugging Face
0likes
_internals.py857 linesDownload Raw Back to llama_cpp
1from __future__ import annotations2 3import os4import ctypes5 6from typing import (7    Dict,8    List,9    Tuple,10    Optional,11    Sequence,12    Callable,13    Union,14)15from dataclasses import dataclass, field16from contextlib import ExitStack17 18import numpy as np19import numpy.typing as npt20 21from .llama_types import *22from .llama_grammar import LlamaGrammar23from ._utils import suppress_stdout_stderr24 25import llama_cpp.llama_cpp as llama_cpp26 27 28# Python wrappers over llama.h structs29 30 31class LlamaModel:32    """Intermediate Python wrapper for a llama.cpp llama_model.33    NOTE: For stability it's recommended you use the Llama class instead."""34 35    def __init__(36        self,37        *,38        path_model: str,39        params: llama_cpp.llama_model_params,40        verbose: bool = True,41    ):42        self.path_model = path_model43        self.params = params44        self.verbose = verbose45        self._exit_stack = ExitStack()46 47        model = None48 49        if not os.path.exists(path_model):50            raise ValueError(f"Model path does not exist: {path_model}")51 52        with suppress_stdout_stderr(disable=verbose):53            model = llama_cpp.llama_model_load_from_file(54                self.path_model.encode("utf-8"), self.params55            )56 57        if model is None:58            raise ValueError(f"Failed to load model from file: {path_model}")59 60        vocab = llama_cpp.llama_model_get_vocab(model)61 62        if vocab is None:63            raise ValueError(f"Failed to get vocab from model: {path_model}")64 65        self.model = model66        self.vocab = vocab67        self.sampler = None  # LlamaModel doesn't use samplers, but some cleanup code expects this attribute68 69        def free_model():70            if self.model is None:71                return72            llama_cpp.llama_model_free(self.model)73            self.model = None74 75        self._exit_stack.callback(free_model)76 77    def close(self):78        if self.sampler is not None:79            # NOTE: Must remove custom samplers before free or llama.cpp will try to free them80            for i, _ in reversed(self.custom_samplers):81                llama_cpp.llama_sampler_chain_remove(self.sampler, i)82            self.custom_samplers.clear()83        self._exit_stack.close()84 85    def __del__(self):86        self.close()87 88    def vocab_type(self) -> int:89        return llama_cpp.llama_vocab_type(self.vocab)90 91    def n_vocab(self) -> int:92        return llama_cpp.llama_vocab_n_tokens(self.vocab)93 94    def n_ctx_train(self) -> int:95        return llama_cpp.llama_model_n_ctx_train(self.model)96 97    def n_embd(self) -> int:98        return llama_cpp.llama_model_n_embd(self.model)99 100    def rope_freq_scale_train(self) -> float:101        return llama_cpp.llama_model_rope_freq_scale_train(self.model)102 103    def desc(self) -> str:104        buf = ctypes.create_string_buffer(1024)105        llama_cpp.llama_model_desc(self.model, buf, 1024)106        return buf.value.decode("utf-8")107 108    def size(self) -> int:109        return llama_cpp.llama_model_size(self.model)110 111    def n_params(self) -> int:112        return llama_cpp.llama_model_n_params(self.model)113 114    def get_tensor(self, name: str) -> ctypes.c_void_p:115        raise NotImplementedError("get_tensor is not implemented in llama.cpp")116 117    # Vocab118 119    def token_get_text(self, token: int) -> str:120        return llama_cpp.llama_vocab_get_text(self.vocab, token).decode("utf-8")121 122    def token_get_score(self, token: int) -> float:123        return llama_cpp.llama_vocab_get_score(self.vocab, token)124 125    def token_get_attr(self, token: int) -> int:126        return llama_cpp.llama_vocab_get_attr(self.vocab, token)127 128    # Special tokens129 130    def token_bos(self) -> int:131        return llama_cpp.llama_vocab_bos(self.vocab)132 133    def token_eos(self) -> int:134        return llama_cpp.llama_vocab_eos(self.vocab)135 136    def token_cls(self) -> int:137        return llama_cpp.llama_vocab_cls(self.vocab)138 139    def token_sep(self) -> int:140        return llama_cpp.llama_vocab_sep(self.vocab)141 142    def token_nl(self) -> int:143        return llama_cpp.llama_vocab_nl(self.vocab)144 145    def token_prefix(self) -> int:146        return llama_cpp.llama_vocab_fim_pre(self.vocab)147 148    def token_middle(self) -> int:149        return llama_cpp.llama_vocab_fim_mid(self.vocab)150 151    def token_suffix(self) -> int:152        return llama_cpp.llama_vocab_fim_suf(self.vocab)153 154    def token_eot(self) -> int:155        return llama_cpp.llama_vocab_eot(self.vocab)156 157    def add_bos_token(self) -> bool:158        return llama_cpp.llama_vocab_get_add_bos(self.vocab)159 160    def add_eos_token(self) -> bool:161        return llama_cpp.llama_vocab_get_add_eos(self.vocab)162 163    # Tokenization164 165    def tokenize(self, text: bytes, add_bos: bool, special: bool):166        n_ctx = self.n_ctx_train()167        tokens = (llama_cpp.llama_token * n_ctx)()168        n_tokens = llama_cpp.llama_tokenize(169            self.vocab, text, len(text), tokens, n_ctx, add_bos, special170        )171        if n_tokens < 0:172            n_tokens = abs(n_tokens)173            tokens = (llama_cpp.llama_token * n_tokens)()174            n_tokens = llama_cpp.llama_tokenize(175                self.vocab, text, len(text), tokens, n_tokens, add_bos, special176            )177            if n_tokens < 0:178                raise RuntimeError(179                    f'Failed to tokenize: text="{text}" n_tokens={n_tokens}'180                )181        return list(tokens[:n_tokens])182 183    def token_to_piece(self, token: int, special: bool = False) -> bytes:184        buf = ctypes.create_string_buffer(32)185        llama_cpp.llama_token_to_piece(self.vocab, token, buf, 32, 0, special)186        return bytes(buf)187 188    def detokenize(self, tokens: List[int], special: bool = False) -> bytes:189        output = b""190        size = 32191        buffer = (ctypes.c_char * size)()192        for token in tokens:193            n = llama_cpp.llama_token_to_piece(194                self.vocab, llama_cpp.llama_token(token), buffer, size, 0, special195            )196            assert n <= size197            output += bytes(buffer[:n])198        # NOTE: Llama1 models automatically added a space at the start of the prompt199        # this line removes a leading space if the first token is a beginning of sentence token200        return (201            output[1:]202            if len(tokens) > 0 and tokens[0] == self.token_bos() and output[0:1] == b" "203            else output204        )205 206    # Extra207    def metadata(self) -> Dict[str, str]:208        metadata: Dict[str, str] = {}209        buffer_size = 1024210        buffer = ctypes.create_string_buffer(buffer_size)211        # zero the buffer212        buffer.value = b"\0" * buffer_size213        # iterate over model keys214        for i in range(llama_cpp.llama_model_meta_count(self.model)):215            nbytes = llama_cpp.llama_model_meta_key_by_index(216                self.model, i, buffer, buffer_size217            )218            if nbytes > buffer_size:219                buffer_size = nbytes + 1220                buffer = ctypes.create_string_buffer(buffer_size)221                nbytes = llama_cpp.llama_model_meta_key_by_index(222                    self.model, i, buffer, buffer_size223                )224            key = buffer.value.decode("utf-8")225            nbytes = llama_cpp.llama_model_meta_val_str_by_index(226                self.model, i, buffer, buffer_size227            )228            if nbytes > buffer_size:229                buffer_size = nbytes + 1230                buffer = ctypes.create_string_buffer(buffer_size)231                nbytes = llama_cpp.llama_model_meta_val_str_by_index(232                    self.model, i, buffer, buffer_size233                )234            value = buffer.value.decode("utf-8")235            metadata[key] = value236        return metadata237 238    @staticmethod239    def default_params():240        """Get the default llama_model_params."""241        return llama_cpp.llama_model_default_params()242 243 244class LlamaContext:245    """Intermediate Python wrapper for a llama.cpp llama_context.246    NOTE: For stability it's recommended you use the Llama class instead."""247 248    def __init__(249        self,250        *,251        model: LlamaModel,252        params: llama_cpp.llama_context_params,253        verbose: bool = True,254    ):255        self.model = model256        self.params = params257        self.verbose = verbose258        self._exit_stack = ExitStack()259 260        ctx = llama_cpp.llama_init_from_model(self.model.model, self.params)261 262        if ctx is None:263            raise ValueError("Failed to create llama_context")264 265        self.ctx = ctx266        self.memory = llama_cpp.llama_get_memory(self.ctx)267        self.sampler = None  # LlamaContext doesn't manage samplers directly, but some cleanup code expects this attribute268 269        def free_ctx():270            if self.ctx is None:271                return272            llama_cpp.llama_free(self.ctx)273            self.ctx = None274 275        self._exit_stack.callback(free_ctx)276 277    def close(self):278        self._exit_stack.close()279 280    def __del__(self):281        self.close()282 283    def n_ctx(self) -> int:284        return llama_cpp.llama_n_ctx(self.ctx)285 286    def pooling_type(self) -> int:287        return llama_cpp.llama_pooling_type(self.ctx)288 289    def kv_cache_clear(self):290        assert self.memory is not None, "Memory is not initialized"291        llama_cpp.llama_memory_clear(self.memory, True)292 293    def kv_cache_seq_rm(self, seq_id: int, p0: int, p1: int):294        assert self.memory is not None, "Memory is not initialized"295        seq_id = seq_id if seq_id >= 0 else 0296        llama_cpp.llama_memory_seq_rm(self.memory, seq_id, p0, p1)297 298    def kv_cache_seq_cp(self, seq_id_src: int, seq_id_dst: int, p0: int, p1: int):299        assert self.memory is not None, "Memory is not initialized"300        llama_cpp.llama_memory_seq_cp(self.memory, seq_id_src, seq_id_dst, p0, p1)301 302    def kv_cache_seq_keep(self, seq_id: int):303        assert self.memory is not None, "Memory is not initialized"304        llama_cpp.llama_memory_seq_keep(self.memory, seq_id)305 306    def kv_cache_seq_shift(self, seq_id: int, p0: int, p1: int, shift: int):307        assert self.memory is not None, "Memory is not initialized"308        llama_cpp.llama_memory_seq_add(self.memory, seq_id, p0, p1, shift)309 310    def get_state_size(self) -> int:311        return llama_cpp.llama_state_get_size(self.ctx)312 313    # TODO: copy_state_data314 315    # TODO: set_state_data316 317    # TODO: llama_load_session_file318 319    # TODO: llama_save_session_file320 321    def decode(self, batch: LlamaBatch):322        return_code = llama_cpp.llama_decode(323            self.ctx,324            batch.batch,325        )326        if return_code != 0:327            raise RuntimeError(f"llama_decode returned {return_code}")328 329    def encode(self, batch: LlamaBatch):330        return_code = llama_cpp.llama_encode(331            self.ctx,332            batch.batch,333        )334        if return_code != 0:335            raise RuntimeError(f"llama_encode returned {return_code}")336 337    def set_n_threads(self, n_threads: int, n_threads_batch: int):338        llama_cpp.llama_set_n_threads(self.ctx, n_threads, n_threads_batch)339 340    def get_logits(self):341        return llama_cpp.llama_get_logits(self.ctx)342 343    def get_logits_ith(self, i: int):344        return llama_cpp.llama_get_logits_ith(self.ctx, i)345 346    def get_embeddings(self):347        return llama_cpp.llama_get_embeddings(self.ctx)348 349    def get_embeddings_ith(self, i: int):350        return llama_cpp.llama_get_embeddings_ith(self.ctx, i)351 352    def get_embeddings_seq(self, seq_id: int):353        return llama_cpp.llama_get_embeddings_seq(self.ctx, seq_id)354 355    # Sampling functions - deprecated, use LlamaSampler instead356 357    def set_rng_seed(self, seed: int):358        raise NotImplementedError("set_rng_seed is deprecated, use LlamaSampler instead")359 360    def sample_repetition_penalties(361        self,362        candidates: "_LlamaTokenDataArray",363        last_tokens_data: "llama_cpp.Array[llama_cpp.llama_token]",364        penalty_last_n: int,365        penalty_repeat: float,366        penalty_freq: float,367        penalty_present: float,368    ):369        raise NotImplementedError("sample_repetition_penalties is deprecated, use LlamaSampler instead")370 371    def sample_softmax(self, candidates: "_LlamaTokenDataArray"):372        raise NotImplementedError("sample_softmax is deprecated, use LlamaSampler instead")373 374    def sample_top_k(self, candidates: "_LlamaTokenDataArray", k: int, min_keep: int):375        raise NotImplementedError("sample_top_k is deprecated, use LlamaSampler instead")376 377    def sample_top_p(self, candidates: "_LlamaTokenDataArray", p: float, min_keep: int):378        raise NotImplementedError("sample_top_p is deprecated, use LlamaSampler instead")379 380    def sample_min_p(self, candidates: "_LlamaTokenDataArray", p: float, min_keep: int):381        raise NotImplementedError("sample_min_p is deprecated, use LlamaSampler instead")382 383    def sample_typical(384        self, candidates: "_LlamaTokenDataArray", p: float, min_keep: int385    ):386        raise NotImplementedError("sample_typical is deprecated, use LlamaSampler instead")387 388    def sample_temp(self, candidates: "_LlamaTokenDataArray", temp: float):389        raise NotImplementedError("sample_temp is deprecated, use LlamaSampler instead")390 391    def sample_grammar(self, candidates: "_LlamaTokenDataArray", grammar: LlamaGrammar):392        raise NotImplementedError("sample_grammar is deprecated, use LlamaSampler instead")393 394    def sample_token_mirostat(395        self,396        candidates: "_LlamaTokenDataArray",397        tau: float,398        eta: float,399        m: int,400        mu: llama_cpp.CtypesPointerOrRef[ctypes.c_float],401    ) -> int:402        raise NotImplementedError("sample_token_mirostat is deprecated, use LlamaSampler instead")403 404    def sample_token_mirostat_v2(405        self,406        candidates: "_LlamaTokenDataArray",407        tau: float,408        eta: float,409        mu: llama_cpp.CtypesPointerOrRef[ctypes.c_float],410    ) -> int:411        raise NotImplementedError("sample_token_mirostat_v2 is deprecated, use LlamaSampler instead")412 413    def sample_token_greedy(self, candidates: "_LlamaTokenDataArray") -> int:414        raise NotImplementedError("sample_token_greedy is deprecated, use LlamaSampler instead")415 416    def sample_token(self, candidates: "_LlamaTokenDataArray") -> int:417        raise NotImplementedError("sample_token is deprecated, use LlamaSampler instead")418 419    # Grammar420    def grammar_accept_token(self, grammar: LlamaGrammar, token: int):421        raise NotImplementedError("grammar_accept_token is deprecated, use LlamaSampler instead")422 423    def reset_timings(self):424        llama_cpp.llama_perf_context_reset(self.ctx)425 426    def print_timings(self):427        llama_cpp.llama_perf_context_print(self.ctx)428 429    # Utility functions430    @staticmethod431    def default_params():432        """Get the default llama_context_params."""433        return llama_cpp.llama_context_default_params()434 435 436class LlamaBatch:437    def __init__(438        self, *, n_tokens: int, embd: int, n_seq_max: int, verbose: bool = True439    ):440        self._n_tokens = n_tokens441        self.embd = embd442        self.n_seq_max = n_seq_max443        self.verbose = verbose444        self._exit_stack = ExitStack()445 446        batch = llama_cpp.llama_batch_init(self._n_tokens, self.embd, self.n_seq_max)447 448        if batch is None:449            raise ValueError("Failed to create llama_batch")450 451        self.batch = batch452        self.sampler = None  # LlamaBatch doesn't use samplers, but some cleanup code expects this attribute453 454        def free_batch():455            if self.batch is None:456                return457            llama_cpp.llama_batch_free(self.batch)458            self.batch = None459 460        self._exit_stack.callback(free_batch)461 462    def close(self):463        self._exit_stack.close()464 465    def __del__(self):466        self.close()467 468    def n_tokens(self) -> int:469        return self.batch.n_tokens470 471    def reset(self):472        self.batch.n_tokens = 0473 474    def set_batch(self, batch: Sequence[int], n_past: int, logits_all: bool):475        n_tokens = len(batch)476        self.batch.n_tokens = n_tokens477        for i in range(n_tokens):478            self.batch.token[i] = batch[i]479            self.batch.pos[i] = n_past + i480            self.batch.seq_id[i][0] = 0481            self.batch.n_seq_id[i] = 1482            self.batch.logits[i] = logits_all483        self.batch.logits[n_tokens - 1] = True484 485    def add_sequence(self, batch: Sequence[int], seq_id: int, logits_all: bool):486        n_tokens = len(batch)487        n_tokens0 = self.batch.n_tokens488        self.batch.n_tokens += n_tokens489        for i in range(n_tokens):490            j = n_tokens0 + i491            self.batch.token[j] = batch[i]492            self.batch.pos[j] = i493            self.batch.seq_id[j][0] = seq_id494            self.batch.n_seq_id[j] = 1495            self.batch.logits[j] = logits_all496        self.batch.logits[n_tokens - 1] = True497 498 499class LlamaTokenDataArray:500    def __init__(self, *, n_vocab: int):501        self.n_vocab = n_vocab502        self.candidates_data = np.recarray(503            (self.n_vocab,),504            dtype=np.dtype(505                [("id", np.intc), ("logit", np.single), ("p", np.single)], align=True506            ),507        )508        self.candidates = llama_cpp.llama_token_data_array(509            data=self.candidates_data.ctypes.data_as(llama_cpp.llama_token_data_p),510            size=self.n_vocab,511            sorted=False,512        )513        self.default_candidates_data_id = np.arange(self.n_vocab, dtype=np.intc)  # type: ignore514        self.default_candidates_data_p = np.zeros(self.n_vocab, dtype=np.single)515        self.sampler = None  # LlamaTokenDataArray doesn't use samplers, but some cleanup code expects this attribute516 517    def copy_logits(self, logits: npt.NDArray[np.single]):518        self.candidates_data.id[:] = self.default_candidates_data_id519        self.candidates_data.logit[:] = logits520        self.candidates_data.p[:] = self.default_candidates_data_p521        self.candidates.sorted = False522        self.candidates.size = self.n_vocab523 524 525# Embedding functions526 527 528def normalize_embedding(embedding):529    norm = float(np.linalg.norm(embedding))530    if norm == 0.0:531        return embedding532    return [v / norm for v in embedding]533 534 535# Python wrappers over common/sampling structs536 537 538@dataclass539class LlamaSamplingParams:540    n_prev: int = 64541    n_probs: int = 0542    top_k: int = 40543    top_p: float = 0.95544    min_p: float = 0.05545    tfs_z: float = 1.00546    typical_p: float = 1.00547    temp: float = 0.80548    penalty_last_n: int = 64549    penalty_repeat: float = 1.0550    penalty_freq: float = 0.00551    penalty_present: float = 0.00552    mirostat: int = 0553    mirostat_tau: float = 5.00554    mirostat_eta: float = 0.10555    penalize_nl: bool = True556 557    grammar: str = ""558 559    cfg_negative_prompt: str = ""560    cfg_scale: float = 1.00561 562    logit_bias: dict[int, float] = field(default_factory=dict)563 564 565@dataclass566class LlamaSamplingContext:567    params: LlamaSamplingParams = field(default_factory=LlamaSamplingParams)568    mirostat_mu: ctypes.c_float = field(default_factory=ctypes.c_float)569    grammar: Optional[LlamaGrammar] = None570    # NOTE: Missing parsed_grammar571    prev: list[int] = field(default_factory=list)572    cur: list[llama_cpp.llama_token_data] = field(default_factory=list)573 574    def reset(self):575        self.prev = []576        self.cur = []577        if self.grammar is not None:578            self.grammar.reset()579 580    def cp(self):581        return LlamaSamplingContext(582            params=self.params,583            mirostat_mu=self.mirostat_mu,584            grammar=self.grammar,585            prev=self.prev.copy(),586            cur=self.cur.copy(),587        )588 589    def last(self) -> Optional[int]:590        if len(self.prev) > 0:591            return self.prev[-1]592        else:593            return None594 595    def prev_str(self, ctx_main: LlamaContext, n: int) -> str:596        return ctx_main.model.detokenize(self.prev[-n:]).decode("utf-8")597 598    def sample(599        self,600        ctx_main: LlamaContext,601        idx: int = 0,602        logits_array: Optional[npt.NDArray[np.single]] = None,603    ):604        # This method is deprecated in favor of using LlamaSampler directly605        raise NotImplementedError("LlamaSamplingContext.sample is deprecated, use LlamaSampler instead")606 607    def accept(self, ctx_main: LlamaContext, id: int, apply_grammar: bool):608        self.prev.append(id)609 610 611class CustomSampler:612    def __init__(613        self, apply_func: Callable[[llama_cpp.llama_token_data_array], None]614    ):615        self.apply_func = apply_func616 617        def apply_wrapper(618            sampler: llama_cpp.llama_sampler_p,619            cur_p: llama_cpp.llama_token_data_array_p,620        ):621            self.apply_func(cur_p)622 623        def free_wrapper(sampler: llama_cpp.llama_sampler_p):624            pass625 626        sampler_i = llama_cpp.llama_sampler_i()627        sampler_i.apply = llama_cpp.llama_sampler_i_apply(apply_wrapper)628        self._apply_wrapper_ref = apply_wrapper629 630        sampler_i.name = llama_cpp.llama_sampler_i_name(0)631        sampler_i.accept = llama_cpp.llama_sampler_i_accept(0)632        sampler_i.reset = llama_cpp.llama_sampler_i_reset(0)633        sampler_i.clone = llama_cpp.llama_sampler_i_clone(0)634        sampler_i.free = llama_cpp.llama_sampler_i_free(0)635 636        self.sampler = llama_cpp.llama_sampler()637        self.sampler.iface = ctypes.pointer(sampler_i)638        self.sampler.ctx = None639 640    def get_sampler(self) -> llama_cpp.llama_sampler_p:641        return ctypes.pointer(self.sampler)642 643 644class LlamaSampler:645    def __init__(self):646        params = llama_cpp.llama_sampler_chain_default_params()647        self.sampler = llama_cpp.llama_sampler_chain_init(params)648        self.custom_samplers: List[Tuple[int, CustomSampler]] = []649        self._exit_stack = ExitStack()650 651        def free_sampler():652            if self.sampler is not None:653                # NOTE: Must remove custom samplers before free or llama.cpp will try to free them654                for i, _ in reversed(self.custom_samplers):655                    llama_cpp.llama_sampler_chain_remove(self.sampler, i)656                llama_cpp.llama_sampler_free(self.sampler)657                self.sampler = None658 659        self._exit_stack.callback(free_sampler)660 661    def close(self):662        self._exit_stack.close()663 664    def __del__(self):665        self.close()666 667    def add_greedy(self):668        sampler = llama_cpp.llama_sampler_init_greedy()669        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)670 671    def add_dist(self, seed: int):672        sampler = llama_cpp.llama_sampler_init_dist(seed)673        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)674 675    def add_softmax(self):676        sampler = llama_cpp.llama_sampler_init_softmax()677        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)678 679    def add_top_k(self, k: int):680        sampler = llama_cpp.llama_sampler_init_top_k(k)681        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)682 683    def add_top_p(self, p: float, min_keep: int = 1):684        sampler = llama_cpp.llama_sampler_init_top_p(p, min_keep)685        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)686 687    def add_min_p(self, p: float, min_keep: int = 1):688        sampler = llama_cpp.llama_sampler_init_min_p(p, min_keep)689        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)690 691    def add_typical(self, p: float, min_keep: int = 1):692        sampler = llama_cpp.llama_sampler_init_typical(p, min_keep)693        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)694 695    def add_temp(self, temp: float):696        sampler = llama_cpp.llama_sampler_init_temp(temp)697        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)698 699    def add_temp_ext(self, t: float, delta: float, exponent: float):700        sampler = llama_cpp.llama_sampler_init_temp_ext(t, delta, exponent)701        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)702 703    def add_xtc(self, p: float, t: float, min_keep: int, seed: int):704        sampler = llama_cpp.llama_sampler_init_xtc(p, t, min_keep, seed)705        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)706 707    def add_top_n_sigma(self, n: float):708        sampler = llama_cpp.llama_sampler_init_top_n_sigma(n)709        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)710 711    def add_mirostat(self, n_vocab: int, seed: int, tau: float, eta: float, m: int):712        sampler = llama_cpp.llama_sampler_init_mirostat(n_vocab, seed, tau, eta, m)713        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)714 715    def add_mirostat_v2(self, seed: int, tau: float, eta: float):716        sampler = llama_cpp.llama_sampler_init_mirostat_v2(seed, tau, eta)717        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)718 719    def add_grammar(self, model: LlamaModel, grammar: LlamaGrammar):720        sampler = llama_cpp.llama_sampler_init_grammar(721            model.vocab, grammar._grammar.encode("utf-8"), grammar._root.encode("utf-8")722        )723        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)724 725    def add_grammar_lazy_patterns(726        self, 727        model: LlamaModel, 728        grammar: LlamaGrammar,729        trigger_patterns: List[str],730        trigger_tokens: List[int]731    ):732        # Convert patterns to C array733        pattern_ptrs = (ctypes.c_char_p * len(trigger_patterns))()734        for i, pattern in enumerate(trigger_patterns):735            pattern_ptrs[i] = pattern.encode("utf-8")736        737        # Convert tokens to C array738        token_array = (llama_cpp.llama_token * len(trigger_tokens))(*trigger_tokens)739        740        sampler = llama_cpp.llama_sampler_init_grammar_lazy_patterns(741            model.vocab,742            grammar._grammar.encode("utf-8"),743            grammar._root.encode("utf-8"),744            pattern_ptrs,745            len(trigger_patterns),746            token_array,747            len(trigger_tokens)748        )749        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)750 751    def add_penalties(752        self,753        penalty_last_n: int,754        penalty_repeat: float,755        penalty_freq: float,756        penalty_present: float,757    ):758        sampler = llama_cpp.llama_sampler_init_penalties(759            penalty_last_n,760            penalty_repeat,761            penalty_freq,762            penalty_present,763        )764        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)765 766    def add_dry(767        self,768        model: LlamaModel,769        n_ctx_train: int,770        dry_multiplier: float,771        dry_base: float,772        dry_allowed_length: int,773        dry_penalty_last_n: int,774        seq_breakers: List[str]775    ):776        # Convert seq_breakers to C array777        breaker_ptrs = (ctypes.c_char_p * len(seq_breakers))()778        for i, breaker in enumerate(seq_breakers):779            breaker_ptrs[i] = breaker.encode("utf-8")780        781        sampler = llama_cpp.llama_sampler_init_dry(782            model.vocab,783            n_ctx_train,784            dry_multiplier,785            dry_base,786            dry_allowed_length,787            dry_penalty_last_n,788            breaker_ptrs,789            len(seq_breakers)790        )791        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)792 793    def add_logit_bias(794        self, 795        n_vocab: int, 796        logit_bias: Dict[int, float]797    ):798        # Convert logit_bias dict to C array799        bias_array = (llama_cpp.llama_logit_bias * len(logit_bias))()800        for i, (token, bias) in enumerate(logit_bias.items()):801            bias_array[i].token = token802            bias_array[i].bias = bias803        804        sampler = llama_cpp.llama_sampler_init_logit_bias(805            n_vocab,806            len(logit_bias),807            bias_array808        )809        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)810 811    def add_infill(self, model: LlamaModel):812        sampler = llama_cpp.llama_sampler_init_infill(model.vocab)813        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)814 815    def add_custom(816        self, apply_func: Callable[[llama_cpp.llama_token_data_array], None]817    ):818        custom_sampler = CustomSampler(apply_func)819        sampler = custom_sampler.get_sampler()820        llama_cpp.llama_sampler_chain_add(self.sampler, sampler)821        # NOTE: Must remove custom samplers before free or llama.cpp will try to free them822        self.custom_samplers.append(823            (llama_cpp.llama_sampler_chain_n(self.sampler) - 1, custom_sampler)824        )825 826    def get_seed(self) -> int:827        return llama_cpp.llama_sampler_get_seed(self.sampler)828 829    def sample(self, ctx: LlamaContext, idx: int = -1) -> int:830        return llama_cpp.llama_sampler_sample(self.sampler, ctx.ctx, idx)831 832    def accept(self, token: int):833        llama_cpp.llama_sampler_accept(self.sampler, token)834 835    def reset(self):836        llama_cpp.llama_sampler_reset(self.sampler)837 838    def clone(self):839        # NOTE: Custom samplers cannot be cloned due to Python callback limitations840        if self.custom_samplers:841            raise NotImplementedError("Cannot clone LlamaSampler that contains custom samplers")842        843        cloned_sampler = llama_cpp.llama_sampler_clone(self.sampler)844        # Create a new wrapper around the cloned sampler845        new_sampler = LlamaSampler.__new__(LlamaSampler)846        new_sampler.sampler = cloned_sampler847        new_sampler.custom_samplers = []848        new_sampler._exit_stack = ExitStack()849        850        def free_sampler():851            if new_sampler.sampler is not None:852                llama_cpp.llama_sampler_free(new_sampler.sampler)853                new_sampler.sampler = None854 855        new_sampler._exit_stack.callback(free_sampler)856        return new_sampler857