feat(cost): add budget tracking core and harden storage reliability (#292)
This commit is contained in:
parent
8882746ced
commit
e4944a5fc2
8 changed files with 890 additions and 2 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue