Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 20 additions & 1 deletion docs/image-input.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,25 @@ launch plus `--mmproj`: the Vision file replaces
`DeepSeek-V4-Flash-0731-ROCMFPX-MIX-STRIX.gguf` for text as well and decodes
at least as fast (numbers below). For R9700 + Strix Halo see [DS4V](#ds4v) below.

With an R9700 in the same box, run the image encoder there while the model
stays on the Strix Halo: expose both GPUs, point `--target-device` at the Strix
Halo and add `--mmproj-device` with the R9700 (on lucebox6, without
`HIP_VISIBLE_DEVICES`, that is `--target-device hip:1 --mmproj-device hip:0`).
The encoder then runs about twice as fast and streams each image into prefill
as soon as it is encoded, so the Strix Halo never waits for the next one:

| Images | Prompt tokens | Encoder on the Strix Halo | Encoder on the R9700 |
| --- | --- | --- | --- |
| 1 | 126 | 2.97 s | 2.94 s |
| 4 | 942 | 11.2 s | 9.1 s |
| 8 | 2,262 | 27.7 s | 19.2 s |
| 16 | 4,358 | 51.8 s | 34.6 s |

Time to the first token, with the published launch above and ChartQA charts.
Answers are identical in both layouts. `--mmproj-device` applies to this one-GPU
layout; with the experts split across both GPUs the encoder already runs on the
R9700.

### Send an image

```bash
Expand Down Expand Up @@ -101,7 +120,7 @@ Use `POST /v1/chat/completions` with user-message content parts in display order

Only base64 JPEG/PNG data URLs are supported. Remote URLs, images outside user
content arrays, and image parts through other API formats are rejected. A
request carries at most four images, 16 MiB encoded each and 32 MiB combined.
request carries at most 16 images, 16 MiB encoded each and 32 MiB combined.
Decoder pixel and aspect limits also apply. A model's image marker cannot be supplied
as ordinary text.

Expand Down
3 changes: 3 additions & 0 deletions server/src/common/backend_args.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ struct BackendArgs {

// Optional: vision projector .gguf (deepseek4 only)
std::optional<std::string> mmproj_path;
// Optional: GPU for the vision encoder when it should not share the
// target's (deepseek4, one-GPU layout only).
std::optional<DevicePlacement> mmproj_device;

// Device placement
DevicePlacement device;
Expand Down
1 change: 1 addition & 0 deletions server/src/common/backend_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ std::unique_ptr<ModelBackend> construct_backend(
DeepSeek4BackendConfig cfg;
cfg.model_path = model.path;
cfg.mmproj_path = model.mmproj_path.value_or("");
cfg.mmproj_gpu = model.mmproj_device ? model.mmproj_device->gpu : -1;
cfg.device = placement.target;
cfg.stream_fd = execution.stream_fd;
cfg.max_ctx = placement.target.max_ctx;
Expand Down
1 change: 1 addition & 0 deletions server/src/common/backend_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class BackendPlan final {
struct Model {
std::string path;
std::optional<std::string> mmproj_path;
std::optional<DevicePlacement> mmproj_device;
GgufModelInfo metadata;
};

Expand Down
1 change: 1 addition & 0 deletions server/src/common/backend_plan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ BackendPreparation BackendPlanBuilder::resolve(
BackendPlan plan;
plan.model_.path = std::move(args.model_path);
plan.model_.mmproj_path = std::move(args.mmproj_path);
plan.model_.mmproj_device = std::move(args.mmproj_device);
plan.model_.metadata = std::move(model);

plan.placement_.target = std::move(args.device);
Expand Down
8 changes: 8 additions & 0 deletions server/src/common/feature_gate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ std::string check_feature_compatibility(
return "--mmproj with DeepSeek4 requires a HIP backend";
}
}
if (args.mmproj_device.has_value()) {
if (!args.mmproj_path.has_value() || arch != "deepseek4" ||
args.mmproj_device->backend != PlacementBackend::Hip ||
args.mmproj_device->gpu == args.device.gpu) {
return "--mmproj-device needs --mmproj, a DeepSeek4 target, and a HIP GPU "
"other than the target's";
}
}

// ── PFlash enablement × drafter model
if (admission.pflash_enabled &&
Expand Down
5 changes: 5 additions & 0 deletions server/src/common/image_prompt.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
#pragma once

#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

namespace luce::common {

// Most images one request may carry, for every vision backend and the HTTP
// transport. Each image still has its own token and byte bounds.
inline constexpr size_t MAX_REQUEST_IMAGES = 16;

struct EncodedImage {
std::string mime_type;
std::vector<uint8_t> bytes;
Expand Down
Loading
Loading