feat(stats): make ST4TS model price table operator-tunable
The hive-wide cost estimate on the dashboard's ST4TS tab used a
hard-coded model->price table in hive_stats.rs. Anthropic list pricing
drifts, so move the table to a nix option operators can keep current
without a code change.
- New `services.hyperhive.modelPrices` option: attrset of model-family
short name -> { input, output, cache_read, cache_write } USD per
million tokens. Passed to `hive-c0re serve --model-prices <json>`.
- hive_stats: `Prices` is now public + Deserialize; add `PriceTable`
type and `resolve_prices` (longest case-insensitive substring key
wins) with the old hard-coded table preserved as `builtin_prices`
fallback for any model not covered.
- Coordinator holds the parsed table (hive-c0re-local, not injected
into containers, so not part of HiveEnv); `/api/stats-hive` reads it.
- Docs: dashboard.md ST4TS cost note updated; option self-documents
via nixosOptionsDoc.
Closes #1434
This commit is contained in:
parent
f5d9f325c6
commit
cc8f58fb24
6 changed files with 172 additions and 23 deletions
|
|
@ -82,6 +82,18 @@ enum Cmd {
|
|||
/// Set via `services.hyperhive.agentMemoryMax`.
|
||||
#[arg(long, default_value = "4G")]
|
||||
agent_memory_max: String,
|
||||
/// Per-model USD prices (per million tokens) for the hive-wide
|
||||
/// ST4TS cost estimate, as a JSON object mapping a model-family
|
||||
/// short name to `{input, output, cache_read, cache_write}`.
|
||||
/// Keys are matched case-insensitively as a substring of the
|
||||
/// model id; models not covered fall back to the built-in
|
||||
/// estimate. Set via the `services.hyperhive.modelPrices` NixOS
|
||||
/// option.
|
||||
#[arg(
|
||||
long,
|
||||
default_value = r#"{"opus":{"input":15.0,"output":75.0,"cache_read":1.5,"cache_write":18.75},"sonnet":{"input":3.0,"output":15.0,"cache_read":0.3,"cache_write":3.75},"haiku":{"input":0.8,"output":4.0,"cache_read":0.08,"cache_write":1.0}}"#
|
||||
)]
|
||||
model_prices: String,
|
||||
},
|
||||
/// Spawn a new agent container directly (`hive-agent-<name>`). Bypasses
|
||||
/// the approval queue — use only as an operator on the host. For
|
||||
|
|
@ -152,6 +164,7 @@ async fn main() -> Result<()> {
|
|||
context_window_tokens,
|
||||
agent_cpu_quota,
|
||||
agent_memory_max,
|
||||
model_prices,
|
||||
} => {
|
||||
cmd_serve(
|
||||
hyperhive_flake,
|
||||
|
|
@ -163,6 +176,7 @@ async fn main() -> Result<()> {
|
|||
context_window_tokens,
|
||||
agent_cpu_quota,
|
||||
agent_memory_max,
|
||||
model_prices,
|
||||
&cli.socket,
|
||||
)
|
||||
.await
|
||||
|
|
@ -221,6 +235,7 @@ async fn cmd_serve(
|
|||
context_window_tokens: String,
|
||||
agent_cpu_quota: String,
|
||||
agent_memory_max: String,
|
||||
model_prices: String,
|
||||
socket: &std::path::Path,
|
||||
) -> Result<()> {
|
||||
// Move any host-side state still at the legacy flat layout into its
|
||||
|
|
@ -230,6 +245,8 @@ async fn cmd_serve(
|
|||
hive_c0re::paths::relocate_legacy_state();
|
||||
let cwt: std::collections::HashMap<String, u64> = serde_json::from_str(&context_window_tokens)
|
||||
.context("--context-window-tokens: invalid JSON")?;
|
||||
let prices: hive_c0re::hive_stats::PriceTable =
|
||||
serde_json::from_str(&model_prices).context("--model-prices: invalid JSON")?;
|
||||
let coord = Arc::new(Coordinator::open(
|
||||
&db,
|
||||
hyperhive_flake,
|
||||
|
|
@ -240,6 +257,7 @@ async fn cmd_serve(
|
|||
cwt,
|
||||
agent_cpu_quota,
|
||||
agent_memory_max,
|
||||
prices,
|
||||
)?);
|
||||
manager_server::start(coord.clone())?;
|
||||
// Idempotent pre-flight: rewrite pre-meta-layout applied
|
||||
|
|
|
|||
Loading…
Reference in a new issue