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
31 changes: 22 additions & 9 deletions app/server/runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1180,9 +1180,26 @@ std::unique_ptr<ServerState::LoadedModel> ServerState::make_model(ServerModelCon
engine::runtime::parse_run_mode(loaded->config.mode),
};
load_voice_presets(*loaded);
refresh_model_option_flags(*loaded);
return loaded;
}

void ServerState::refresh_model_option_flags(LoadedModel & model) {
const auto effective_override = model.config.model_spec_override.has_value()
? model.config.model_spec_override
: config_.model_spec_override;
// Deliberately uncaught: model_accepts_request_option already returns true for a
// model with no contract, swallowing only the missing-contract errors and
// rethrowing the rest. Anything that propagates here is therefore a real
// misconfiguration (invalid spec, missing override file, family mismatch) and
// must fail at registration rather than be assumed away.
model.accepts_reference_text = model_accepts_request_option(
model.config.family,
"reference_text",
effective_override,
model.config.path);
}

HttpResponse ServerState::handle_model_load(const std::string & body_text) {
if (!config_.ui_management) {
return error_response(403, "dynamic model management is disabled", "forbidden");
Expand Down Expand Up @@ -1221,6 +1238,7 @@ HttpResponse ServerState::handle_model_load(const std::string & body_text) {
engine::runtime::parse_run_mode(existing->config.mode),
};
load_voice_presets(*existing);
refresh_model_option_flags(*existing);
}
ensure_model_loaded_locked(*existing);
return json_response(
Expand Down Expand Up @@ -1869,15 +1887,10 @@ engine::runtime::TaskRequest ServerState::build_speech_request(const LoadedModel

bool voice_field_is_preset = false;
const auto * preset = select_voice_preset(model, body, voice_field_is_preset);
const auto effective_model_spec_override = model.config.model_spec_override.has_value()
? model.config.model_spec_override
: config_.model_spec_override;
const bool can_inject_reference_text =
model_accepts_request_option(
model.config.family,
"reference_text",
effective_model_spec_override,
model.config.path);
// Resolved once at registration (refresh_model_option_flags): calling
// model_accepts_request_option per request re-reads the model file's embedded
// spec on the request thread, which cost ~0.9 s per request for large GGUFs.
const bool can_inject_reference_text = model.accepts_reference_text;

engine::runtime::VoiceCondition voice;
bool has_voice = false;
Expand Down
9 changes: 9 additions & 0 deletions app/server/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ class ServerState final : public IHttpHandler {
mutable std::shared_mutex metadata_mutex;
std::unordered_map<std::string, RuntimeVoicePreset> voice_presets;
std::optional<RuntimeVoicePreset> default_voice_preset;
// Whether this model's contract accepts the `reference_text` request
// option, resolved once at registration (refresh_model_option_flags).
// Resolving it per request re-reads the model file's embedded spec on
// the request thread, which costs ~0.9 s per request for large GGUFs.
// `true` mirrors model_accepts_request_option's no-contract behavior.
bool accepts_reference_text = true;
// Serializes runs on this model and bounds how long a caller waits for its
// turn; see BusyGuard.
BusyGuard busy;
Expand All @@ -85,6 +91,9 @@ class ServerState final : public IHttpHandler {

void load_models();
std::unique_ptr<LoadedModel> make_model(ServerModelConfig config);
// Recompute the per-model, config-derived request-option flags (currently
// accepts_reference_text). Called at registration and on reconfiguration.
void refresh_model_option_flags(LoadedModel & model);
std::filesystem::path resolve_ui_model_path(const std::filesystem::path & path) const;
HttpResponse handle_model_load(const std::string & body_text);
HttpResponse handle_model_unload(const std::string & body_text);
Expand Down
Loading