Token-usage doesn't belong on the per-insight row — every consumer of
Towerops.LLM (insight enrichment today; billing-anomaly summaries,
monitoring narratives, fleet weekly reports tomorrow) needs to record
into one queryable place. Per-insight columns also made it impossible
to track LLM calls that aren't tied to an insight.
Schema: `llm_usage` keyed (organization_id, day, model, purpose) with a
unique index. organization_id is nullable for system-level analyses.
Each row carries running totals plus a request_count — callers UPSERT
and increment atomically.
API: `Towerops.LLM.Usage.record/1` for write, `summarize/2` for
read-side rollups. The worker now calls `Usage.record(%{... purpose:
"insight_enrichment"})` after each successful enrichment.
Removed: `llm_prompt_tokens` / `llm_completion_tokens` from
preseem_insights — same migration drops the columns and creates
llm_usage in one go. `apply_llm_enrichment` reverts to /3 (no usage
arg).
41 lines
1.5 KiB
Elixir
41 lines
1.5 KiB
Elixir
defmodule Towerops.Repo.Migrations.CreateLlmUsage do
|
|
use Ecto.Migration
|
|
|
|
def change do
|
|
create table(:llm_usage, primary_key: false) do
|
|
add :id, :binary_id, primary_key: true
|
|
# Nullable: future system-level analysis ("which orgs are
|
|
# at-risk this week?") may not be scoped to a single org.
|
|
add :organization_id,
|
|
references(:organizations, type: :binary_id, on_delete: :delete_all)
|
|
|
|
add :day, :date, null: false
|
|
add :model, :string, null: false
|
|
# "insight_enrichment", "alert_summary", "billing_anomaly", etc.
|
|
# Free-form so new callers don't need a schema change.
|
|
add :purpose, :string, null: false
|
|
|
|
add :prompt_tokens, :integer, null: false, default: 0
|
|
add :completion_tokens, :integer, null: false, default: 0
|
|
add :request_count, :integer, null: false, default: 0
|
|
|
|
timestamps(type: :utc_datetime)
|
|
end
|
|
|
|
# One row per (org, day, model, purpose) so callers can UPSERT and
|
|
# increment cheaply.
|
|
create unique_index(:llm_usage, [:organization_id, :day, :model, :purpose],
|
|
name: :llm_usage_org_day_model_purpose_idx,
|
|
nulls_distinct: false
|
|
)
|
|
|
|
create index(:llm_usage, [:day])
|
|
create index(:llm_usage, [:organization_id, :day])
|
|
|
|
# Drop the per-insight token columns — superseded by the new table.
|
|
alter table(:preseem_insights) do
|
|
remove :llm_prompt_tokens, :integer
|
|
remove :llm_completion_tokens, :integer
|
|
end
|
|
end
|
|
end
|