diff --git a/NEWS.md b/NEWS.md index 78d59ea..4bed06a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,9 @@ ### Fixes +- **Past months' costs**: your AI assistant no longer reports past months as having no + cost data. + --- ## October 26, 2026 diff --git a/stacklet/mcp/assetdb/sql_info.md b/stacklet/mcp/assetdb/sql_info.md index 3a39370..c27ae77 100644 --- a/stacklet/mcp/assetdb/sql_info.md +++ b/stacklet/mcp/assetdb/sql_info.md @@ -21,12 +21,23 @@ Many AssetDB tables are extremely large. Always use LIMIT and indexed column fil | `cost_source` | Lookup table for cost sources | Very Small | Sources of ingested cost data | | `account_id` | Lookup table for accounts | Small | Bridge between cost tables and account/provider detail | | `resource_id` | Lookup table for resources | Medium | Bridge between cost tables and resource detail | +| `cloud_service` | Lookup table for services | Small | Bridge between cost tables and service/provider names | +| `cloud_region` | Lookup table for regions | Small | Bridge between cost tables and region names | ### **Special Considerations** - Most table columns are commented. When investigating table structure, these are valuable. - Foreign key constraints do not tell the whole story; there are many implicit foreign relations. +### **Cost Data** + +- Do not filter on `current` to pick a month; filter on `date`. `current` marks each account's (`account_cost_monthly`) or resource's (`resource_cost_monthly`) own latest month, not the latest calendar month, and a resource that has stopped costing has no current row. Rows are never superseded, so filtering `current = true` silently drops past months, and any account or resource whose latest data is older. +- A month's row may be partial while its data is still arriving: compare `source_date` or `last_date` with the month end. +- Filter on `account_id.provider`, through the cost row's `account`, not by matching service names: every cost table has `account`, and a provider's services need not contain its name. Some deployments have non-cloud providers (e.g. `anthropic`, in preview, whose services are model names); `SELECT DISTINCT provider FROM account_id` lists them. +- `account_cost_monthly.account`, `.service` and `.region` are integer ids; join them to `account_id`, `cloud_service` and `cloud_region` on `id`. `resource_cost_monthly` has only `account` and `resource_id`, with no service or region. +- `resource_focus_cost` and `resource_cost_monthly` have `tags`, an array of `resource_tags.id`s: unnest and join them to see whose cost it is, even for resources that aren't in `resources`. They are the tags the cost source reported, so for cloud resources they can be empty (AWS legacy CUR) or have `user_`-prefixed keys (AWS CUR 2.0: `team` is `user_team`); `resources._tags` has the inventory tags. Only `resource_focus_cost` has `sku`: join it to `sku.id` for `sku_id`, which encodes the pricing dimensions. +- Anthropic (preview): the cost row's `resource_id` joins `resource_id.id`, whose `r_id` is a user (`user_…`, Claude Code usage included), an API key (`apikey_…`) or a service account (untagged). Users are tagged `anthropic:user_email`/`anthropic:user_name` (or `anthropic:claude_code_user` for users who've left), keys `anthropic:api_key_name`/`anthropic:owner_*`. `sku_id` is `model:token_type:context_window:service_tier:inference_geo:speed` (e.g. `…:200k-1M:…` for long context), or `web_search`. Some cost (Console usage, code execution, small shares) has no principal, so is only in account-level cost; resource rows sum to less. + ### **Safe Querying Process** 1. **Query table sizes:**