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
16 changes: 14 additions & 2 deletions server/src/common/daemon_loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,14 +228,26 @@ int run_daemon(ModelBackend & backend, const DaemonLoopArgs & args) {
if (starts_with(line, "park")) {
std::string what;
if (line.size() > 5) what = line.substr(5);
backend.park(what);
const auto target = parse_park_target(what);
if (!target) {
std::fprintf(stderr, "[daemon] invalid park target: %s\n",
what.c_str());
} else {
backend.park(*target);
}
io.emit(-1);
continue;
}
if (starts_with(line, "unpark")) {
std::string what;
if (line.size() > 7) what = line.substr(7);
backend.unpark(what);
const auto target = parse_park_target(what);
if (!target) {
std::fprintf(stderr, "[daemon] invalid unpark target: %s\n",
what.c_str());
} else {
backend.unpark(*target);
}
io.emit(-1);
continue;
}
Expand Down
14 changes: 8 additions & 6 deletions server/src/common/layer_split_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@ void LayerSplitBackend::print_ready_banner() const {
std::fflush(stdout);
}

bool LayerSplitBackend::park(const std::string & what) {
std::fprintf(stderr, "[target-split] park is not supported yet (%s)\n",
what.c_str());
bool LayerSplitBackend::park(ParkTarget target) {
std::fprintf(stderr,
"[target-split] park is not supported yet (target=%s)\n",
park_target_name(target));
return false;
}

bool LayerSplitBackend::unpark(const std::string & what) {
std::fprintf(stderr, "[target-split] unpark is not supported yet (%s)\n",
what.c_str());
bool LayerSplitBackend::unpark(ParkTarget target) {
std::fprintf(stderr,
"[target-split] unpark is not supported yet (target=%s)\n",
park_target_name(target));
return false;
}

Expand Down
4 changes: 2 additions & 2 deletions server/src/common/layer_split_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ class LayerSplitBackend : public ModelBackend {
bool init();

void print_ready_banner() const override;
bool park(const std::string & what) override;
bool unpark(const std::string & what) override;
bool park(ParkTarget target) override;
bool unpark(ParkTarget target) override;
bool is_target_parked() const override { return false; }

GenerateResult generate_impl(const GenerateRequest & req,
Expand Down
55 changes: 52 additions & 3 deletions server/src/common/model_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,56 @@

namespace dflash::common {

enum class ParkTarget {
// NOTE: Empty preserves Qwen3's existing no-target park/unpark behavior.
Empty,
All,
TargetModel,
DraftModel,
};

struct ParkTargetMapping {
ParkTarget target;
std::string_view str_value;
};

inline constexpr ParkTargetMapping park_target_to_str_mappings[] = {
{ParkTarget::Empty, ""},
{ParkTarget::All, "all"},
{ParkTarget::TargetModel, "target"},
{ParkTarget::DraftModel, "draft"},
};

constexpr const char * park_target_name(ParkTarget target) {
for (const auto & mapping : park_target_to_str_mappings) {
if (mapping.target == target) {
return mapping.str_value.empty()
? "empty"
: mapping.str_value.data();
}
}
return "unknown";
}

constexpr std::optional<ParkTarget> parse_park_target(std::string_view value) {
for (const auto & mapping : park_target_to_str_mappings) {
if (mapping.str_value == value) return mapping.target;
}
return std::nullopt;
}

constexpr bool park_target_includes_target_model(ParkTarget target) {
return target == ParkTarget::Empty ||
target == ParkTarget::All ||
target == ParkTarget::TargetModel;
}

constexpr bool park_target_includes_draft_model(ParkTarget target) {
return target == ParkTarget::Empty ||
target == ParkTarget::All ||
target == ParkTarget::DraftModel;
}

// Token callback for streaming generation. Called once per committed token.
// Return true to continue generation, false to abort.
using TokenCallback = std::function<bool(int32_t token)>;
Expand Down Expand Up @@ -227,11 +277,10 @@ struct ModelBackend {
virtual void print_ready_banner() const = 0;

// ── Park / unpark ────────────────────────────────────────────────
// `what` is the tail of the command: "", "all", "target", "draft".
// Backend decides which resources to release/restore. Returns true on
// success; on failure prints to stderr and returns false.
virtual bool park(const std::string & what) = 0;
virtual bool unpark(const std::string & what) = 0;
virtual bool park(ParkTarget target) = 0;
virtual bool unpark(ParkTarget target) = 0;
virtual bool is_target_parked() const = 0;

// ── Generation ───────────────────────────────────────────────────
Expand Down
16 changes: 8 additions & 8 deletions server/src/deepseek4/deepseek4_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,16 +662,16 @@ void DeepSeek4Backend::print_ready_banner() const {
std::fflush(stdout);
}

bool DeepSeek4Backend::park(const std::string & what) {
const bool want_draft = (what.empty() || what == "all" || what == "draft");
const bool want_target = (what.empty() || what == "all" || what == "target");
bool DeepSeek4Backend::park(ParkTarget target) {
const bool want_draft = park_target_includes_draft_model(target);
const bool want_target_model = park_target_includes_target_model(target);

if (want_draft && spec_drafter_) {
release_spec_drafter(/*mark_parked=*/true);
std::printf("[deepseek4] DSpark drafter parked (VRAM released)\n");
std::fflush(stdout);
}
if (!want_target || parked_) return true;
if (!want_target_model || parked_) return true;

maybe_save_routing_stats();
for (int i = 0; i < PREFIX_SLOTS; ++i) {
Expand All @@ -694,11 +694,11 @@ bool DeepSeek4Backend::park(const std::string & what) {
return true;
}

bool DeepSeek4Backend::unpark(const std::string & what) {
const bool want_draft = (what.empty() || what == "all" || what == "draft");
const bool want_target = (what.empty() || what == "all" || what == "target");
bool DeepSeek4Backend::unpark(ParkTarget target) {
const bool want_draft = park_target_includes_draft_model(target);
const bool want_target_model = park_target_includes_target_model(target);

if (want_target && parked_) {
if (want_target_model && parked_) {
if (!load_model()) {
std::fprintf(stderr, "[deepseek4] unpark: failed to restore target model\n");
free_deepseek4_weights(w_);
Expand Down
4 changes: 2 additions & 2 deletions server/src/deepseek4/deepseek4_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class DeepSeek4Backend : public ModelBackend {
// ModelBackend interface
void print_ready_banner() const override;

bool park(const std::string & what) override;
bool unpark(const std::string & what) override;
bool park(ParkTarget target) override;
bool unpark(ParkTarget target) override;
bool is_target_parked() const override { return parked_; }

GenerateResult generate_impl(const GenerateRequest & req,
Expand Down
28 changes: 14 additions & 14 deletions server/src/gemma4/gemma4_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,16 +85,16 @@ void Gemma4Backend::print_ready_banner() const {

// ── Park / Unpark ──────────────────────────────────────────────────────

bool Gemma4Backend::park(const std::string & what) {
const bool want_draft = (what.empty() || what == "all" || what == "draft");
const bool want_target = (what.empty() || what == "all" || what == "target");
bool Gemma4Backend::park(ParkTarget target) {
const bool want_draft_model = park_target_includes_draft_model(target);
const bool want_target_model = park_target_includes_target_model(target);

if (want_draft && !draft_parked_) {
if (want_draft_model && !draft_parked_) {
free_decode_draft();
draft_parked_ = true;
std::printf("[gemma4] draft released\n"); std::fflush(stdout);
}
if (!want_target || parked_) return true;
if (!want_target_model || parked_) return true;

// Free snapshots first (they reference the snap_backend buffer)
for (int i = 0; i < PREFIX_SLOTS; ++i) {
Expand All @@ -112,13 +112,13 @@ bool Gemma4Backend::park(const std::string & what) {
return true;
}

bool Gemma4Backend::unpark(const std::string & what) {
const bool want_draft = (what.empty() || what == "all" || what == "draft");
const bool want_target = (what.empty() || what == "all" || what == "target");
bool Gemma4Backend::unpark(ParkTarget target) {
const bool want_draft_model = park_target_includes_draft_model(target);
const bool want_target_model = park_target_includes_target_model(target);

if (want_target && !parked_) {
if (want_target_model && !parked_) {
// target already resident
} else if (want_target && parked_) {
} else if (want_target_model && parked_) {
// Reload weights from disk
if (!load_gemma4_gguf(cfg_.model_path, backend_, w_)) {
std::fprintf(stderr, "[gemma4] unpark: failed to reload weights\n");
Expand All @@ -145,7 +145,7 @@ bool Gemma4Backend::unpark(const std::string & what) {
}
}

if (want_draft && draft_parked_ && cfg_.draft_path) {
if (want_draft_model && draft_parked_ && cfg_.draft_path) {
if (!load_decode_draft()) return false;
}
return true;
Expand Down Expand Up @@ -1186,7 +1186,7 @@ bool Gemma4Backend::handle_compress(const std::string & line,
// Park target to free VRAM for the drafter (unless skip_park).
const bool was_parked = parked_;
if (!skip_park && !parked_) {
park("target");
park(ParkTarget::TargetModel);
}

// Synchronize backend
Expand All @@ -1199,7 +1199,7 @@ bool Gemma4Backend::handle_compress(const std::string & line,
std::fprintf(stderr, "[compress] drafter init failed: %s\n",
dflash27b_last_error());
io.emit(-1);
if (!skip_park && !was_parked) unpark("target");
if (!skip_park && !was_parked) unpark(ParkTarget::TargetModel);
return false;
}
drafter_loaded_ = true;
Expand All @@ -1222,7 +1222,7 @@ bool Gemma4Backend::handle_compress(const std::string & line,

// Restore park state
if (!skip_park && !was_parked) {
unpark("target");
unpark(ParkTarget::TargetModel);
}

return ok;
Expand Down
4 changes: 2 additions & 2 deletions server/src/gemma4/gemma4_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ class Gemma4Backend : public ModelBackend {
// ModelBackend interface
void print_ready_banner() const override;

bool park(const std::string & what) override;
bool unpark(const std::string & what) override;
bool park(ParkTarget target) override;
bool unpark(ParkTarget target) override;
bool is_target_parked() const override { return parked_; }

GenerateResult generate_impl(const GenerateRequest & req,
Expand Down
24 changes: 12 additions & 12 deletions server/src/laguna/laguna_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,17 +356,17 @@ void LagunaBackend::print_ready_banner() const {

// ── Park / unpark ───────────────────────────────────────────────────────

bool LagunaBackend::park(const std::string & what) {
const bool want_draft = (what.empty() || what == "all" || what == "draft");
const bool want_target = (what.empty() || what == "all" || what == "target");
bool LagunaBackend::park(ParkTarget target) {
const bool want_draft_model = park_target_includes_draft_model(target);
const bool want_target_model = park_target_includes_target_model(target);

if (want_draft && !draft_parked_ && !args_.draft_path.empty()) {
if (want_draft_model && !draft_parked_ && !args_.draft_path.empty()) {
free_decode_draft();
draft_parked_ = true;
std::printf("[park] draft released\n"); std::fflush(stdout);
}

if (want_target && !target_parked_) {
if (want_target_model && !target_parked_) {
if (!draft_parked_ && !args_.draft_path.empty()) {
free_decode_draft();
draft_parked_ = true;
Expand All @@ -379,10 +379,10 @@ bool LagunaBackend::park(const std::string & what) {
return true;
}

bool LagunaBackend::unpark(const std::string & what) {
const bool want_draft = (what.empty() || what == "all" || what == "draft");
const bool want_target = (what.empty() || what == "all" || what == "target");
if (want_target && target_parked_) {
bool LagunaBackend::unpark(ParkTarget target) {
const bool want_draft_model = park_target_includes_draft_model(target);
const bool want_target_model = park_target_includes_target_model(target);
if (want_target_model && target_parked_) {
if (!args_.draft_path.empty()) {
if (!load_target_gguf_laguna(args_.target_path, backend_, w_)) {
std::fprintf(stderr, "[unpark] target: %s\n", dflash27b_last_error());
Expand Down Expand Up @@ -428,7 +428,7 @@ bool LagunaBackend::unpark(const std::string & what) {
target_parked_ = false;
std::printf("[unpark] target restored\n"); std::fflush(stdout);
}
if (want_draft && draft_parked_ && !args_.draft_path.empty()) {
if (want_draft_model && draft_parked_ && !args_.draft_path.empty()) {
if (!load_decode_draft()) return false;
}
return true;
Expand Down Expand Up @@ -1792,7 +1792,7 @@ bool LagunaBackend::handle_compress(const std::string & line,
}

const bool restore_target = !target_parked_;
if (restore_target) park("target");
if (restore_target) park(ParkTarget::TargetModel);

if (!drafter_loaded_) {
if (!load_drafter(drafter_path, /*gpu_layers=*/999, drafter_ctx_)) {
Expand All @@ -1813,7 +1813,7 @@ bool LagunaBackend::handle_compress(const std::string & line,
src_ids.size(), compressed.size(), keep);
std::fflush(stdout);

if (restore_target) unpark("target");
if (restore_target) unpark(ParkTarget::TargetModel);

for (int32_t t : compressed) io.emit(t);
io.emit(-1);
Expand Down
4 changes: 2 additions & 2 deletions server/src/laguna/laguna_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ class LagunaBackend : public ModelBackend {
// ── ModelBackend interface ────────────────────────────────────────
void print_ready_banner() const override;

bool park(const std::string & what) override;
bool unpark(const std::string & what) override;
bool park(ParkTarget target) override;
bool unpark(ParkTarget target) override;
bool is_target_parked() const override { return target_parked_; }
bool spark_wants_bootstrap() const override;
bool spark_bootstrap_finalize(const std::string & profile_path) override;
Expand Down
Loading
Loading