openclaw-grafana-lens
Version:
OpenClaw plugin that gives AI agents full Grafana access — 18 composable tools for PromQL/LogQL/TraceQL queries, dashboard creation, alerting, SRE investigation, security monitoring, data collection pipeline management via Grafana Alloy (29 recipes), and
100 lines (99 loc) • 4.02 kB
JavaScript
/**
* Fallback model pricing for cost estimation.
*
* When openclaw's `estimateUsageCost()` returns undefined (because the user
* hasn't configured model pricing in openclaw.json), this module provides
* well-known pricing for popular models so cost dashboard panels still work.
*
* Pricing: USD per 1M tokens — matches openclaw's ModelCostConfig shape.
* Returns undefined for unknown models (no guessing).
*/
// Provider → model ID → pricing (USD per 1M tokens)
const KNOWN_MODEL_PRICING = {
anthropic: {
"claude-opus-4-6": { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
"claude-sonnet-4-6": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
"claude-haiku-4-5-20251001": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
// Older models still in use
"claude-sonnet-4-5-20250514": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
"claude-3-5-sonnet-20241022": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
"claude-3-5-haiku-20241022": { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
},
};
/**
* Estimate cost when openclaw's pricing config is missing.
* Formula matches openclaw's estimateUsageCost():
* (input × inputCost + output × outputCost + cacheRead × cacheReadCost + cacheWrite × cacheWriteCost) / 1_000_000
*
* Returns undefined for unknown provider/model combos.
*/
/**
* Resolve model pricing from openclaw's config — the same config that
* openclaw's own `resolveModelCostConfig()` reads. This covers ALL
* user-configured models, not just the 6 hardcoded Anthropic ones.
*
* Config path: `config.models.providers[provider].models[{id: model}].cost`
*
* Returns undefined if the provider/model isn't configured or has no cost entry.
*/
export function resolveModelCostFromConfig(config, provider, model) {
if (!provider || !model)
return undefined;
try {
const models = config.models;
if (!models)
return undefined;
const providers = models.providers;
if (!providers)
return undefined;
const providerEntry = providers[provider];
if (!providerEntry)
return undefined;
const modelList = providerEntry.models;
if (!Array.isArray(modelList))
return undefined;
const modelEntry = modelList.find((m) => m.id === model);
if (!modelEntry)
return undefined;
const cost = modelEntry.cost;
if (!cost)
return undefined;
// openclaw's ModelCostConfig uses per-million pricing
const input = typeof cost.input === "number" ? cost.input : undefined;
const output = typeof cost.output === "number" ? cost.output : undefined;
if (input === undefined || output === undefined)
return undefined;
return {
input,
output,
cacheRead: typeof cost.cacheRead === "number" ? cost.cacheRead : input * 0.1,
cacheWrite: typeof cost.cacheWrite === "number" ? cost.cacheWrite : input * 1.25,
};
}
catch {
return undefined;
}
}
/**
* Estimate cost from a resolved ModelCost and token usage.
* Same formula as openclaw's estimateUsageCost():
* (input × inputCost + output × outputCost + ...) / 1_000_000
*/
export function estimateUsageCost(pricing, usage) {
if (!usage)
return undefined;
const cost = ((usage.input ?? 0) * pricing.input +
(usage.output ?? 0) * pricing.output +
(usage.cacheRead ?? 0) * pricing.cacheRead +
(usage.cacheWrite ?? 0) * pricing.cacheWrite) /
1_000_000;
return cost > 0 ? cost : undefined;
}
export function estimateCostFallback(provider, model, usage) {
if (!provider || !model || !usage)
return undefined;
const pricing = KNOWN_MODEL_PRICING[provider]?.[model];
if (!pricing)
return undefined;
return estimateUsageCost(pricing, usage);
}