Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1024255
Fix rope_deltas state corruption in Qwen2.5VL during CFG generation
notkisk Jul 29, 2025
5604180
Override rope_deltas method in GLM4V to prevent inheritance
notkisk Jul 29, 2025
9cd0157
Fix rope_deltas corruption in Qwen2.5VL CFG generation and GLM4V inhe…
notkisk Jul 29, 2025
d02be86
Fix linting issues in GLM4V modular file
notkisk Jul 29, 2025
f4e8a83
Apply code formatting to GLM4V modular file
notkisk Jul 29, 2025
a0a9af6
Refactor Qwen2.5VL rope_deltas handling to use parameter-based approach
notkisk Jul 31, 2025
9a27648
Fix torch.compile compatibility by removing locals() check
notkisk Jul 31, 2025
e141e77
Fix rope_deltas corruption during CFG generation in Qwen2-VL and Qwen…
notkisk Aug 1, 2025
9e81bf0
Fix code formatting with ruff
notkisk Aug 1, 2025
88ead3f
Fix responses add tests (#39848)
LysandreJik Aug 1, 2025
7b4d984
Add fast image processor Janus, Deepseek VL, Deepseek VL hybrid (#39739)
yonigozlan Aug 1, 2025
1ec0fec
[image-processing] deprecate `plot_keypoint_matching`, make `visualiz…
sbucaille Aug 1, 2025
b727c2b
Allow `TrackioCallback` to work when pynvml is not installed (#39851)
qgallouedec Aug 1, 2025
6dfd561
remove dtensors, not explicit (#39840)
ArthurZucker Aug 1, 2025
166fcad
Improve `is_wandb_available` function to verify WandB installation (#…
qgallouedec Aug 4, 2025
4819adb
Refactor label name handling for PEFT models in Trainer class (#39265)
qgallouedec Aug 4, 2025
0d511f7
Use comment to build doc on PRs (#39846)
ydshieh Aug 4, 2025
2a9febd
Add support for including in-memory videos (not just files/urls) in a…
akibjawad Aug 4, 2025
831e0b0
Fix rope_deltas state corruption in Qwen2.5VL during CFG generation
notkisk Jul 29, 2025
5dc104a
Override rope_deltas method in GLM4V to prevent inheritance
notkisk Jul 29, 2025
1795d38
Fix rope_deltas corruption in Qwen2.5VL CFG generation and GLM4V inhe…
notkisk Jul 29, 2025
8f74b23
Fix linting issues in GLM4V modular file
notkisk Jul 29, 2025
6e00552
Apply code formatting to GLM4V modular file
notkisk Jul 29, 2025
26b9494
Refactor Qwen2.5VL rope_deltas handling to use parameter-based approach
notkisk Jul 31, 2025
b48cc3e
Fix torch.compile compatibility by removing locals() check
notkisk Jul 31, 2025
de141db
Fix rope_deltas corruption during CFG generation in Qwen2-VL and Qwen…
notkisk Aug 1, 2025
45ca565
Fix code formatting with ruff
notkisk Aug 1, 2025
a6406ab
Merge branch 'fix-qwen2-5-vl-rope-deltas-cfg' of https://github.com/n…
notkisk Aug 4, 2025
410aaaa
feat: added real cfg generation tests
notkisk Aug 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/pr_build_doc_with_comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: PR - build doc via comment
on:
issue_comment:
types:
- created
branches-ignore:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.event.issue.number }}-${{ startsWith(github.event.comment.body, 'build-doc') }}
cancel-in-progress: true
permissions: {}


jobs:
get-pr-number:
name: Get PR number
if: ${{ github.event.issue.state == 'open' && contains(fromJSON('["ydshieh", "ArthurZucker", "zucchini-nlp", "qubvel", "molbap", "gante", "LysandreJik", "Cyrilvallez", "Rocketknight1", "SunMarc", "muellerzr", "eustlb", "MekkCyber", "manueldeprada", "vasqu", "ivarflakstad", "stevhliu", "ebezzam"]'), github.actor) && (startsWith(github.event.comment.body, 'build-doc')) }}
uses: ./.github/workflows/get-pr-number.yml

get-pr-info:
name: Get PR commit SHA
needs: get-pr-number
if: ${{ needs.get-pr-number.outputs.PR_NUMBER != ''}}
uses: ./.github/workflows/get-pr-info.yml
with:
pr_number: ${{ needs.get-pr-number.outputs.PR_NUMBER }}

verity_pr_commit:
name: Verity PR commit corresponds to a specific event by comparing timestamps
if: ${{ needs.get-pr-number.outputs.PR_NUMBER != ''}}
runs-on: ubuntu-22.04
needs: get-pr-info
env:
COMMENT_DATE: ${{ github.event.comment.created_at }}
PR_MERGE_COMMIT_DATE: ${{ needs.get-pr-info.outputs.PR_MERGE_COMMIT_DATE }}
PR_MERGE_COMMIT_TIMESTAMP: ${{ needs.get-pr-info.outputs.PR_MERGE_COMMIT_TIMESTAMP }}
steps:
- run: |
COMMENT_TIMESTAMP=$(date -d "${COMMENT_DATE}" +"%s")
echo "COMMENT_DATE: $COMMENT_DATE"
echo "PR_MERGE_COMMIT_DATE: $PR_MERGE_COMMIT_DATE"
echo "COMMENT_TIMESTAMP: $COMMENT_TIMESTAMP"
echo "PR_MERGE_COMMIT_TIMESTAMP: $PR_MERGE_COMMIT_TIMESTAMP"
if [ $COMMENT_TIMESTAMP -le $PR_MERGE_COMMIT_TIMESTAMP ]; then
echo "Last commit on the pull request is newer than the issue comment triggering this run! Abort!";
exit -1;
fi

build-doc:
name: Build doc
needs: [get-pr-number, get-pr-info]
if: ${{ needs.get-pr-number.outputs.PR_NUMBER != '' }}
uses: huggingface/doc-builder/.github/workflows/build_pr_documentation.yml@main
with:
commit_sha: ${{ needs.get-pr-info.outputs.PR_HEAD_SHA }}
pr_number: ${{ needs.get-pr-number.outputs.PR_NUMBER }}
package: transformers
languages: ar de en es fr hi it ko pt tr zh ja te
22 changes: 0 additions & 22 deletions .github/workflows/pr_run_slow_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,6 @@ jobs:
with:
pr_number: ${{ needs.get-pr-number.outputs.PR_NUMBER }}

# We only need to verify the timestamp if the workflow is triggered by `issue_comment`.
verity_pr_commit:
name: Verity PR commit corresponds to a specific event by comparing timestamps
if: ${{ github.event.comment.created_at != '' }}
runs-on: ubuntu-22.04
needs: get-pr-info
env:
COMMENT_DATE: ${{ github.event.comment.created_at }}
PR_MERGE_COMMIT_DATE: ${{ needs.get-pr-info.outputs.PR_MERGE_COMMIT_DATE }}
PR_MERGE_COMMIT_TIMESTAMP: ${{ needs.get-pr-info.outputs.PR_MERGE_COMMIT_TIMESTAMP }}
steps:
- run: |
COMMENT_TIMESTAMP=$(date -d "${COMMENT_DATE}" +"%s")
echo "COMMENT_DATE: $COMMENT_DATE"
echo "PR_MERGE_COMMIT_DATE: $PR_MERGE_COMMIT_DATE"
echo "COMMENT_TIMESTAMP: $COMMENT_TIMESTAMP"
echo "PR_MERGE_COMMIT_TIMESTAMP: $PR_MERGE_COMMIT_TIMESTAMP"
if [ $COMMENT_TIMESTAMP -le $PR_MERGE_COMMIT_TIMESTAMP ]; then
echo "Last commit on the pull request is newer than the issue comment triggering this run! Abort!";
exit -1;
fi

get-jobs:
name: Get test files to run
runs-on: ubuntu-22.04
Expand Down
47 changes: 47 additions & 0 deletions docs/source/en/chat_templating_multimodal.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ Some vision models also support video inputs. The message format is very similar

- The content `"type"` should be `"video"` to indicate the content is a video.
- For videos, it can be a link to the video (`"url"`) or it could be a file path (`"path"`). Videos loaded from a URL can only be decoded with [PyAV](https://pyav.basswood-io.com/docs/stable/) or [Decord](https://github.com/dmlc/decord).
- In addition to loading videos from a URL or file path, you can also pass decoded video data directly. This is useful if you’ve already preprocessed or decoded video frames elsewhere in memory (e.g., using OpenCV, decord, or torchvision). You don't need to save to files or store it in an URL.

> [!WARNING]
> Loading a video from `"url"` is only supported by the PyAV or Decord backends.
Expand All @@ -137,6 +138,52 @@ messages = [
]
```

### Example: Passing decoded video objects
```python
import numpy as np

video_object1 = np.random.randint(0, 255, size=(16, 224, 224, 3), dtype=np.uint8),

messages = [
{
"role": "system",
"content": [{"type": "text", "text": "You are a friendly chatbot who always responds in the style of a pirate"}],
},
{
"role": "user",
"content": [
{"type": "video", "video": video_object1},
{"type": "text", "text": "What do you see in this video?"}
],
},
]
```
You can also use existing (`"load_video()"`) function to load a video, edit the video in memory and pass it in the messages.
```python

# Make sure a video backend library (pyav, decord, or torchvision) is available.
from transformers.video_utils import load_video

# load a video file in memory for testing
video_object2, _ = load_video(
"https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/720/Big_Buck_Bunny_720_10s_10MB.mp4"
)

messages = [
{
"role": "system",
"content": [{"type": "text", "text": "You are a friendly chatbot who always responds in the style of a pirate"}],
},
{
"role": "user",
"content": [
{"type": "video", "video": video_object2},
{"type": "text", "text": "What do you see in this video?"}
],
},
]
```

Pass `messages` to [`~ProcessorMixin.apply_chat_template`] to tokenize the input content. There are a few extra parameters to include in [`~ProcessorMixin.apply_chat_template`] that controls the sampling process.

The `video_load_backend` parameter refers to a specific framework to load a video. It supports [PyAV](https://pyav.basswood-io.com/docs/stable/), [Decord](https://github.com/dmlc/decord), [OpenCV](https://github.com/opencv/opencv), and [torchvision](https://pytorch.org/vision/stable/index.html).
Expand Down
4 changes: 4 additions & 0 deletions docs/source/en/model_doc/deepseek_vl.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ model = DeepseekVLForConditionalGeneration.from_pretrained(

[[autodoc]] DeepseekVLImageProcessor

## DeepseekVLImageProcessorFast

[[autodoc]] DeepseekVLImageProcessorFast

## DeepseekVLModel

[[autodoc]] DeepseekVLModel
Expand Down
4 changes: 4 additions & 0 deletions docs/source/en/model_doc/deepseek_vl_hybrid.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ model = DeepseekVLHybridForConditionalGeneration.from_pretrained(

[[autodoc]] DeepseekVLHybridImageProcessor

## DeepseekVLHybridImageProcessorFast

[[autodoc]] DeepseekVLHybridImageProcessorFast

## DeepseekVLHybridModel

[[autodoc]] DeepseekVLHybridModel
Expand Down
14 changes: 9 additions & 5 deletions docs/source/en/model_doc/janus.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ Here is the example of visual understanding with a single image.
> Note that the model has been trained with a specific prompt format for chatting. Use `processor.apply_chat_template(my_conversation_dict)` to correctly format your prompts.

```python
import torch
from PIL import Image
import requests
import torch
from PIL import Image
import requests

from transformers import JanusForConditionalGeneration, JanusProcessor
from transformers import JanusForConditionalGeneration, JanusProcessor

model_id = "deepseek-community/Janus-Pro-1B"
# Prepare Input for generation.
Expand All @@ -64,7 +64,7 @@ messages = [

# Set generation mode to `text` to perform text generation.
processor = JanusProcessor.from_pretrained(model_id)
model = JanusForConditionalGeneration.from_pretrained(model_id,
model = JanusForConditionalGeneration.from_pretrained(model_id,
torch_dtype=torch.bfloat16,
device_map="auto")

Expand Down Expand Up @@ -209,6 +209,10 @@ for i, image in enumerate(images['pixel_values']):

[[autodoc]] JanusImageProcessor

## JanusImageProcessorFast

[[autodoc]] JanusImageProcessorFast

## JanusVisionModel

[[autodoc]] JanusVisionModel
Expand Down
4 changes: 2 additions & 2 deletions docs/source/en/model_doc/lightglue.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ processed_outputs = processor.post_process_keypoint_matching(outputs, image_size

```py
# Easy visualization using the built-in plotting method
processor.plot_keypoint_matching(images, processed_outputs)
processor.visualize_keypoint_matching(images, processed_outputs)
```

<div class="flex justify-center">
Expand All @@ -128,7 +128,7 @@ processed_outputs = processor.post_process_keypoint_matching(outputs, image_size

- preprocess
- post_process_keypoint_matching
- plot_keypoint_matching
- visualize_keypoint_matching

<frameworkcontent>
<pt>
Expand Down
34 changes: 4 additions & 30 deletions docs/source/en/model_doc/superglue.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,38 +103,11 @@ processed_outputs = processor.post_process_keypoint_matching(outputs, image_size
print(f"Keypoint at {keypoint0.numpy()} matches with keypoint at {keypoint1.numpy()} with score {matching_score}")
```

- The example below demonstrates how to visualize matches between two images.
- Visualize the matches between the images using the built-in plotting functionality.

```py
import matplotlib.pyplot as plt
import numpy as np

# Create side by side image
merged_image = np.zeros((max(image1.height, image2.height), image1.width + image2.width, 3))
merged_image[: image1.height, : image1.width] = np.array(image1) / 255.0
merged_image[: image2.height, image1.width :] = np.array(image2) / 255.0
plt.imshow(merged_image)
plt.axis("off")

# Retrieve the keypoints and matches
output = processed_outputs[0]
keypoints0 = output["keypoints0"]
keypoints1 = output["keypoints1"]
matching_scores = output["matching_scores"]

# Plot the matches
for keypoint0, keypoint1, matching_score in zip(keypoints0, keypoints1, matching_scores):
plt.plot(
[keypoint0[0], keypoint1[0] + image1.width],
[keypoint0[1], keypoint1[1]],
color=plt.get_cmap("RdYlGn")(matching_score.item()),
alpha=0.9,
linewidth=0.5,
)
plt.scatter(keypoint0[0], keypoint0[1], c="black", s=2)
plt.scatter(keypoint1[0] + image1.width, keypoint1[1], c="black", s=2)

plt.savefig("matched_image.png", dpi=300, bbox_inches='tight')
# Easy visualization using the built-in plotting method
processor.visualize_keypoint_matching(images, processed_outputs)
```

<div class="flex justify-center">
Expand All @@ -155,6 +128,7 @@ processed_outputs = processor.post_process_keypoint_matching(outputs, image_size

- preprocess
- post_process_keypoint_matching
- visualize_keypoint_matching

<frameworkcontent>
<pt>
Expand Down
27 changes: 26 additions & 1 deletion src/transformers/commands/serving.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,9 @@ class ServeArguments:
log_level: str = field(
default="info", metadata={"help": "Logging level as a string. Example: 'info' or 'warning'."}
)
default_seed: Optional[int] = field(
default=None, metadata={"help": "The default seed for torch, should be an integer."}
)
enable_cors: bool = field(
default=False,
metadata={
Expand Down Expand Up @@ -451,6 +454,9 @@ def __init__(self, args: ServeArguments):
self.use_continuous_batching = self.args.attn_implementation == "sdpa_paged"
self.enable_cors = self.args.enable_cors

if self.args.default_seed is not None:
torch.manual_seed(self.args.default_seed)

# Set up logging
transformers_logger = logging.get_logger("transformers")
transformers_logger.setLevel(logging.log_levels[self.args.log_level.lower()])
Expand Down Expand Up @@ -1032,7 +1038,26 @@ def generate_response(self, req: dict) -> Generator[str, None, None]:
self.last_model = model_id_and_revision
model, processor = self.load_model_and_processor(model_id_and_revision)

inputs = processor.apply_chat_template(req["input"], add_generation_prompt=True).to(model.device)
if isinstance(req["input"], str):
inputs = [{"role": "system", "content": req["instructions"]}] if "instructions" in req else []
inputs.append({"role": "user", "content": req["input"]})
elif isinstance(req["input"], list):
if "instructions" in req:
if req["input"][0]["role"] != "system":
inputs = [{"role": "system", "content": req["instructions"]}, *req["input"]]
else:
inputs = req["input"]
inputs[0]["content"] = req["instructions"]
else:
inputs = req["input"]
elif isinstance(req["input"], dict):
inputs = [{"role": "system", "content": req["instructions"]}] if "instructions" in req else []
inputs.append(req["input"])
else:
raise ValueError("inputs should be a list, dict, or str")

inputs = processor.apply_chat_template(inputs, add_generation_prompt=True, return_tensors="pt")
inputs = inputs.to(model.device)
request_id = req.get("previous_response_id", "req_0")

generation_streamer = TextIteratorStreamer(processor, skip_special_tokens=True, skip_prompt=True)
Expand Down
25 changes: 22 additions & 3 deletions src/transformers/integrations/integration_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
import numpy as np
import packaging.version

from transformers.utils.import_utils import _is_package_available


if os.getenv("WANDB_MODE") == "offline":
print("⚙️ Running in WANDB offline mode")
Expand Down Expand Up @@ -109,7 +111,14 @@ def is_wandb_available():
"--report_to flag to control the integrations used for logging result (for instance --report_to none)."
)
return False
return importlib.util.find_spec("wandb") is not None
if importlib.util.find_spec("wandb") is not None:
import wandb

# wandb might still be detected by find_spec after an uninstall (leftover files or metadata), but not actually
# import correctly. To confirm it's fully installed and usable, we check for a key attribute like "run".
return hasattr(wandb, "run")
else:
return False


def is_trackio_available():
Expand Down Expand Up @@ -1043,6 +1052,14 @@ def on_predict(self, args, state, control, metrics, **kwargs):
class TrackioCallback(TrainerCallback):
"""
A [`TrainerCallback`] that logs metrics to Trackio.

It records training metrics, model (and PEFT) configuration, and GPU memory usage.
If `nvidia-ml-py` is installed, GPU power consumption is also tracked.

**Requires**:
```bash
pip install trackio
```
"""

def __init__(self):
Expand Down Expand Up @@ -1119,12 +1136,14 @@ def on_log(self, args, state, control, model=None, logs=None, **kwargs):
device_idx = torch.cuda.current_device()
total_memory = torch.cuda.get_device_properties(device_idx).total_memory
memory_allocated = torch.cuda.memory_allocated(device_idx)
power = torch.cuda.power_draw(device_idx)

gpu_memory_logs = {
f"gpu/{device_idx}/allocated_memory": memory_allocated / (1024**3), # GB
f"gpu/{device_idx}/memory_usage": memory_allocated / total_memory, # ratio
f"gpu/{device_idx}/power": power / 1000, # Watts
}
if _is_package_available("pynvml"):
power = torch.cuda.power_draw(device_idx)
gpu_memory_logs[f"gpu/{device_idx}/power"] = power / 1000 # Watts
if dist.is_available() and dist.is_initialized():
gathered_logs = [None] * dist.get_world_size()
dist.all_gather_object(gathered_logs, gpu_memory_logs)
Expand Down
Loading