fn parse_bounded_usize(value: &str, minimum: usize, maximum: usize) -> Result<usize, String> {
let parsed = value
.parse::<usize>()
.map_err(|_| format!("{value:?} is not a positive integer"))?;
if (minimum..=maximum).contains(&parsed) {
Ok(parsed)
} else {
Err(format!("value must be between {minimum} and {maximum}"))
}
}
fn parse_max_response_bytes(value: &str) -> Result<usize, String> {
parse_bounded_usize(value, 4_096, 4_194_304)
}
fn parse_max_context_bytes(value: &str) -> Result<usize, String> {
parse_bounded_usize(value, 16_384, 1_048_576)
}
fn parse_max_context_tokens(value: &str) -> Result<usize, String> {
parse_bounded_usize(value, 2_048, 32_768)
}
fn parse_max_output_tokens(value: &str) -> Result<usize, String> {
parse_bounded_usize(value, 128, 8_192)
}
fn parse_runtime_context_tokens(value: &str) -> Result<usize, String> {
parse_bounded_usize(value, 2_048, 40_960)
}
fn parse_excerpt_bytes(value: &str) -> Result<usize, String> {
parse_bounded_usize(value, 512, 16_384)
}
fn parse_max_slices(value: &str) -> Result<usize, String> {
parse_bounded_usize(value, 1, 10)
}
#[derive(Debug, Args)]
#[command(group(
ArgGroup::new("selector")
.args(["path", "cluster", "relationship", "top"])
.multiple(false)
))]
struct AdviseArgs {
#[arg(long)]
report: Option<PathBuf>,
#[arg(long, conflicts_with = "validate_artifact")]
path: Option<String>,
#[arg(long, conflicts_with = "validate_artifact")]
relationship: Option<String>,
#[arg(long, conflicts_with = "validate_artifact")]
cluster: Option<String>,
#[arg(long, conflicts_with = "validate_artifact")]
top: Option<usize>,
#[arg(long = "policy", action = clap::ArgAction::Append, conflicts_with = "validate_artifact")]
policies: Vec<String>,
#[arg(long, conflicts_with = "validate_artifact")]
context_only: bool,
#[arg(long, hide = true, conflicts_with_all = ["context_only", "validate_artifact"])]
infer: bool,
#[arg(long, conflicts_with = "validate_artifact")]
ephemeral: bool,
#[arg(long, value_enum, default_value_t = crate::advice::EvaluationScenario::Unmodified, hide = true, conflicts_with = "validate_artifact")]
evaluation_scenario: crate::advice::EvaluationScenario,
#[arg(long)]
validate_artifact: Option<PathBuf>,
#[arg(long, value_enum, hide = true, requires = "infer", conflicts_with = "validate_artifact")]
provider: Option<crate::advice::ProviderKind>,
#[arg(long, hide = true, requires = "infer", conflicts_with = "validate_artifact")]
endpoint: Option<String>,
#[arg(long, hide = true, requires = "infer", conflicts_with = "validate_artifact")]
model: Option<String>,
#[arg(long, hide = true, requires = "infer", conflicts_with = "validate_artifact")]
runtime_model: Option<String>,
#[arg(long, hide = true, requires = "infer", conflicts_with = "validate_artifact")]
runtime_label: Option<String>,
#[arg(long, hide = true, requires = "infer", conflicts_with = "validate_artifact")]
model_digest: Option<String>,
#[arg(long, hide = true, requires = "infer", conflicts_with = "validate_artifact")]
model_size_bytes: Option<u64>,
#[arg(long, hide = true, requires = "infer", conflicts_with = "validate_artifact")]
estimated_peak_memory_bytes: Option<u64>,
#[arg(long, hide = true, requires = "infer", conflicts_with = "validate_artifact")]
confirm_resources: bool,
#[arg(long, value_enum, hide = true, default_value_t = crate::advice::ReasoningEffort::Medium, requires = "infer", conflicts_with = "validate_artifact")]
reasoning: crate::advice::ReasoningEffort,
#[arg(long, hide = true, default_value_t = 5, value_parser = clap::value_parser!(u64).range(1..=60), requires = "infer", conflicts_with = "validate_artifact")]
connect_timeout_seconds: u64,
#[arg(long, hide = true, default_value_t = 120, value_parser = clap::value_parser!(u64).range(1..=3600), requires = "infer", conflicts_with = "validate_artifact")]
timeout_seconds: u64,
#[arg(long, hide = true, default_value_t = 1_048_576, value_parser = parse_max_response_bytes, requires = "infer", conflicts_with = "validate_artifact")]
max_response_bytes: usize,
#[arg(long, hide = true, default_value_t = 2048, value_parser = parse_max_output_tokens, requires = "infer", conflicts_with = "validate_artifact")]
max_output_tokens: usize,
#[arg(long, hide = true, value_parser = parse_runtime_context_tokens, requires = "infer", conflicts_with = "validate_artifact")]
runtime_context_tokens: Option<usize>,
#[arg(long, default_value_t = 131_072, value_parser = parse_max_context_bytes, conflicts_with = "validate_artifact")]
max_context_bytes: usize,
#[arg(long, default_value_t = 8192, value_parser = parse_max_context_tokens, conflicts_with = "validate_artifact")]
max_context_tokens: usize,
#[arg(long, default_value_t = 4096, value_parser = parse_excerpt_bytes, conflicts_with = "validate_artifact")]
excerpt_bytes: usize,
#[arg(long, default_value_t = 3, value_parser = parse_max_slices, conflicts_with = "validate_artifact")]
max_slices: usize,
#[arg(long, hide = true, requires = "infer", conflicts_with = "validate_artifact")]
mock_response: Option<PathBuf>,
#[arg(long, value_enum)]
format: Option<AdviceFormat>,
#[arg(long)]
output: Option<PathBuf>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
enum AdviceFormat {
Markdown,
Json,
}