Formatted src/application_options

This commit is contained in:
Aaron Po
2026-07-19 04:10:33 -04:00
parent 1f25d25dce
commit 03467cbb65

View File

@@ -10,207 +10,212 @@
std::optional<ApplicationOptions> ParseArguments( std::optional<ApplicationOptions> ParseArguments(
const int argc, char** argv, std::shared_ptr<ILogger> logger) { const int argc, char** argv, std::shared_ptr<ILogger> logger) {
prog_opts::options_description desc("Pipeline Options"); prog_opts::options_description desc("Pipeline Options");
auto opt = desc.add_options(); auto opt = desc.add_options();
opt("help,h", "Produce help message"); opt("help,h", "Produce help message");
auto add_sampling_options = [&]() -> void { auto add_sampling_options = [&]() -> void {
const SamplingOptions sampling_defaults{}; const SamplingOptions sampling_defaults{};
opt("temperature", opt("temperature",
prog_opts::value<float>()->default_value(sampling_defaults.temperature), prog_opts::value<float>()->default_value(
"Sampling temperature (higher = more random)"); sampling_defaults.temperature),
opt("top-p", "Sampling temperature (higher = more random)");
prog_opts::value<float>()->default_value(sampling_defaults.top_p), opt("top-p",
"Nucleus sampling top-p in (0,1] (higher = more random)"); prog_opts::value<float>()->default_value(sampling_defaults.top_p),
opt("top-k", "Nucleus sampling top-p in (0,1] (higher = more random)");
prog_opts::value<uint32_t>()->default_value(sampling_defaults.top_k), opt("top-k",
"Top-k sampling parameter (higher = more candidate tokens)"); prog_opts::value<uint32_t>()->default_value(sampling_defaults.top_k),
opt("n-ctx", "Top-k sampling parameter (higher = more candidate tokens)");
prog_opts::value<uint32_t>()->default_value(sampling_defaults.n_ctx), opt("n-ctx",
"Context window size in tokens"); prog_opts::value<uint32_t>()->default_value(sampling_defaults.n_ctx),
opt("seed", prog_opts::value<int>()->default_value(sampling_defaults.seed), "Context window size in tokens");
"Sampler seed: -1 for random, otherwise non-negative integer"); opt("seed",
opt("n-gpu-layers", prog_opts::value<int>()->default_value(0), prog_opts::value<int>()->default_value(sampling_defaults.seed),
"Number of layers to offload to GPU"); "Sampler seed: -1 for random, otherwise non-negative integer");
}; opt("n-gpu-layers", prog_opts::value<int>()->default_value(0),
"Number of layers to offload to GPU");
};
// --mocked and --model are mutually exclusive; validation is enforced below // --mocked and --model are mutually exclusive; validation is enforced below
// rather than at registration to produce a clear diagnostic message. // rather than at registration to produce a clear diagnostic message.
auto add_generator_options = [&]() -> void { auto add_generator_options = [&]() -> void {
opt("mocked", prog_opts::bool_switch(), opt("mocked", prog_opts::bool_switch(),
"Use mocked generator for brewery/user data"); "Use mocked generator for brewery/user data");
opt("model,m", prog_opts::value<std::string>()->default_value(""), opt("model,m", prog_opts::value<std::string>()->default_value(""),
"Path to LLM model (gguf)"); "Path to LLM model (gguf)");
}; };
auto add_pipeline_options = [&]() -> void { auto add_pipeline_options = [&]() -> void {
opt("output,o", prog_opts::value<std::string>()->default_value("output"), opt("output,o", prog_opts::value<std::string>()->default_value("output"),
"Directory for generated artifacts"); "Directory for generated artifacts");
opt("log-path", opt("log-path",
prog_opts::value<std::string>()->default_value("pipeline.log"), prog_opts::value<std::string>()->default_value("pipeline.log"),
"Path for application logs"); "Path for application logs");
opt("prompt-dir", prog_opts::value<std::string>()->default_value(""), opt("prompt-dir", prog_opts::value<std::string>()->default_value(""),
"Directory containing named prompt files (e.g. " "Directory containing named prompt files (e.g. "
"BREWERY_GENERATION.md)." "BREWERY_GENERATION.md)."
" Required when not using --mocked."); " Required when not using --mocked.");
opt("location-count", prog_opts::value<uint32_t>()->default_value(10)); opt("location-count", prog_opts::value<uint32_t>()->default_value(10));
}; };
add_sampling_options(); add_sampling_options();
add_generator_options(); add_generator_options();
add_pipeline_options(); add_pipeline_options();
// No flags provided — treat as a help request rather than an error. // No flags provided — treat as a help request rather than an error.
if (argc == 1) { if (argc == 1) {
const std::string title = "Biergarten Pipeline"; const std::string title = "Biergarten Pipeline";
const std::string usage = ([&] { const std::string usage = ([&] {
std::stringstream usage_stream; std::stringstream usage_stream;
usage_stream << "\nUsage: biergarten-pipeline [options]\n\n" << desc; usage_stream << "\nUsage: biergarten-pipeline [options]\n\n" << desc;
return usage_stream.str(); return usage_stream.str();
})(); })();
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Info,
.phase = PipelinePhase::Startup,
.message = title});
logger->Log(LogDTO{.level = LogLevel::Info,
.phase = PipelinePhase::Startup,
.message = usage});
}
return std::nullopt;
}
try {
prog_opts::variables_map var_map;
prog_opts::store(prog_opts::parse_command_line(argc, argv, desc), var_map);
prog_opts::notify(var_map);
if (var_map.contains("help")) {
std::stringstream help_stream;
help_stream << "\n" << desc;
if (logger) { if (logger) {
logger->Log(LogDTO{.level = LogLevel::Info, logger->Log(LogDTO{.level = LogLevel::Info,
.phase = PipelinePhase::Startup, .phase = PipelinePhase::Startup,
.message = help_stream.str()}); .message = title});
logger->Log(LogDTO{.level = LogLevel::Info,
.phase = PipelinePhase::Startup,
.message = usage});
} }
return std::nullopt; return std::nullopt;
} }
ApplicationOptions options; try {
prog_opts::variables_map var_map;
prog_opts::store(prog_opts::parse_command_line(argc, argv, desc),
var_map);
prog_opts::notify(var_map);
options.pipeline.output_path = var_map["output"].as<std::string>(); if (var_map.contains("help")) {
options.pipeline.log_path = var_map["log-path"].as<std::string>(); std::stringstream help_stream;
options.pipeline.prompt_dir = var_map["prompt-dir"].as<std::string>(); help_stream << "\n" << desc;
options.pipeline.location_count = var_map["location-count"].as<uint32_t>(); if (logger) {
logger->Log(LogDTO{.level = LogLevel::Info,
const bool use_mocked = var_map["mocked"].as<bool>(); .phase = PipelinePhase::Startup,
const std::string model_path = var_map["model"].as<std::string>(); .message = help_stream.str()});
const int n_gpu_layers = var_map["n-gpu-layers"].as<int>(); }
return std::nullopt;
// Enforce mutual exclusivity before any further configuration is
// applied.
if (use_mocked && !model_path.empty()) {
const std::string msg =
"Invalid arguments: --mocked and --model are mutually "
"exclusive";
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Error,
.phase = PipelinePhase::Startup,
.message = msg});
} else {
std::cerr << msg << std::endl;
} }
return std::nullopt;
}
if (!use_mocked && model_path.empty()) { ApplicationOptions options;
const std::string msg =
"Invalid arguments: either --mocked or --model must be " options.pipeline.output_path = var_map["output"].as<std::string>();
"specified"; options.pipeline.log_path = var_map["log-path"].as<std::string>();
if (logger) { options.pipeline.prompt_dir = var_map["prompt-dir"].as<std::string>();
logger->Log(LogDTO{.level = LogLevel::Error, options.pipeline.location_count =
.phase = PipelinePhase::Startup, var_map["location-count"].as<uint32_t>();
.message = msg});
} else { const bool use_mocked = var_map["mocked"].as<bool>();
std::cerr << msg << std::endl; const std::string model_path = var_map["model"].as<std::string>();
const int n_gpu_layers = var_map["n-gpu-layers"].as<int>();
// Enforce mutual exclusivity before any further configuration is
// applied.
if (use_mocked && !model_path.empty()) {
const std::string msg =
"Invalid arguments: --mocked and --model are mutually "
"exclusive";
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Error,
.phase = PipelinePhase::Startup,
.message = msg});
} else {
std::cerr << msg << std::endl;
}
return std::nullopt;
} }
return std::nullopt;
}
// Prompt directory is only meaningful for live inference — the mock if (!use_mocked && model_path.empty()) {
// generator has no use for it and should not require it to be present. const std::string msg =
if (!use_mocked && options.pipeline.prompt_dir.empty()) { "Invalid arguments: either --mocked or --model must be "
const std::string msg = "specified";
"Invalid arguments: --prompt-dir is required when not using " if (logger) {
"--mocked"; logger->Log(LogDTO{.level = LogLevel::Error,
if (logger) { .phase = PipelinePhase::Startup,
logger->Log({.level = LogLevel::Error, .message = msg});
.phase = PipelinePhase::Startup, } else {
.message = msg}); std::cerr << msg << std::endl;
} else { }
std::cerr << msg << std::endl; return std::nullopt;
} }
return std::nullopt;
}
options.generator.use_mocked = use_mocked; // Prompt directory is only meaningful for live inference — the mock
options.generator.model_path = model_path; // generator has no use for it and should not require it to be present.
if (!use_mocked && options.pipeline.prompt_dir.empty()) {
// Only populate sampling config when the user explicitly overrides at const std::string msg =
// least one value. Leaving it as std::nullopt lets LlamaGenerator fall "Invalid arguments: --prompt-dir is required when not using "
// back to its own SamplingOptions{} defaults, keeping the two paths "--mocked";
// consistent without redundant copies. if (logger) {
const bool user_provided_sampling = logger->Log({.level = LogLevel::Error,
!var_map["temperature"].defaulted() || !var_map["top-p"].defaulted() ||
!var_map["top-k"].defaulted() || !var_map["n-ctx"].defaulted() ||
!var_map["seed"].defaulted() || !var_map["n_gpu_layers"].defaulted();
if (user_provided_sampling) {
// Warn but do not fail — the run is still valid, the flags are just
// silently irrelevant when no model is loaded.
if (use_mocked) {
const std::string msg =
"Sampling parameters are ignored when using --mocked";
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Warn,
.phase = PipelinePhase::Startup,
.message = msg});
} else {
std::cerr << msg << std::endl;
}
} else {
SamplingOptions sampling;
sampling.temperature = var_map["temperature"].as<float>();
sampling.top_p = var_map["top-p"].as<float>();
sampling.top_k = var_map["top-k"].as<uint32_t>();
sampling.n_ctx = var_map["n-ctx"].as<uint32_t>();
sampling.seed = var_map["seed"].as<int>();
sampling.n_gpu_layers = var_map["n-gpu-layers"].as<int>();
options.generator.sampling = sampling;
}
}
return options;
} catch (const std::exception& exception) {
const std::string msg =
std::string("Failed to parse command-line arguments: ") +
exception.what();
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Error,
.phase = PipelinePhase::Startup, .phase = PipelinePhase::Startup,
.message = msg}); .message = msg});
} } else {
return std::nullopt; std::cerr << msg << std::endl;
} catch (...) { }
const std::string msg = return std::nullopt;
"Failed to parse command-line arguments: unknown error"; }
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Error, options.generator.use_mocked = use_mocked;
.phase = PipelinePhase::Startup, options.generator.model_path = model_path;
.message = msg});
} // Only populate sampling config when the user explicitly overrides at
return std::nullopt; // least one value. Leaving it as std::nullopt lets LlamaGenerator fall
} // back to its own SamplingOptions{} defaults, keeping the two paths
// consistent without redundant copies.
const bool user_provided_sampling =
!var_map["temperature"].defaulted() ||
!var_map["top-p"].defaulted() || !var_map["top-k"].defaulted() ||
!var_map["n-ctx"].defaulted() || !var_map["seed"].defaulted() ||
!var_map["n_gpu_layers"].defaulted();
if (user_provided_sampling) {
// Warn but do not fail — the run is still valid, the flags are just
// silently irrelevant when no model is loaded.
if (use_mocked) {
const std::string msg =
"Sampling parameters are ignored when using --mocked";
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Warn,
.phase = PipelinePhase::Startup,
.message = msg});
} else {
std::cerr << msg << std::endl;
}
} else {
SamplingOptions sampling;
sampling.temperature = var_map["temperature"].as<float>();
sampling.top_p = var_map["top-p"].as<float>();
sampling.top_k = var_map["top-k"].as<uint32_t>();
sampling.n_ctx = var_map["n-ctx"].as<uint32_t>();
sampling.seed = var_map["seed"].as<int>();
sampling.n_gpu_layers = var_map["n-gpu-layers"].as<int>();
options.generator.sampling = sampling;
}
}
return options;
} catch (const std::exception& exception) {
const std::string msg =
std::string("Failed to parse command-line arguments: ") +
exception.what();
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Error,
.phase = PipelinePhase::Startup,
.message = msg});
}
return std::nullopt;
} catch (...) {
const std::string msg =
"Failed to parse command-line arguments: unknown error";
if (logger) {
logger->Log(LogDTO{.level = LogLevel::Error,
.phase = PipelinePhase::Startup,
.message = msg});
}
return std::nullopt;
}
} }