feat(cost): add budget tracking core and harden storage reliability (#292)

This commit is contained in:
Chummy 2026-02-16 23:40:47 +08:00 committed by GitHub
parent 8882746ced
commit e4944a5fc2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 890 additions and 2 deletions

View file

@ -2,7 +2,7 @@ pub mod schema;
#[allow(unused_imports)]
pub use schema::{
AuditConfig, AutonomyConfig, BrowserConfig, ChannelsConfig, ComposioConfig, Config,
AuditConfig, AutonomyConfig, BrowserConfig, ChannelsConfig, ComposioConfig, Config, CostConfig,
DelegateAgentConfig, DiscordConfig, DockerRuntimeConfig, GatewayConfig, HeartbeatConfig,
HttpRequestConfig, IMessageConfig, IdentityConfig, LarkConfig, MatrixConfig, MemoryConfig,
ModelRouteConfig, ObservabilityConfig, ReliabilityConfig, ResourceLimitsConfig, RuntimeConfig,

View file

@ -71,6 +71,9 @@ pub struct Config {
#[serde(default)]
pub identity: IdentityConfig,
#[serde(default)]
pub cost: CostConfig,
/// Hardware Abstraction Layer (HAL) configuration.
/// Controls how ZeroClaw interfaces with physical hardware
/// (GPIO, serial, debug probes).
@ -127,6 +130,147 @@ impl Default for IdentityConfig {
}
}
// ── Cost tracking and budget enforcement ───────────────────────────
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CostConfig {
/// Enable cost tracking (default: false)
#[serde(default)]
pub enabled: bool,
/// Daily spending limit in USD (default: 10.00)
#[serde(default = "default_daily_limit")]
pub daily_limit_usd: f64,
/// Monthly spending limit in USD (default: 100.00)
#[serde(default = "default_monthly_limit")]
pub monthly_limit_usd: f64,
/// Warn when spending reaches this percentage of limit (default: 80)
#[serde(default = "default_warn_percent")]
pub warn_at_percent: u8,
/// Allow requests to exceed budget with --override flag (default: false)
#[serde(default)]
pub allow_override: bool,
/// Per-model pricing (USD per 1M tokens)
#[serde(default)]
pub prices: std::collections::HashMap<String, ModelPricing>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelPricing {
/// Input price per 1M tokens
#[serde(default)]
pub input: f64,
/// Output price per 1M tokens
#[serde(default)]
pub output: f64,
}
fn default_daily_limit() -> f64 {
10.0
}
fn default_monthly_limit() -> f64 {
100.0
}
fn default_warn_percent() -> u8 {
80
}
impl Default for CostConfig {
fn default() -> Self {
Self {
enabled: false,
daily_limit_usd: default_daily_limit(),
monthly_limit_usd: default_monthly_limit(),
warn_at_percent: default_warn_percent(),
allow_override: false,
prices: get_default_pricing(),
}
}
}
/// Default pricing for popular models (USD per 1M tokens)
fn get_default_pricing() -> std::collections::HashMap<String, ModelPricing> {
let mut prices = std::collections::HashMap::new();
// Anthropic models
prices.insert(
"anthropic/claude-sonnet-4-20250514".into(),
ModelPricing {
input: 3.0,
output: 15.0,
},
);
prices.insert(
"anthropic/claude-opus-4-20250514".into(),
ModelPricing {
input: 15.0,
output: 75.0,
},
);
prices.insert(
"anthropic/claude-3.5-sonnet".into(),
ModelPricing {
input: 3.0,
output: 15.0,
},
);
prices.insert(
"anthropic/claude-3-haiku".into(),
ModelPricing {
input: 0.25,
output: 1.25,
},
);
// OpenAI models
prices.insert(
"openai/gpt-4o".into(),
ModelPricing {
input: 5.0,
output: 15.0,
},
);
prices.insert(
"openai/gpt-4o-mini".into(),
ModelPricing {
input: 0.15,
output: 0.60,
},
);
prices.insert(
"openai/o1-preview".into(),
ModelPricing {
input: 15.0,
output: 60.0,
},
);
// Google models
prices.insert(
"google/gemini-2.0-flash".into(),
ModelPricing {
input: 0.10,
output: 0.40,
},
);
prices.insert(
"google/gemini-1.5-pro".into(),
ModelPricing {
input: 1.25,
output: 5.0,
},
);
prices
}
// ── Agent delegation ─────────────────────────────────────────────
/// Configuration for a named delegate agent that can be invoked via the
@ -1200,6 +1344,7 @@ impl Default for Config {
browser: BrowserConfig::default(),
http_request: HttpRequestConfig::default(),
identity: IdentityConfig::default(),
cost: CostConfig::default(),
hardware: crate::hardware::HardwareConfig::default(),
agents: HashMap::new(),
security: SecurityConfig::default(),
@ -1556,6 +1701,7 @@ mod tests {
browser: BrowserConfig::default(),
http_request: HttpRequestConfig::default(),
identity: IdentityConfig::default(),
cost: CostConfig::default(),
hardware: crate::hardware::HardwareConfig::default(),
agents: HashMap::new(),
security: SecurityConfig::default(),
@ -1632,6 +1778,7 @@ default_temperature = 0.7
browser: BrowserConfig::default(),
http_request: HttpRequestConfig::default(),
identity: IdentityConfig::default(),
cost: CostConfig::default(),
hardware: crate::hardware::HardwareConfig::default(),
agents: HashMap::new(),
security: SecurityConfig::default(),