Integrate pricing with canonical model (#6130)

This commit is contained in:
David Katz
2025-12-18 11:44:36 -05:00
committed by GitHub
parent 473f269daa
commit 6a0b8c25d5
17 changed files with 263 additions and 1034 deletions
+1 -16
View File
@@ -33,7 +33,6 @@ use goose::agents::extension::{Envs, ExtensionConfig, PLATFORM_EXTENSIONS};
use goose::agents::types::RetryConfig;
use goose::agents::{Agent, SessionConfig, COMPACT_TRIGGERS};
use goose::config::{Config, GooseMode};
use goose::providers::pricing::initialize_pricing_cache;
use goose::session::SessionManager;
use input::InputResult;
use rmcp::model::PromptMessage;
@@ -1416,19 +1415,6 @@ impl CliSession {
.get_goose_provider()
.unwrap_or_else(|_| "unknown".to_string());
// Do not get costing information if show cost is disabled
// This will prevent the API call to openrouter.ai
// This is useful if for cases where openrouter.ai may be blocked by corporate firewalls
if show_cost {
// Initialize pricing cache on startup
tracing::info!("Initializing pricing cache...");
if let Err(e) = initialize_pricing_cache().await {
tracing::warn!(
"Failed to initialize pricing cache: {e}. Pricing data may not be available."
);
}
}
match self.get_session().await {
Ok(metadata) => {
let total_tokens = metadata.total_tokens.unwrap_or(0) as usize;
@@ -1443,8 +1429,7 @@ impl CliSession {
&model_config.model_name,
input_tokens,
output_tokens,
)
.await;
);
}
}
Err(_) => {
+10 -56
View File
@@ -5,11 +5,9 @@ use goose::config::Config;
use goose::conversation::message::{
ActionRequiredData, Message, MessageContent, ToolRequest, ToolResponse,
};
use goose::providers::pricing::get_model_pricing;
use goose::providers::pricing::parse_model_id;
use goose::providers::canonical::maybe_get_canonical_model;
use goose::utils::safe_truncate;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use regex::Regex;
use rmcp::model::{CallToolRequestParam, JsonObject, PromptArgument};
use serde_json::Value;
use std::cell::RefCell;
@@ -795,69 +793,25 @@ pub fn display_context_usage(total_tokens: usize, context_limit: usize) {
);
}
fn normalize_model_name(model: &str) -> String {
let mut result = model.to_string();
// Remove "-latest" suffix
if result.ends_with("-latest") {
result = result.strip_suffix("-latest").unwrap().to_string();
}
// Remove date-like suffixes: -YYYYMMDD
let re_date = Regex::new(r"-\d{8}$").unwrap();
if re_date.is_match(&result) {
result = re_date.replace(&result, "").to_string();
}
// Convert version numbers like -3-7- to -3.7- (e.g., claude-3-7-sonnet -> claude-3.7-sonnet)
let re_version = Regex::new(r"-(\d+)-(\d+)-").unwrap();
if re_version.is_match(&result) {
result = re_version.replace(&result, "-$1.$2-").to_string();
}
result
}
async fn estimate_cost_usd(
fn estimate_cost_usd(
provider: &str,
model: &str,
input_tokens: usize,
output_tokens: usize,
) -> Option<f64> {
// For OpenRouter, parse the model name to extract real provider/model
let openrouter_data = if provider == "openrouter" {
parse_model_id(model)
} else {
None
};
let canonical_model = maybe_get_canonical_model(provider, model)?;
let (provider_to_use, model_to_use) = match &openrouter_data {
Some((real_provider, real_model)) => (real_provider.as_str(), real_model.as_str()),
None => (provider, model),
};
let input_cost_per_token = canonical_model.pricing.prompt?;
let output_cost_per_token = canonical_model.pricing.completion?;
// Use the pricing module's get_model_pricing which handles model name mapping internally
let cleaned_model = normalize_model_name(model_to_use);
let pricing_info = get_model_pricing(provider_to_use, &cleaned_model).await;
match pricing_info {
Some(pricing) => {
let input_cost = pricing.input_cost * input_tokens as f64;
let output_cost = pricing.output_cost * output_tokens as f64;
Some(input_cost + output_cost)
}
None => None,
}
let input_cost = input_cost_per_token * input_tokens as f64;
let output_cost = output_cost_per_token * output_tokens as f64;
Some(input_cost + output_cost)
}
/// Display cost information, if price data is available.
pub async fn display_cost_usage(
provider: &str,
model: &str,
input_tokens: usize,
output_tokens: usize,
) {
if let Some(cost) = estimate_cost_usd(provider, model, input_tokens, output_tokens).await {
pub fn display_cost_usage(provider: &str, model: &str, input_tokens: usize, output_tokens: usize) {
if let Some(cost) = estimate_cost_usd(provider, model, input_tokens, output_tokens) {
use console::style;
eprintln!(
"Cost: {} USD ({} tokens: in {}, out {})",