diff --git a/cli/alora/train.py b/cli/alora/train.py index 722a20096..65581c0be 100644 --- a/cli/alora/train.py +++ b/cli/alora/train.py @@ -20,6 +20,7 @@ from transformers import ( AutoModelForCausalLM, AutoTokenizer, + PreTrainedTokenizerBase, TrainerCallback, TrainerControl, TrainerState, @@ -53,7 +54,7 @@ def load_dataset_from_json( - json_path: str, tokenizer: AutoTokenizer, invocation_prompt: str + json_path: str, tokenizer: PreTrainedTokenizerBase, invocation_prompt: str ) -> Dataset: """Load a JSONL dataset and format it for SFT training. @@ -165,8 +166,12 @@ def save_model(self, output_dir: str | None = None, _internal_call: bool = False """ if self.model is not None: self.model.save_pretrained(output_dir, safe_serialization=True) - if self.tokenizer is not None: - self.tokenizer.save_pretrained(output_dir) + # transformers v5 renamed .tokenizer -> .processing_class + processor = getattr(self, "processing_class", None) or getattr( + self, "tokenizer", None + ) + if processor is not None: + processor.save_pretrained(output_dir) def train_model( @@ -224,7 +229,8 @@ def train_model( base_model, padding_side="right", trust_remote_code=True ) tokenizer.pad_token = tokenizer.eos_token - tokenizer.add_special_tokens = False + # Note: previously had `tokenizer.add_special_tokens = False` here, which was + # a no-op (assigning False to a method reference). Removed — do not restore. dataset = load_dataset_from_json(dataset_path, tokenizer, invocation_prompt) dataset = dataset.shuffle(seed=42) diff --git a/docs/docs/advanced/prefix-caching-and-kv-blocks.md b/docs/docs/advanced/prefix-caching-and-kv-blocks.md index a69e53e44..751001e08 100644 --- a/docs/docs/advanced/prefix-caching-and-kv-blocks.md +++ b/docs/docs/advanced/prefix-caching-and-kv-blocks.md @@ -64,7 +64,7 @@ When a prompt contains a mix of cached and uncached blocks, Mellea: 2. Runs forward passes on uncached blocks. 3. Retrieves stored `DynamicCache` for cached blocks. 4. **Smashes** (concatenates) all KV caches along the time axis using - `merge_dynamic_caches()`. + `merge_dynamic_caches_v5()`. 5. Passes the merged cache plus the combined input IDs to the generation step. The result is identical to a single full-context forward pass, with the prefill diff --git a/docs/kv_smash/kv_with_chat.py b/docs/kv_smash/kv_with_chat.py index 7f43c5398..3e3d91527 100644 --- a/docs/kv_smash/kv_with_chat.py +++ b/docs/kv_smash/kv_with_chat.py @@ -1,7 +1,7 @@ import torch from mellea.backends.huggingface import LocalHFBackend -from mellea.backends.kv_block_helpers import DynamicCache, merge_dynamic_caches +from mellea.backends.kv_block_helpers import DynamicCache, merge_dynamic_caches_v5 from mellea.backends.model_ids import IBM_GRANITE_4_HYBRID_MICRO backend = LocalHFBackend(model_id=IBM_GRANITE_4_HYBRID_MICRO) @@ -30,7 +30,7 @@ def cache(s: str, store=True) -> DynamicCache: def merge(toks, dcs): merged_toks = torch.cat([t["input_ids"] for t in toks], dim=1) merged_masks = torch.cat([t["attention_mask"] for t in toks], dim=1) - merged_dcs = merge_dynamic_caches(dcs) + merged_dcs = merge_dynamic_caches_v5(dcs) return merged_toks, merged_masks, merged_dcs @@ -89,7 +89,7 @@ def merge(toks, dcs): # Merge everything together. merged_toks = torch.cat([toks["input_ids"] for toks in tok_parts], dim=1) merged_masks = torch.cat([toks["attention_mask"] for toks in tok_parts], dim=1) -merged_dcs = merge_dynamic_caches(dc_parts) +merged_dcs = merge_dynamic_caches_v5(dc_parts) # crop the last KV for safety. merged_dcs.crop(-1) diff --git a/docs/kv_smash/kvcache.py b/docs/kv_smash/kvcache.py index 83f226cf1..850bff7b5 100644 --- a/docs/kv_smash/kvcache.py +++ b/docs/kv_smash/kvcache.py @@ -4,17 +4,20 @@ # "mellea[hf]", # ] # /// +from typing import cast + import torch -from transformers import AutoModelForCausalLM, AutoTokenizer, PreTrainedTokenizer -from transformers.generation import GenerateDecoderOnlyOutput +from transformers import AutoModelForCausalLM, AutoTokenizer, PreTrainedTokenizerBase +from transformers.generation.utils import GenerateDecoderOnlyOutput +from transformers.modeling_utils import PreTrainedModel -from mellea.backends.kv_block_helpers import DynamicCache, merge_dynamic_caches +from mellea.backends.kv_block_helpers import DynamicCache, merge_dynamic_caches_v5 model_id = "ibm-granite/granite-4.0-tiny-preview" device = torch.device("mps") -model = AutoModelForCausalLM.from_pretrained(model_id) +model: PreTrainedModel = AutoModelForCausalLM.from_pretrained(model_id) # type: ignore[assignment] # model = model.to(device=device) # this part does not pass mypy; possible misconfiguration -tokenizer: PreTrainedTokenizer = AutoTokenizer.from_pretrained(model_id) +tokenizer: PreTrainedTokenizerBase = AutoTokenizer.from_pretrained(model_id) def cache(toks) -> DynamicCache: @@ -34,7 +37,7 @@ def merge(strs: list[str]): merged_toks = torch.cat([toks["input_ids"] for toks in strs_toks], dim=1) merged_masks = torch.cat([toks["attention_mask"] for toks in strs_toks], dim=1) - merged_dcs = merge_dynamic_caches(strs_dcs) + merged_dcs = merge_dynamic_caches_v5(strs_dcs) return merged_toks, merged_masks, merged_dcs @@ -45,7 +48,7 @@ def merge(strs: list[str]): merged_dcs.crop(-1) # GenerateDecoderOnlyOutput | GenerateEncoderDecoderOutput | GenerateBeamDecoderOnlyOutput | GenerateBeamEncoderDecoderOutput | LongTensor -result = model.generate( +result = model.generate( # type: ignore[operator] merged_toks.to(model.device), attention_mask=merged_masks.to(model.device), past_key_values=merged_dcs, diff --git a/docs/metrics/coverage-current.json b/docs/metrics/coverage-current.json index 80d7493e9..9c04e28a7 100644 --- a/docs/metrics/coverage-current.json +++ b/docs/metrics/coverage-current.json @@ -355,7 +355,7 @@ "TokenizedCacheIterleaving", "LegacyCache", "legacy_cache_smash", - "merge_dynamic_caches", + "merge_dynamic_caches_v5", "tokens_to_legacy_cache" ], "mellea.backends.huggingface.granite_formatters": [ diff --git a/mellea/backends/huggingface.py b/mellea/backends/huggingface.py index 4f14c36c3..ea28e1a77 100644 --- a/mellea/backends/huggingface.py +++ b/mellea/backends/huggingface.py @@ -12,7 +12,7 @@ import json import threading from collections.abc import Callable, Coroutine, Sequence -from typing import Any, overload +from typing import Any, cast, overload import jinja2 import jinja2.meta @@ -32,7 +32,7 @@ from transformers.generation.streamers import AsyncTextIteratorStreamer from transformers.generation.utils import GenerateDecoderOnlyOutput from transformers.modeling_utils import PreTrainedModel - from transformers.tokenization_utils import PreTrainedTokenizer + from transformers.tokenization_utils_base import PreTrainedTokenizerBase from transformers.trainer_utils import set_seed except ImportError as e: raise ImportError( @@ -125,7 +125,7 @@ def _install_cancel_stopping_criteria( Huggingface backends can initialize themselves from a model string if the transformers `Auto*` classes can be used. Therefore, a TransformersTorchConfig usually isn't required. However, sometimes a model needs special care to instantiate properly, or a custom device type needs to bse used. Instead of trying to do a lot of partial magic, we basically have two modaliites: either the constructor can figure out everything from the model_id, or the user has to provide an entire config. """ -TransformersTorchConfig = tuple[PreTrainedTokenizer, PreTrainedModel, torch.device] +TransformersTorchConfig = tuple[PreTrainedTokenizerBase, PreTrainedModel, torch.device] format: None = None # typing this variable in order to shadow the global format function and ensure mypy checks for errors @@ -213,16 +213,21 @@ def _cleanup_kv_cache(cache_info: HFAloraCacheInfo) -> None: # group) or silently replace a function injected by the Jinja environment (for the globals group). # # ⚠️ REVIEW NOTE: verify this set against the transformers source whenever upgrading. -# Named params: transformers.tokenization_utils_base.PreTrainedTokenizerBase.apply_chat_template -# (the render_jinja_template call around line 119 in that method) -# Jinja globals: transformers.utils.chat_template_utils._compile_jinja_template -# (the jinja_env.globals assignments near the bottom of that function) +# Namespace vars: transformers.utils.chat_template_utils.render_jinja_template +# (the compiled_template.render(...) call — every kwarg there becomes +# a Jinja variable and must be excluded from the allowlist) +# Jinja globals: transformers.utils.chat_template_utils._cached_compile_jinja_template +# (the jinja_env.globals[...] assignments near the bottom) _HF_INTERNAL_TEMPLATE_VARS: frozenset[str] = frozenset( { - # --- Named parameters passed explicitly to render_jinja_template --- - # Forwarding these from model_options would cause duplicate-kwarg TypeError. - "messages", # the conversation; always the first positional arg + # --- Variables injected into the Jinja namespace by render_jinja_template --- + # These come from `compiled_template.render(messages=chat, tools=..., documents=..., + # add_generation_prompt=..., **kwargs)` in transformers' chat_template_utils. + # Forwarding any of these from model_options to apply_chat_template would + # cause a duplicate-kwarg TypeError or silently shadow the value HF supplies. + "messages", # the conversation; HF binds `chat` to this name in render() "tools", # tool schemas; our call sites pass tools=convert_tools_to_json(...) explicitly + "documents", # RAG documents; injected even when None "add_generation_prompt", # bool; passed explicitly at the standard-generation call site # --- Jinja environment globals set by _compile_jinja_template --- # find_undeclared_variables cannot see env globals, so these appear as "undeclared" @@ -328,8 +333,8 @@ def __init__( self._model: PreTrainedModel = AutoModelForCausalLM.from_pretrained( self._hf_model_id, device_map=str(self._device), torch_dtype="auto" ) - self._tokenizer: PreTrainedTokenizer = AutoTokenizer.from_pretrained( - self._hf_model_id + self._tokenizer: PreTrainedTokenizerBase = ( + AutoTokenizer.from_pretrained(self._hf_model_id) ) case _: self._tokenizer, self._model, self._device = custom_config @@ -844,7 +849,7 @@ def _make_merged_kv_cache( [toks["attention_mask"] for toks in tok_parts], dim=1 ) assert input_ids.shape == attention_mask.shape - merged_cache: DynamicCache = kv_block_helpers.merge_dynamic_caches(dc_parts) + merged_cache: DynamicCache = kv_block_helpers.merge_dynamic_caches_v5(dc_parts) # TODO: also assert that the merged cached is the correct shape given the input_ids and attention_mask shapes. # rewind merged cache by 1 for safety. merged_cache.crop(-1) # type: ignore @@ -1127,7 +1132,8 @@ async def _generate_from_context_standard( "", # Empty for no adapters. self._model.generate, # type: ignore # Passed as args/kwargs to generate. - input_ids, + inputs=input_ids["input_ids"], + attention_mask=input_ids["attention_mask"], return_dict_in_generate=True, use_cache=self._use_caches, # Only create KV cache if caching is enabled **generate_kwargs, @@ -1207,6 +1213,10 @@ async def processing( input_ids: The prompt token IDs used for decoding; required to slice off the prompt portion from the generated sequences. """ + input_ids_tensor = ( + input_ids if isinstance(input_ids, torch.Tensor) else input_ids["input_ids"] + ) + if mot._underlying_value is None: mot._underlying_value = "" @@ -1217,8 +1227,12 @@ async def processing( elif isinstance(chunk, GenerateDecoderOnlyOutput): # Otherwise, it's a non-streaming request. Decode it here. mot._meta["hf_output"] = chunk - mot._underlying_value += self._tokenizer.decode( - chunk.sequences[0, input_ids.shape[1] :], skip_special_tokens=True + mot._underlying_value += cast( + str, + self._tokenizer.decode( + chunk.sequences[0, input_ids_tensor.shape[1] :], + skip_special_tokens=True, + ), ) async def post_processing( @@ -1272,7 +1286,11 @@ class used during generation, if any. kv_cache=kv_cache, merged_token_ids=output_complete, merged_attention=torch.ones_like(output_complete).to(self._device), - q_end=len(input_ids[0]), # type: ignore + q_end=( + input_ids + if isinstance(input_ids, torch.Tensor) + else input_ids["input_ids"] + ).shape[1], scores=hf_output.scores, ) @@ -1302,7 +1320,11 @@ class used during generation, if any. if isinstance(hf_output, GenerateDecoderOnlyOutput): try: if input_ids is not None and hf_output.sequences is not None: - n_prompt = input_ids.shape[1] + n_prompt = ( + input_ids + if isinstance(input_ids, torch.Tensor) + else input_ids["input_ids"] + ).shape[1] n_completion = hf_output.sequences[0].shape[0] - n_prompt except Exception: pass @@ -1772,14 +1794,11 @@ def load_adapter(self, adapter_qualified_name: str): ) try: - adapter_kwargs = {} - - # Peft tries to stringify the device. If it's mps, it gets stringified as "mps:0" which causes - # an error when loading with safetensors.torch.load_file. Force the device as a string "mps" to fix. - if self._device == torch.device("mps"): - adapter_kwargs["device"] = "mps" + # v5: adapter_kwargs is forwarded to download_kwargs only; device is + # derived automatically from self.device, so we don't pass it here — + # find_adapter_config_file() no longer accepts a 'device' argument. self._model.load_adapter( - adapter.path, adapter.qualified_name, adapter_kwargs=adapter_kwargs + adapter.path, adapter.qualified_name, adapter_kwargs={} ) except ValueError as e: # If it's just that it's already loaded, ignore it. diff --git a/mellea/backends/kv_block_helpers.py b/mellea/backends/kv_block_helpers.py index b3a8e5dd9..8b35c8cab 100644 --- a/mellea/backends/kv_block_helpers.py +++ b/mellea/backends/kv_block_helpers.py @@ -1,20 +1,21 @@ """Low-level utilities for concatenating transformer KV caches (KV smashing). -Provides functions for merging `DynamicCache` and legacy tuple caches along the -time axis (`merge_dynamic_caches`, `legacy_cache_smash`), and -`tokens_to_legacy_cache` for converting a tokenized prompt into a prefilled KV -cache. These helpers are used internally by local HuggingFace backends that reuse -cached prefix computations across multiple generation calls. +Provides ``prefill_cache_v5`` for converting a tokenized prompt into a prefilled +``DynamicCache``, ``merge_dynamic_caches_v5`` for concatenating multiple +``DynamicCache`` objects along the time axis, and ``merge_v5`` which composes +the two. These helpers are used internally by local HuggingFace backends +(transformers v5+) that reuse cached prefix computations across multiple +generation calls. """ from collections.abc import Iterable -from functools import reduce -from typing import Any +from typing import Any, cast try: import torch - from transformers import PreTrainedModel - from transformers.cache_utils import DynamicCache + from transformers import PreTrainedModel, PreTrainedTokenizerBase + from transformers.cache_utils import CacheLayerMixin, DynamicCache + from transformers.generation.utils import GenerateDecoderOnlyOutput from transformers.tokenization_utils_base import BatchEncoding except ImportError as e: raise ImportError( @@ -26,61 +27,110 @@ LegacyCache = Any -def legacy_cache_smash(a: LegacyCache, b: LegacyCache) -> LegacyCache: - """Concatenates two LegacyCache Ks and Vs along the time axis. - - Args: - a: First legacy KV cache (tuple of per-layer (K, V) tensor pairs). - b: Second legacy KV cache to concatenate after `a`. - - Returns: - New legacy cache with `b` appended to `a` along the sequence dimension. - """ - legacy_merged = tuple( - (torch.cat([a[i][0], b[i][0]], dim=2), torch.cat([a[i][1], b[i][1]], dim=2)) - for i in range(len(a)) +@torch.no_grad() +def prefill_cache_v5( + model: PreTrainedModel, + tokenizer: PreTrainedTokenizerBase, + text: str, + device: torch.device, +) -> tuple[dict, DynamicCache]: + """Prefills cache for transformers v5.""" + toks = tokenizer(text, return_tensors="pt") + toks = {k: v.to(device) for k, v in toks.items()} + + dc = DynamicCache() + out = model( + input_ids=toks["input_ids"], + attention_mask=toks["attention_mask"], + past_key_values=dc, + use_cache=True, + ) + dc = out.past_key_values + dc.crop(-1) + return toks, dc # v5 returns DynamicCache (not legacy tuple) + + +def merge_dynamic_caches_v5(caches: Iterable[DynamicCache]) -> DynamicCache: + """Merge multiple v5 DynamicCache objects by concatenating KV states along the time axis.""" + caches = list(caches) + if not caches: + raise ValueError("caches must be non-empty") + + for c in caches: + if any( + getattr(layer, "is_sliding", False) for layer in getattr(c, "layers", []) + ): + raise ValueError( + "KV cache smashing does not currently support sliding-window " + "attention layers (e.g. Granite 4 models). Disable prefix " + "caching for this model." + ) + + merged = DynamicCache() + + # reuse Cache.update() to append each segment's KV to the merged cache per layer. + # DynamicLayer.update(): self.keys = cat([self.keys, key_states], dim=-2). + for c in caches: + for layer_idx, layer in enumerate(c.layers): + if isinstance(layer, CacheLayerMixin): + if layer.keys is None or layer.values is None: + continue + merged.update(layer.keys, layer.values, layer_idx=layer_idx) + + return merged + + +def merge_v5( + model: PreTrainedModel, + tokenizer: PreTrainedTokenizerBase, + strs: list[str], + device: torch.device, +): + """Merges DynamicCache for transformers>=5.0.0.""" + strs_toks, strs_dcs = [], [] + for s in strs: + toks, dc = prefill_cache_v5(model, tokenizer, s, device) + strs_toks.append(toks) + strs_dcs.append(dc) + + merged_toks = torch.cat([t["input_ids"] for t in strs_toks], dim=1) + merged_masks = torch.cat([t["attention_mask"] for t in strs_toks], dim=1) + + merged_dc = merge_dynamic_caches_v5(strs_dcs) + + return merged_toks, merged_masks, merged_dc + + +if __name__ == "__main__": + from mellea.backends.huggingface import LocalHFBackend + from mellea.backends.model_ids import IBM_GRANITE_3_3_8B + + assert IBM_GRANITE_3_3_8B.hf_model_name is not None + backend = LocalHFBackend(model_id=IBM_GRANITE_3_3_8B.hf_model_name) + _model_raw, tokenizer, device = backend._model, backend._tokenizer, backend._device + model = cast(PreTrainedModel, _model_raw) + + docs = [ + "Nathan Fulton is expert in large language models, formal verification, and reinforcement learning. He holds a Ph.D. from Carnegie Mellon University's Computer Science Department and has worked at Amazon Web Services and IBM Research. He currently works at IBM Research - Cambridge.", + "IBM Research has a headquarters at 1101 Kitchawan Rd in Yorktown Heights and a Cambridge office at 314 Main Street in Cambridge, MA.", + "What is the address of Nathan's place of work?", + ] + + merged_tokens, merged_masks, merged_cache = merge_v5( + model, tokenizer, docs, device=backend._device + ) + input_ids = merged_tokens.to(device) + generate_out = cast( + GenerateDecoderOnlyOutput, + model.generate( # type: ignore[operator] + input_ids=input_ids, + use_cache=True, + return_dict_in_generate=True, + past_key_values=merged_cache, + max_new_tokens=512, + ), + ) + result = tokenizer.decode( + generate_out.sequences[0, input_ids.shape[1] :], skip_special_tokens=True ) - return legacy_merged - - -def merge_dynamic_caches(caches: Iterable[DynamicCache]) -> DynamicCache: - """Merges two DynamicCache Ks and Vs along the time axis. - - Args: - caches: Iterable of `DynamicCache` objects to merge in order. - - Returns: - A single `DynamicCache` with all caches concatenated along the sequence dimension. - """ - legacies = [c.to_legacy_cache() for c in caches] # type: ignore - assert len(legacies) >= 1 - rv = DynamicCache.from_legacy_cache(reduce(legacy_cache_smash, legacies)) # type: ignore - return rv # type: ignore - - -def tokens_to_legacy_cache( - model: PreTrainedModel, device: str, tokens_or_cache: BatchEncoding | DynamicCache -) -> Iterable[LegacyCache]: - """Prefills and returns Ks and Vs as a LegacyCache. - - Args: - model: The HuggingFace model used for prefill. - device: Target device string (e.g. `"cuda"`, `"cpu"`). - tokens_or_cache: Either a `BatchEncoding` to prefill, or an existing - `DynamicCache` to convert directly. - - Returns: - Legacy KV cache representation as a tuple of per-layer (K, V) tensor pairs. - """ - if type(tokens_or_cache) is DynamicCache: - return tokens_or_cache.to_legacy_cache() # type: ignore - else: - tokens = tokens_or_cache - dc = DynamicCache() - with torch.no_grad(): - dc = model( - tokens["input_ids"].to(device), # type: ignore - attention_mask=tokens["attention_mask"].to(device), # type: ignore - past_key_values=dc, - ).past_key_values - return dc.to_legacy_cache() + print(result) diff --git a/mellea/backends/openai.py b/mellea/backends/openai.py index 81b2864db..af5a7a074 100644 --- a/mellea/backends/openai.py +++ b/mellea/backends/openai.py @@ -6,7 +6,7 @@ import inspect import os from collections.abc import Coroutine, Sequence -from typing import TYPE_CHECKING, Any, overload +from typing import Any, overload import openai from openai.types.chat import ChatCompletion @@ -64,9 +64,6 @@ convert_tools_to_json, ) -if TYPE_CHECKING: - from transformers.tokenization_utils import PreTrainedTokenizer - openai_ollama_batching_error = "json: cannot unmarshal array into Go struct field CompletionRequest.prompt of type string" format: None = None # typing this variable in order to shadow the global format function and ensure mypy checks for errors diff --git a/mellea/formatters/granite/base/util.py b/mellea/formatters/granite/base/util.py index 60c8ca9fc..4ad210234 100644 --- a/mellea/formatters/granite/base/util.py +++ b/mellea/formatters/granite/base/util.py @@ -389,13 +389,11 @@ def generate_with_transformers( # Third Party import torch - # Input tokens must be passed to generate() as a positional argument, not a named - # argument. input_tokens = generate_input["input_tokens"] generate_input = generate_input.copy() del generate_input["input_tokens"] - generate_result = model.generate(input_tokens, **generate_input) # type: ignore[operator] + generate_result = model.generate(inputs=input_tokens, **generate_input) # type: ignore[operator] # Result is a a 2D tensor of shape (num responses, prompt + max generated tokens) # containing tokens, plus a tuple of tensors of shape @@ -462,6 +460,7 @@ def generate_with_transformers( all_logprobs[token_ix][response_tokens[token_ix]].item() for token_ix in range(len(response_tokens)) ] + assert isinstance(response_string, str) token_strings = [response_string[begin:end] for begin, end in token_offsets] token_bytes = [list(s.encode("utf-8")) for s in token_strings] @@ -474,8 +473,8 @@ def generate_with_transformers( torch.nan_to_num(all_logprobs, float("-inf")), other_input["top_logprobs"], ) - top_k_token_strs = [ - [tokenizer.decode(t) for t in row_i] for row_i in top_k_indices + top_k_token_strs: list[list[str]] = [ + [str(tokenizer.decode(t)) for t in row_i] for row_i in top_k_indices ] top_logprobs = [ [ diff --git a/pyproject.toml b/pyproject.toml index a4e74cf9b..76fcf334e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ hf = [ "datasets>=4.0.0", "llguidance", "peft>=0.18.1", # Native aLoRA support added in PEFT 0.18.0 - "transformers>=4.53.2,<5", + "transformers>5.5.0,<6.0.0", # 5.5.0 broke granite 4.0 "trl==0.19.1", "huggingface-hub>=0.33.4", ] diff --git a/test/backends/test_document_rendering_unit.py b/test/backends/test_document_rendering_unit.py index d1edaada8..87b49d815 100644 --- a/test/backends/test_document_rendering_unit.py +++ b/test/backends/test_document_rendering_unit.py @@ -253,15 +253,21 @@ async def test_watsonx_renders_documents_in_prompt(): async def test_huggingface_renders_documents_in_prompt(): """HuggingFace backend includes rendered documents in the chat template input.""" import torch + from transformers import BatchEncoding from transformers.generation.utils import GenerateDecoderOnlyOutput mock_tokenizer = MagicMock() mock_model = MagicMock() mock_device = torch.device("cpu") - # apply_chat_template returns input_ids tensor - fake_input_ids = torch.tensor([[1, 2, 3, 4, 5]]) - mock_tokenizer.apply_chat_template = MagicMock(return_value=fake_input_ids) + # apply_chat_template returns a BatchEncoding (v5 behavior with return_dict=True) + fake_batch = BatchEncoding( + { + "input_ids": torch.tensor([[1, 2, 3, 4, 5]]), + "attention_mask": torch.tensor([[1, 1, 1, 1, 1]]), + } + ) + mock_tokenizer.apply_chat_template = MagicMock(return_value=fake_batch) # model.generate returns a real GenerateDecoderOnlyOutput (not a MagicMock) # to avoid AsyncIterator detection in send_to_queue diff --git a/test/backends/test_huggingface.py b/test/backends/test_huggingface.py index c24213732..c0d452ded 100644 --- a/test/backends/test_huggingface.py +++ b/test/backends/test_huggingface.py @@ -376,19 +376,33 @@ async def test_generate_with_lock(backend) -> None: memoized: dict[torch.Tensor, str] = dict() # type: ignore[name-defined] gen_func = model.generate - def mock_func(input_ids, *args, **kwargs): + def _extract_inputs(inputs, args, kwargs): + # Callers in mellea/backends/huggingface.py use three different conventions + # (positional, inputs=, input_ids=); canonicalize to one tensor key. + if inputs is not None: + return inputs + if args: + return args[0] + return kwargs.get("input_ids", kwargs.get("inputs")) + + def mock_func(inputs=None, *args, **kwargs): """Mocks the generate function. Must call `populate_mocked_dict` with each input that must be cached before using this.""" + key_tensor = _extract_inputs(inputs, args, kwargs) for key, val in memoized.items(): - if torch.equal(key, input_ids): + if torch.equal(key, key_tensor): time.sleep(random.uniform(0.1, 0.5)) # Simulate a bit of work. return val assert False, "did not get a cached response" # Safely create the dict. - def populate_mocked_dict(input_ids, *args, **kwargs): + def populate_mocked_dict(inputs=None, *args, **kwargs): """Generates the model output and adds to the memoized dict.""" - output = gen_func(input_ids, *args, **kwargs) # type: ignore - memoized[input_ids] = output + key_tensor = _extract_inputs(inputs, args, kwargs) + if inputs is not None: + output = gen_func(inputs, *args, **kwargs) # type: ignore + else: + output = gen_func(*args, **kwargs) # type: ignore + memoized[key_tensor] = output return output model.generate = Mock(side_effect=populate_mocked_dict) diff --git a/test/backends/test_huggingface_filter_options.py b/test/backends/test_huggingface_filter_options.py index 1ad98698f..9d05882e1 100644 --- a/test/backends/test_huggingface_filter_options.py +++ b/test/backends/test_huggingface_filter_options.py @@ -47,9 +47,10 @@ class _FakeTokenizer: # --------------------------------------------------------------------------- _EXPECTED_INTERNAL_VARS = { - # Named parameters wired by apply_chat_template / render_jinja_template + # Variables bound in the Jinja namespace by render_jinja_template (transformers v5+) "messages", "tools", + "documents", "add_generation_prompt", # Jinja environment globals from _compile_jinja_template "raise_exception", diff --git a/uv.lock b/uv.lock index 835a2b276..94e382427 100644 --- a/uv.lock +++ b/uv.lock @@ -23,7 +23,8 @@ name = "accelerate" version = "1.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "numpy" }, { name = "packaging" }, { name = "psutil" }, @@ -701,7 +702,7 @@ wheels = [ [[package]] name = "click" -version = "8.3.3" +version = "8.4.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.15'", @@ -711,9 +712,9 @@ resolution-markers = [ dependencies = [ { name = "colorama", marker = "(python_full_version < '3.12' and sys_platform == 'win32') or (python_full_version >= '3.14' and sys_platform == 'win32')" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, ] [[package]] @@ -1025,7 +1026,8 @@ dependencies = [ { name = "filelock" }, { name = "fsspec", extra = ["http"] }, { name = "httpx" }, - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "multiprocess" }, { name = "numpy" }, { name = "packaging" }, @@ -1047,7 +1049,7 @@ version = "9.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, - { name = "click", version = "8.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "click", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "lxml" }, { name = "primp" }, ] @@ -1166,7 +1168,8 @@ dependencies = [ { name = "docling-parse" }, { name = "filetype" }, { name = "httpx" }, - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "lxml" }, { name = "marko" }, { name = "ocrmac", marker = "sys_platform == 'darwin'" }, @@ -1240,7 +1243,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "accelerate" }, { name = "docling-core" }, - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "jsonlines" }, { name = "numpy" }, { name = "pillow" }, @@ -1869,21 +1873,52 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "0.36.2" +version = "1.16.1" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.13.*'", + "python_full_version == '3.12.*'", +] dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, - { name = "packaging" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "tqdm" }, - { name = "typing-extensions" }, + { name = "filelock", marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "fsspec", marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "hf-xet", marker = "(python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'arm64') or (python_full_version >= '3.12' and python_full_version < '3.14' and platform_machine == 'x86_64')" }, + { name = "httpx", marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "packaging", marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "pyyaml", marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "tqdm", marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "typer", marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7c/b7/8cb61d2eece5fb05a83271da168186721c450eb74e3c31f7ef3169fa475b/huggingface_hub-0.36.2.tar.gz", hash = "sha256:1934304d2fb224f8afa3b87007d58501acfda9215b334eed53072dd5e815ff7a", size = 649782, upload-time = "2026-02-06T09:24:13.098Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/0f/ed994dbade67a54407c28cab96ef845e0e6d25500be56aca6394f8bfc9dd/huggingface_hub-1.16.1.tar.gz", hash = "sha256:7f1dc4c5ec21aed69be630ad0c3378616be16f3de1a47b141c0e812965d9c832", size = 792534, upload-time = "2026-05-21T18:40:00.908Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/af/48ac8483240de756d2438c380746e7130d1c6f75802ef22f3c6d49982787/huggingface_hub-0.36.2-py3-none-any.whl", hash = "sha256:48f0c8eac16145dfce371e9d2d7772854a4f591bcb56c9cf548accf531d54270", size = 566395, upload-time = "2026-02-06T09:24:11.133Z" }, + { url = "https://files.pythonhosted.org/packages/49/79/621a7dbb80c70974f73a597275351ebe03ce5bc65cb5f8f4acb5859252bc/huggingface_hub-1.16.1-py3-none-any.whl", hash = "sha256:64340de934b9ce37857ef85a82de72f5629e8a270f9119eabb12bf495eb53c22", size = 668176, upload-time = "2026-05-21T18:39:58.596Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "1.18.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.15'", + "python_full_version == '3.14.*'", + "python_full_version < '3.12'", +] +dependencies = [ + { name = "click", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "filelock", marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "fsspec", marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "hf-xet", marker = "(python_full_version < '3.12' and platform_machine == 'AMD64') or (python_full_version >= '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.12' and platform_machine == 'aarch64') or (python_full_version >= '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.12' and platform_machine == 'amd64') or (python_full_version >= '3.14' and platform_machine == 'amd64') or (python_full_version < '3.12' and platform_machine == 'arm64') or (python_full_version >= '3.14' and platform_machine == 'arm64') or (python_full_version < '3.12' and platform_machine == 'x86_64') or (python_full_version >= '3.14' and platform_machine == 'x86_64')" }, + { name = "httpx", marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "packaging", marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "pyyaml", marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "tqdm", marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "typer", marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "typing-extensions", marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/d8/748ea0a47f0fa15227fe682f7a80826b4b7c096e4818044b8f56d6cb66d6/huggingface_hub-1.18.0.tar.gz", hash = "sha256:f0c5ecd1ef8c6a60f86f61ee278f2c1570ba9e279c9f54de9094210723b3613b", size = 812699, upload-time = "2026-06-05T09:26:33.401Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/03/40a05316cb6616e5b7efd7773656441ab04b4b022c2199e79bb4622a92a3/huggingface_hub-1.18.0-py3-none-any.whl", hash = "sha256:729be4a976fb706dcc02d176bcda8a3f32bdf21a294e8f4b3dda6fbcbc9c1ab1", size = 684411, upload-time = "2026-06-05T09:26:31.48Z" }, ] [[package]] @@ -2809,7 +2844,7 @@ resolution-markers = [ ] dependencies = [ { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, - { name = "click", version = "8.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "click", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "fastuuid", marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "httpx", marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "importlib-metadata", version = "8.7.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, @@ -3228,7 +3263,8 @@ all = [ { name = "fastapi" }, { name = "grpcio" }, { name = "httpx" }, - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "ibm-watsonx-ai" }, { name = "langchain-core" }, { name = "litellm", version = "1.83.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, @@ -3256,7 +3292,8 @@ backends = [ { name = "accelerate" }, { name = "boto3" }, { name = "datasets" }, - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "ibm-watsonx-ai" }, { name = "litellm", version = "1.83.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "litellm", version = "1.83.14", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, @@ -3281,7 +3318,8 @@ granite-retriever = [ hf = [ { name = "accelerate" }, { name = "datasets" }, - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "llguidance" }, { name = "peft" }, { name = "transformers" }, @@ -3305,7 +3343,8 @@ server = [ { name = "uvicorn" }, ] switch = [ - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, ] telemetry = [ { name = "cpex" }, @@ -3437,7 +3476,7 @@ requires-dist = [ { name = "rouge-score" }, { name = "sentence-transformers", marker = "extra == 'granite-retriever'" }, { name = "smolagents", marker = "extra == 'tools'", specifier = ">=1.0.0" }, - { name = "transformers", marker = "extra == 'hf'", specifier = ">=4.53.2,<5" }, + { name = "transformers", marker = "extra == 'hf'", specifier = ">5.5.0,<6.0.0" }, { name = "trl", marker = "extra == 'hf'", specifier = "==0.19.1" }, { name = "typer", marker = "extra == 'cli'" }, { name = "uvicorn", marker = "extra == 'server'" }, @@ -3896,7 +3935,7 @@ version = "3.9.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, - { name = "click", version = "8.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "click", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "joblib" }, { name = "regex" }, { name = "tqdm" }, @@ -4177,7 +4216,7 @@ version = "1.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, - { name = "click", version = "8.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "click", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "pillow" }, { name = "pyobjc-framework-vision" }, ] @@ -4689,7 +4728,8 @@ version = "0.19.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "accelerate" }, - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "numpy" }, { name = "packaging" }, { name = "psutil" }, @@ -6543,7 +6583,8 @@ name = "sentence-transformers" version = "5.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "numpy" }, { name = "scikit-learn" }, { name = "scipy" }, @@ -6701,19 +6742,20 @@ wheels = [ [[package]] name = "smolagents" -version = "1.24.0" +version = "1.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "jinja2" }, { name = "pillow" }, { name = "python-dotenv" }, { name = "requests" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0a/1c/8d8e7f39f3586dc85a7f96681b08b7c1cfbd734b258a025f368632f2d666/smolagents-1.24.0.tar.gz", hash = "sha256:4d5028ffbd72aca85fb2e8daac52d03fb7d4290a9bf99dbc311dd65980f6b5de", size = 225246, upload-time = "2026-01-16T05:37:04.156Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/85/3ca67bb57743434ac321821ea54cbdbf0d3dcc8d55f37199709e83141340/smolagents-1.26.0.tar.gz", hash = "sha256:4ec92313265f9cfbcabfc88e192b4bc4505f8475dc5f33dc872062fc567037bd", size = 239034, upload-time = "2026-05-29T05:08:44.732Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/7a/461ead649c088d30f7847ef1c70f245a9c188381ed09a264633831050497/smolagents-1.24.0-py3-none-any.whl", hash = "sha256:54853759d07a92a939f1ff59238b757fb1a153204c90b69324c8e9709025aa86", size = 155727, upload-time = "2026-01-16T05:37:02.903Z" }, + { url = "https://files.pythonhosted.org/packages/89/8c/72bd5edc13288e3f27d4d9c1ef65adc0a68c950ee1fc6d3be270e1110f6c/smolagents-1.26.0-py3-none-any.whl", hash = "sha256:70e1cfb1576f782da93190ee31d9bb2659e5ca4bd84fda0c412e1f20498f28b6", size = 161466, upload-time = "2026-05-29T05:08:43.28Z" }, ] [[package]] @@ -6960,7 +7002,8 @@ name = "tokenizers" version = "0.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } wheels = [ @@ -7167,23 +7210,23 @@ wheels = [ [[package]] name = "transformers" -version = "4.57.6" +version = "5.10.2" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "filelock" }, - { name = "huggingface-hub" }, + { name = "huggingface-hub", version = "1.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, + { name = "huggingface-hub", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "numpy" }, { name = "packaging" }, { name = "pyyaml" }, { name = "regex" }, - { name = "requests" }, { name = "safetensors" }, { name = "tokenizers" }, { name = "tqdm" }, + { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/35/67252acc1b929dc88b6602e8c4a982e64f31e733b804c14bc24b47da35e6/transformers-4.57.6.tar.gz", hash = "sha256:55e44126ece9dc0a291521b7e5492b572e6ef2766338a610b9ab5afbb70689d3", size = 10134912, upload-time = "2026-01-16T10:38:39.284Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/38/d5f978bd5091019e89aef29b9a831f5cd70f2598963a3ead8b9570cab592/transformers-5.10.2.tar.gz", hash = "sha256:f9a44b9c8ca9ab1156b467f574d832ea066284299c2fd0ed84641ccb592751fc", size = 8799687, upload-time = "2026-06-04T18:43:49.119Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/b8/e484ef633af3887baeeb4b6ad12743363af7cce68ae51e938e00aaa0529d/transformers-4.57.6-py3-none-any.whl", hash = "sha256:4c9e9de11333ddfe5114bc872c9f370509198acf0b87a832a0ab9458e2bd0550", size = 11993498, upload-time = "2026-01-16T10:38:31.289Z" }, + { url = "https://files.pythonhosted.org/packages/73/6f/e1564b0cc182afa05e219a8e09a8e770ffaab879b6b824b56c819bd221da/transformers-5.10.2-py3-none-any.whl", hash = "sha256:8a669db546f82c7c3618cb46ceb0f0afd89292bc70f319c058f8332ec63e268d", size = 11003830, upload-time = "2026-06-04T18:43:45.303Z" }, ] [[package]] @@ -7334,7 +7377,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, - { name = "click", version = "8.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "click", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "rich" }, { name = "shellingham" }, ] @@ -7476,7 +7519,7 @@ version = "0.46.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and python_full_version < '3.14'" }, - { name = "click", version = "8.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, + { name = "click", version = "8.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12' or python_full_version >= '3.14'" }, { name = "h11" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1f/93/041fca8274050e40e6791f267d82e0e2e27dd165627bd640d3e0e378d877/uvicorn-0.46.0.tar.gz", hash = "sha256:fb9da0926999cc6cb22dc7cd71a94a632f078e6ae47ff683c5c420750fb7413d", size = 88758, upload-time = "2026-04-23T07:16:00.151Z" }