feat: show pricing per 1M tokens in model inspects
This commit is contained in:
parent
d8163174fd
commit
7425895931
2 changed files with 91 additions and 4 deletions
|
|
@ -249,11 +249,11 @@ async fn autocomplete_model_id<'a>(
|
||||||
#[poise::command(slash_command)]
|
#[poise::command(slash_command)]
|
||||||
async fn model(
|
async fn model(
|
||||||
ctx: Context<'_>,
|
ctx: Context<'_>,
|
||||||
#[description = "Exact model ID from /models or /status"]
|
#[description = "Exact model ID from /free-models or /status"]
|
||||||
#[autocomplete = "autocomplete_model_id"]
|
#[autocomplete = "autocomplete_model_id"]
|
||||||
id: String,
|
id: String,
|
||||||
#[description = "Hide the description (default false)"] compact: Option<bool>,
|
#[description = "Hide the description (default false)"] compact: Option<bool>,
|
||||||
#[description = "Include raw pricing (default true)"] show_pricing: Option<bool>,
|
#[description = "Include pricing per 1M tokens (default true)"] show_pricing: Option<bool>,
|
||||||
#[description = "Show details to everyone (default false)"] public: Option<bool>,
|
#[description = "Show details to everyone (default false)"] public: Option<bool>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let public = public.unwrap_or(false);
|
let public = public.unwrap_or(false);
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,56 @@ pub fn snapshot(state: &State, options: &Browse, poll_seconds: u64) -> serenity:
|
||||||
.field("Poll interval", format!("{formatted_poll} seconds · run /status again to refresh this snapshot"), false)
|
.field("Poll interval", format!("{formatted_poll} seconds · run /status again to refresh this snapshot"), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_price_usd(val: f64) -> String {
|
||||||
|
if val == 0.0 {
|
||||||
|
return "$0.00".into();
|
||||||
|
}
|
||||||
|
let formatted = format!("{val:.4}");
|
||||||
|
let trimmed = formatted.trim_end_matches('0');
|
||||||
|
let result = if trimmed.ends_with('.') {
|
||||||
|
format!("{trimmed}00")
|
||||||
|
} else {
|
||||||
|
let decimals = trimmed.split('.').nth(1).unwrap_or("").len();
|
||||||
|
if decimals < 2 {
|
||||||
|
format!("{trimmed}0")
|
||||||
|
} else {
|
||||||
|
trimmed.to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
format!("${result}")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_price_per_1m(key: &str, raw: &str) -> String {
|
||||||
|
if let Ok(price) = raw.parse::<f64>() {
|
||||||
|
if price < 0.0 {
|
||||||
|
return "Variable / unknown".into();
|
||||||
|
}
|
||||||
|
if key == "request" {
|
||||||
|
if price == 0.0 {
|
||||||
|
"$0 / request".into()
|
||||||
|
} else {
|
||||||
|
format!("${price:.4} / request")
|
||||||
|
}
|
||||||
|
} else if key == "image" {
|
||||||
|
if price == 0.0 {
|
||||||
|
"$0 / image".into()
|
||||||
|
} else {
|
||||||
|
format!("${price:.4} / image")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let per_1m = price * 1_000_000.0;
|
||||||
|
let formatted = format_price_usd(per_1m);
|
||||||
|
if per_1m == 0.0 {
|
||||||
|
format!("{formatted} / 1M tokens (Free)")
|
||||||
|
} else {
|
||||||
|
format!("{formatted} / 1M tokens")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
raw.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn model_details(model: &Model, compact: bool, show_pricing: bool) -> serenity::CreateEmbed {
|
pub fn model_details(model: &Model, compact: bool, show_pricing: bool) -> serenity::CreateEmbed {
|
||||||
let mut result = embed(&model.name)
|
let mut result = embed(&model.name)
|
||||||
.field("Model ID", text(&model.id, 1024), false)
|
.field("Model ID", text(&model.id, 1024), false)
|
||||||
|
|
@ -206,11 +256,17 @@ pub fn model_details(model: &Model, compact: bool, show_pricing: bool) -> sereni
|
||||||
.pricing
|
.pricing
|
||||||
.iter()
|
.iter()
|
||||||
.take(10)
|
.take(10)
|
||||||
.map(|(key, value)| format!("{}: {}", text(key, 40), text(value, 45)))
|
.map(|(key, value)| {
|
||||||
|
format!(
|
||||||
|
"{}: {}",
|
||||||
|
text(key, 40),
|
||||||
|
text(&format_price_per_1m(key, value), 45)
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n");
|
.join("\n");
|
||||||
result = result.field(
|
result = result.field(
|
||||||
"Raw USD prices (prompt/completion per token)",
|
"Pricing (USD per 1M tokens)",
|
||||||
if prices.is_empty() {
|
if prices.is_empty() {
|
||||||
"Unavailable"
|
"Unavailable"
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -411,4 +467,35 @@ pub mod tests {
|
||||||
assert_eq!(format_number(1000000), "1,000,000");
|
assert_eq!(format_number(1000000), "1,000,000");
|
||||||
assert_eq!(format_number(u64::MAX), "18,446,744,073,709,551,615");
|
assert_eq!(format_number(u64::MAX), "18,446,744,073,709,551,615");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_price_per_1m_cases() {
|
||||||
|
assert_eq!(
|
||||||
|
format_price_per_1m("prompt", "0"),
|
||||||
|
"$0.00 / 1M tokens (Free)"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
format_price_per_1m("prompt", "0.00000015"),
|
||||||
|
"$0.15 / 1M tokens"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
format_price_per_1m("completion", "0.0000015"),
|
||||||
|
"$1.50 / 1M tokens"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
format_price_per_1m("prompt", "0.000003"),
|
||||||
|
"$3.00 / 1M tokens"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
format_price_per_1m("prompt", "0.000000075"),
|
||||||
|
"$0.075 / 1M tokens"
|
||||||
|
);
|
||||||
|
assert_eq!(format_price_per_1m("request", "0"), "$0 / request");
|
||||||
|
assert_eq!(format_price_per_1m("image", "0.001"), "$0.0010 / image");
|
||||||
|
assert_eq!(format_price_per_1m("prompt", "-1"), "Variable / unknown");
|
||||||
|
assert_eq!(
|
||||||
|
format_price_per_1m("overrides", "tiered pricing"),
|
||||||
|
"tiered pricing"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue