mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-20 02:17:24 +00:00
fix(usage): repair cost analytics and display precision (#1330)
fix(usage): repair cost analytics and display precision - Automatic OpenRouter pricing sync with cost backfill for traces/snapshots/events - Live usage data merging for current-hour dashboard accuracy - 2-decimal API cost formatting across usage/overview pages - Comprehensive test coverage (PG, SQLite, HTTP, UI) Merged by github-maintain automation.
This commit is contained in:
1 parent
fb4dadce8b
commit
747b58d24c
59 files changed
+3136
-197
No files matched your search
@@ -56,16 +56,17 @@ Streaming fallback is conservative: backup models are tried only if the stream f
|
||||
|
||||
## Usage Cap Pricing Enforcement
|
||||
|
||||
Standard edition can enforce AI budget caps before billable provider dispatch. API-key providers use OpenRouter `/models` pricing as the catalog source, with optional tenant/provider/model overrides in the dashboard.
|
||||
Standard edition can enforce AI budget caps before billable provider dispatch. API-key providers use OpenRouter `/models` pricing as the catalog source, with optional tenant/provider/model overrides in the dashboard. The gateway syncs the OpenRouter catalog automatically at startup and then once per day; the dashboard sync action remains available for manual refresh.
|
||||
|
||||
Excluded provider classes:
|
||||
- `chatgpt_oauth`, `claude_cli`, and `bailian` are treated as subscription/non-API pricing in round one.
|
||||
- `chatgpt_oauth`, `claude_cli`, and `bailian` are skipped for budget-cap enforcement in round one.
|
||||
- Tracing observability can still map Bailian/DashScope Qwen model IDs to the OpenRouter catalog for market-price dashboard reporting.
|
||||
- local/no-key subprocess providers such as `acp` and `ollama` are skipped unless a future feature explicitly enables pricing for them.
|
||||
|
||||
Runtime flow:
|
||||
1. Resolve the stored provider by name and skip non-billable provider classes.
|
||||
2. Load matching policies for tenant, agent, provider, provider type, and model.
|
||||
3. Resolve custom pricing override first, then OpenRouter catalog pricing when a matching policy has a cost ceiling. Native provider model IDs are mapped to OpenRouter prefixes for common providers such as OpenAI, Anthropic, and Gemini.
|
||||
3. Resolve custom pricing override first, then OpenRouter catalog pricing when a matching policy has a cost ceiling. Native provider model IDs are mapped to OpenRouter prefixes for common providers such as OpenAI, Anthropic, and Gemini. Tracing cost calculation uses the same override/catalog resolver, with legacy `telemetry.model_pricing` kept only as a fallback.
|
||||
4. Reserve estimated tokens and cost atomically before each dispatch attempt.
|
||||
5. Reconcile reserved counters after the provider returns usage or after a failed call.
|
||||
|
||||
|
||||
@@ -121,7 +121,13 @@ The exporter lives in a separate sub-package (`internal/tracing/otelexport/`) so
|
||||
|
||||
## 5. Cost Calculation
|
||||
|
||||
Per-span cost is calculated using the `CalculateCost()` function in `internal/tracing/cost.go`. For each LLM call span:
|
||||
Per-span cost is calculated in `internal/tracing/cost.go`. For each LLM call span, pricing resolves in this order:
|
||||
|
||||
1. Tenant/provider/model override
|
||||
2. OpenRouter-backed pricing catalog
|
||||
3. Legacy `telemetry.model_pricing` config fallback
|
||||
|
||||
Catalog and override prices are stored as USD per token/unit. Legacy config prices remain USD per million tokens:
|
||||
|
||||
```
|
||||
Cost = (PromptTokens × InputCostPerMillion) / 1,000,000
|
||||
@@ -130,9 +136,11 @@ Cost = (PromptTokens × InputCostPerMillion) / 1,000,000
|
||||
+ (CacheCreationTokens × CacheCreateCostPerMillion) / 1,000,000
|
||||
```
|
||||
|
||||
Model pricing is loaded from `config.ModelPricing` and keyed by `provider/model` (with fallback to `model` only). Cost is stored in the `total_cost` field of each LLM call span. The trace aggregation sums costs from all child `llm_call` spans to compute the trace-level `total_cost`.
|
||||
Cost is stored in the `total_cost` field of each LLM call span. The trace aggregation sums costs from all child `llm_call` spans to compute the trace-level `total_cost`.
|
||||
|
||||
Cache token costs (read + create) are optional and only applied if the pricing config specifies non-zero values.
|
||||
Cache token costs (read + create) are optional and only applied when the resolved catalog, override, or fallback config provides those price dimensions.
|
||||
|
||||
At startup, after the OpenRouter catalog sync succeeds, PostgreSQL tracing runs an idempotent historical backfill for LLM spans whose `total_cost` is still zero. The backfill uses the same override/catalog precedence, refreshes trace totals, and refreshes the affected `usage_snapshots` buckets so the Usage dashboard updates without waiting for new traffic. Bailian/DashScope Qwen model IDs and common unprefixed OpenAI-compatible model IDs are mapped to OpenRouter catalog IDs for observability pricing; usage cap enforcement rules remain separate.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ Current bundled core skills:
|
||||
| `skill-creator` | Meta-skill for creating new skills |
|
||||
| `workspace-organizing` | Workspace layout and file organization guidance |
|
||||
| `goclaw` | Gateway CLI/runtime administration and troubleshooting |
|
||||
| `lark-pm` | Lark/Feishu project-management workflows through GoClaw MCP tools |
|
||||
| `lark-playbook` | Troubleshooting playbook for failed Lark/Feishu MCP operations |
|
||||
|
||||
Shared helper modules live in `skills/_shared/` and are copied alongside each skill but not registered as standalone skills.
|
||||
|
||||
@@ -92,6 +94,10 @@ skills/
|
||||
│ └── SKILL.md
|
||||
├── workspace-organizing/
|
||||
│ └── SKILL.md
|
||||
├── lark-pm/
|
||||
│ └── SKILL.md
|
||||
├── lark-playbook/
|
||||
│ └── SKILL.md
|
||||
└── goclaw/
|
||||
└── SKILL.md
|
||||
```
|
||||
|
||||
@@ -4,6 +4,122 @@ Significant changes, features, and fixes in reverse chronological order.
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-03
|
||||
|
||||
### Usage cost display precision
|
||||
|
||||
**Fixes**
|
||||
|
||||
- Usage and overview cost values now render with two decimal places for API cost
|
||||
displays, matching dashboard expectations such as `$1.02` and `$5.93`.
|
||||
|
||||
**Tests**
|
||||
|
||||
- Added web formatter regression coverage for two-decimal API cost rendering.
|
||||
|
||||
### Usage analytics historical cost audit
|
||||
|
||||
**Fixes**
|
||||
|
||||
- Backfilled zero-cost historical `usage_snapshots` rows from the synced pricing
|
||||
catalog, fixing 7-day and 30-day totals that previously showed the same cost
|
||||
despite different token counts.
|
||||
- Usage analytics span aggregation now includes model-using `tool_call` spans in
|
||||
token and cost totals while keeping direct LLM call counts scoped to
|
||||
`llm_call` spans.
|
||||
- Added trace aggregate reconciliation so overview daily token/cost cards match
|
||||
the same `llm_call` + `tool_call` usage scope used by usage analytics.
|
||||
|
||||
**Tests**
|
||||
|
||||
- Added PostgreSQL and SQLite regressions for cost backfill and trace aggregate
|
||||
reconciliation with tool-call model usage.
|
||||
|
||||
### Live usage cost refresh
|
||||
|
||||
**Fixes**
|
||||
|
||||
- Usage summary and breakdown endpoints now include current-hour live trace/span
|
||||
data, so dashboard cost cards and top model costs update immediately after a
|
||||
completed LLM call instead of waiting for the hourly snapshot worker.
|
||||
|
||||
**Tests**
|
||||
|
||||
- Added HTTP regression coverage for current-hour summary cost and provider/model
|
||||
breakdown cost when no usage snapshot exists yet.
|
||||
|
||||
### Usage cost backfill after pricing sync
|
||||
|
||||
**Fixes**
|
||||
|
||||
- Added startup backfill for historical `llm_call` spans with zero cost after
|
||||
OpenRouter catalog sync succeeds.
|
||||
- Backfill now refreshes trace totals and affected usage snapshot buckets so
|
||||
dashboard cost numbers update for existing data.
|
||||
- Added catalog aliases for Bailian/DashScope Qwen models and common unprefixed
|
||||
OpenAI-compatible GPT/o-series model IDs.
|
||||
|
||||
**Tests**
|
||||
|
||||
- Added PG regression coverage for catalog-backed trace cost backfill.
|
||||
- Extended pricing resolver coverage for custom OpenAI-compatible and Bailian
|
||||
model aliases.
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-02
|
||||
|
||||
### Automatic model pricing for usage costs
|
||||
|
||||
**Fixes**
|
||||
|
||||
- Tracing cost now resolves tenant/provider/model pricing overrides first, then
|
||||
the OpenRouter catalog, before falling back to legacy telemetry config.
|
||||
- OpenRouter model pricing catalog now syncs automatically at gateway startup
|
||||
and once per day, while preserving the manual dashboard sync action.
|
||||
|
||||
**Tests**
|
||||
|
||||
- Added regressions for catalog-backed trace cost precedence, config fallback,
|
||||
and cached-token price calculation.
|
||||
|
||||
### Zalo Personal nested reply context
|
||||
|
||||
**Fixes**
|
||||
|
||||
- Preserved Zalo Personal `quote` payloads on inbound messages so agents receive
|
||||
quoted-message context when users reply to a reply.
|
||||
- Rendered quoted text and quoted attachment placeholders into DM and group
|
||||
prompts before mention gating/history flush.
|
||||
- Added a channel-agnostic bounded reply-context cache so Zalo Personal can
|
||||
render multi-level reply chains without DB persistence or Zalo API backfill.
|
||||
- Moved Zalo Personal policy checks before attachment download/media extraction
|
||||
so rejected messages cannot trigger media fetches or reply-cache writes.
|
||||
|
||||
**Tests**
|
||||
|
||||
- Added Zalo Personal protocol and handler regressions for quoted text, quoted
|
||||
attachments, and nested-style replies.
|
||||
- Added reply-context cache coverage for scoped lookup, multi-level chains,
|
||||
fallback, depth/size caps, cycle guard, TTL expiry, and clear-on-stop behavior.
|
||||
- Added a Zalo Personal regression proving denied attachment messages do not
|
||||
download media before policy rejection.
|
||||
|
||||
### Lark MCP skill guidance
|
||||
|
||||
**Added**
|
||||
|
||||
- Added bundled `lark-pm` and `lark-playbook` skills so GoClaw agents handle
|
||||
Lark/Feishu PM operations through MCP only.
|
||||
- Documented schema-first LarkBase writes, writable field checks, person-field
|
||||
payload shape, per-record updates, and canonical user ID verification.
|
||||
|
||||
**Docs**
|
||||
|
||||
- Updated the core skills system list to include the new Lark MCP skills.
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-01
|
||||
|
||||
### Usage event analytics schema gate
|
||||
|
||||
Reference in new issue
Block a user