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, 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, default_value_t = crate::advice::ProviderKind::OpenaiCompatible, conflicts_with = "validate_artifact")]
provider: crate::advice::ProviderKind,
#[arg(long, conflicts_with = "validate_artifact")]
endpoint: Option<String>,
#[arg(long, conflicts_with = "validate_artifact")]
model: Option<String>,
#[arg(long, conflicts_with = "validate_artifact")]
runtime_model: Option<String>,
#[arg(long, conflicts_with = "validate_artifact")]
runtime_label: Option<String>,
#[arg(long, conflicts_with = "validate_artifact")]
model_digest: Option<String>,
#[arg(long, conflicts_with = "validate_artifact")]
allow_remote: bool,
#[arg(long, value_enum, default_value_t = crate::advice::ReasoningEffort::Medium, conflicts_with = "validate_artifact")]
reasoning: crate::advice::ReasoningEffort,
#[arg(long, default_value_t = 120, value_parser = clap::value_parser!(u64).range(1..=3600), conflicts_with = "validate_artifact")]
timeout_seconds: u64,
#[arg(long, default_value_t = 1_048_576, value_parser = parse_max_response_bytes, conflicts_with = "validate_artifact")]
max_response_bytes: usize,
#[arg(long, default_value_t = 2048, value_parser = parse_max_output_tokens, conflicts_with = "validate_artifact")]
max_output_tokens: usize,
#[arg(long, value_parser = parse_runtime_context_tokens, 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, conflicts_with = "validate_artifact")]
mock_response: Option<PathBuf>,
#[arg(long, value_enum, default_value_t = AdviceFormat::Markdown)]
format: AdviceFormat,
#[arg(long)]
output: Option<PathBuf>,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
enum AdviceFormat {
Markdown,
Json,
}