- Refactored AI gateway to utilize new cost management structures for usage tracking. - Replaced deprecated token extraction methods with a unified cost parsing approach. - Enhanced usage fallback mechanisms and introduced detailed usage metrics in responses. - Added new metering functionality to record AI usage and costs effectively. - Updated tests to reflect changes in usage parsing and cost calculations. - Introduced new API endpoints for retrieving AI usage summaries and pricing information.
97 lines
4.5 KiB
SQL
97 lines
4.5 KiB
SQL
-- Model pricing (micro-EUR per 1M tokens)
|
|
CREATE TABLE IF NOT EXISTS ai_model_pricing (
|
|
model_id TEXT NOT NULL,
|
|
provider_type TEXT NOT NULL DEFAULT 'generic',
|
|
input_micro_eur_per_mtok BIGINT NOT NULL,
|
|
cached_input_micro_eur_per_mtok BIGINT,
|
|
output_micro_eur_per_mtok BIGINT NOT NULL,
|
|
reasoning_micro_eur_per_mtok BIGINT,
|
|
effective_from DATE NOT NULL DEFAULT CURRENT_DATE,
|
|
source TEXT NOT NULL DEFAULT 'manual',
|
|
PRIMARY KEY (model_id, effective_from)
|
|
);
|
|
|
|
-- Detailed usage ledger
|
|
CREATE TABLE IF NOT EXISTS ai_usage_events (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
feature TEXT NOT NULL,
|
|
model_id TEXT NOT NULL,
|
|
provider_id TEXT NOT NULL,
|
|
billing_scope TEXT NOT NULL CHECK (billing_scope IN ('org', 'user')),
|
|
provider_key_fingerprint TEXT NOT NULL DEFAULT '',
|
|
prompt_tokens INT NOT NULL DEFAULT 0,
|
|
completion_tokens INT NOT NULL DEFAULT 0,
|
|
cached_input_tokens INT NOT NULL DEFAULT 0,
|
|
reasoning_tokens INT NOT NULL DEFAULT 0,
|
|
cost_micro_eur BIGINT NOT NULL DEFAULT 0,
|
|
estimated BOOLEAN NOT NULL DEFAULT false,
|
|
request_id TEXT
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_ai_usage_events_user_time ON ai_usage_events(user_id, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_ai_usage_events_scope_key ON ai_usage_events(billing_scope, provider_key_fingerprint, created_at DESC);
|
|
CREATE INDEX IF NOT EXISTS idx_ai_usage_events_created ON ai_usage_events(created_at DESC);
|
|
|
|
-- Extend daily rollups
|
|
ALTER TABLE ai_usage_daily
|
|
ADD COLUMN IF NOT EXISTS cost_micro_eur_org BIGINT NOT NULL DEFAULT 0,
|
|
ADD COLUMN IF NOT EXISTS cost_micro_eur_user BIGINT NOT NULL DEFAULT 0;
|
|
|
|
-- Extend monthly rollups
|
|
ALTER TABLE ai_usage_monthly
|
|
ADD COLUMN IF NOT EXISTS cost_micro_eur_org BIGINT NOT NULL DEFAULT 0,
|
|
ADD COLUMN IF NOT EXISTS cost_micro_eur_user BIGINT NOT NULL DEFAULT 0;
|
|
|
|
-- Org-wide aggregates
|
|
CREATE TABLE IF NOT EXISTS ai_org_usage_daily (
|
|
usage_date DATE NOT NULL PRIMARY KEY,
|
|
cost_micro_eur_org BIGINT NOT NULL DEFAULT 0,
|
|
cost_micro_eur_user BIGINT NOT NULL DEFAULT 0,
|
|
requests INT NOT NULL DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS ai_org_usage_monthly (
|
|
usage_month DATE NOT NULL PRIMARY KEY,
|
|
cost_micro_eur_org BIGINT NOT NULL DEFAULT 0,
|
|
cost_micro_eur_user BIGINT NOT NULL DEFAULT 0
|
|
);
|
|
|
|
-- Cost limit policies (org / group / user)
|
|
CREATE TABLE IF NOT EXISTS ai_cost_policies (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
scope_type TEXT NOT NULL CHECK (scope_type IN ('org', 'group', 'user')),
|
|
scope_id UUID,
|
|
daily_limit_micro_eur BIGINT,
|
|
monthly_limit_micro_eur BIGINT,
|
|
warn_threshold_pct INT NOT NULL DEFAULT 80,
|
|
priority INT NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_ai_cost_policies_org ON ai_cost_policies(scope_type) WHERE scope_type = 'org';
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_ai_cost_policies_group ON ai_cost_policies(scope_type, scope_id) WHERE scope_type = 'group';
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_ai_cost_policies_user ON ai_cost_policies(scope_type, scope_id) WHERE scope_type = 'user';
|
|
|
|
-- Default org policy: ~10 EUR/day, ~100 EUR/month
|
|
INSERT INTO ai_cost_policies (scope_type, scope_id, daily_limit_micro_eur, monthly_limit_micro_eur, warn_threshold_pct, priority)
|
|
SELECT 'org', NULL, 10000000, 100000000, 80, 0
|
|
WHERE NOT EXISTS (SELECT 1 FROM ai_cost_policies WHERE scope_type = 'org');
|
|
|
|
-- Seed common model pricing (approximate public rates in EUR)
|
|
INSERT INTO ai_model_pricing (model_id, provider_type, input_micro_eur_per_mtok, cached_input_micro_eur_per_mtok, output_micro_eur_per_mtok, source) VALUES
|
|
('gpt-4o-mini', 'openai', 140000, 70000, 560000, 'seed'),
|
|
('gpt-4o', 'openai', 2300000, 1150000, 9200000, 'seed'),
|
|
('gpt-4.1-mini', 'openai', 360000, 90000, 1440000, 'seed'),
|
|
('gpt-4.1', 'openai', 1800000, 450000, 7200000, 'seed'),
|
|
('o3-mini', 'openai', 990000, 250000, 3960000, 'seed'),
|
|
('claude-sonnet-4-6', 'anthropic', 2700000, 270000, 13500000, 'seed'),
|
|
('claude-haiku-4-5', 'anthropic', 900000, 90000, 4500000, 'seed'),
|
|
('mistral-small-latest', 'mistral', 180000, 90000, 540000, 'seed'),
|
|
('mistral-large-latest', 'mistral', 1800000, 450000, 5400000, 'seed'),
|
|
('gemini-2.0-flash', 'google_gemini', 90000, 23000, 360000, 'seed'),
|
|
('gemini-2.5-pro', 'google_gemini', 1100000, 280000, 9000000, 'seed')
|
|
ON CONFLICT DO NOTHING;
|