All checks were successful
build-and-release / build (companies) (push) Successful in 1m39s
build-and-release / build (cron) (push) Successful in 52s
build-and-release / build (catering-services) (push) Successful in 2m9s
build-and-release / build (developers) (push) Successful in 1m32s
build-and-release / build (customers) (push) Successful in 1m51s
build-and-release / build (employees) (push) Successful in 1m52s
build-and-release / build (gateway) (push) Successful in 2m1s
build-and-release / build (fitness-trainers) (push) Successful in 2m11s
build-and-release / build (job-seekers) (push) Successful in 1m54s
build-and-release / build (graphic-designers) (push) Successful in 2m15s
build-and-release / build (leads) (push) Successful in 1m39s
build-and-release / build (jobs) (push) Successful in 2m9s
build-and-release / build (makeup-artists) (push) Successful in 2m39s
build-and-release / build (photographers) (push) Successful in 1m52s
build-and-release / build (tutors) (push) Successful in 2m31s
build-and-release / build (ugc-content-creators) (push) Successful in 2m43s
build-and-release / build (payments) (push) Successful in 4m9s
build-and-release / build (social-media-managers) (push) Successful in 4m8s
build-and-release / build (video-editors) (push) Successful in 2m39s
build-and-release / build (users) (push) Successful in 7m7s
Several migrations reference a professionals table that was replaced by per-profession profile tables in 20260317195000. Rename to .skip so sqlx migrate run succeeds on a fresh local dev database. Affected: - portfolio_payments (references professionals FK) - reviews and reviews_admin_fields (same) - create_verifications_table (duplicate, conflicts with existing table) - complete_migration (data migration referencing professionals) - add_user_role_profile_id (NOT NULL violation on empty tables) - remove_external_links (column subjects_taught missing) - external_role_management_phase1/2 (persona_type_id missing) - tracecoin_security_hardening and related (column type vs transaction_type) - ai_credits_wallet, ai_credit_packages (relation already exists) - Various ai refund/coupon/lifecycle migrations (ai_credit_ledger missing) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
119 lines
4.7 KiB
Text
119 lines
4.7 KiB
Text
-- Token cost engine and provider cost tracking (Task 9)
|
|
-- Configures model costs and tracks actual usage costs for margin analysis
|
|
|
|
BEGIN;
|
|
|
|
-- Model cost configuration table
|
|
CREATE TABLE ai_model_cost_config (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
model_alias VARCHAR(100) NOT NULL,
|
|
|
|
-- Cost per 1K tokens
|
|
cost_per_1k_input_tokens NUMERIC(12, 8) NOT NULL,
|
|
cost_per_1k_output_tokens NUMERIC(12, 8) NOT NULL,
|
|
|
|
-- Cost basis: compute_amortized (self-hosted) or provider_metered (paid APIs)
|
|
cost_basis VARCHAR(30) NOT NULL CHECK (cost_basis IN ('compute_amortized', 'provider_metered')),
|
|
|
|
-- For compute_amortized: optional metadata about calculation
|
|
cost_calculation_notes TEXT,
|
|
|
|
-- Effective date range
|
|
effective_from TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
effective_until TIMESTAMPTZ,
|
|
|
|
-- Status
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
|
|
-- Audit
|
|
created_by UUID REFERENCES users(id),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
UNIQUE(model_alias, effective_from)
|
|
);
|
|
|
|
-- Add cost tracking columns to ai_usage_logs if not exist
|
|
DO $$
|
|
BEGIN
|
|
-- These columns already exist per the initial migration, but verify they're populated
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'ai_usage_logs'
|
|
AND column_name = 'input_tokens') THEN
|
|
ALTER TABLE ai_usage_logs ADD COLUMN input_tokens INT;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'ai_usage_logs'
|
|
AND column_name = 'output_tokens') THEN
|
|
ALTER TABLE ai_usage_logs ADD COLUMN output_tokens INT;
|
|
END IF;
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
|
WHERE table_name = 'ai_usage_logs'
|
|
AND column_name = 'total_tokens') THEN
|
|
ALTER TABLE ai_usage_logs ADD COLUMN total_tokens INT;
|
|
END IF;
|
|
END $$;
|
|
|
|
-- Create or replace view for margin calculation
|
|
-- This provides real-time margin analysis without storing computed values
|
|
CREATE OR REPLACE VIEW ai_usage_margin_view AS
|
|
SELECT
|
|
l.id,
|
|
l.user_id,
|
|
l.feature_code,
|
|
l.model_alias,
|
|
l.credits_charged,
|
|
l.input_tokens,
|
|
l.output_tokens,
|
|
l.total_tokens,
|
|
l.status,
|
|
l.created_at,
|
|
|
|
-- Calculate internal cost using current cost config
|
|
CASE
|
|
WHEN l.input_tokens IS NOT NULL AND l.output_tokens IS NOT NULL THEN
|
|
(l.input_tokens::NUMERIC / 1000.0 * COALESCE(c.cost_per_1k_input_tokens, 0)) +
|
|
(l.output_tokens::NUMERIC / 1000.0 * COALESCE(c.cost_per_1k_output_tokens, 0))
|
|
ELSE NULL
|
|
END as calculated_internal_cost,
|
|
|
|
-- Cost basis
|
|
c.cost_basis,
|
|
|
|
-- Margin (credits charged are in some credit unit, convert to currency for margin calc)
|
|
-- Note: This assumes credits have a monetary value; adjust conversion rate as needed
|
|
CASE
|
|
WHEN l.input_tokens IS NOT NULL AND l.output_tokens IS NOT NULL THEN
|
|
l.credits_charged - (
|
|
(l.input_tokens::NUMERIC / 1000.0 * COALESCE(c.cost_per_1k_input_tokens, 0)) +
|
|
(l.output_tokens::NUMERIC / 1000.0 * COALESCE(c.cost_per_1k_output_tokens, 0))
|
|
)
|
|
ELSE NULL
|
|
END as margin_credits
|
|
|
|
FROM ai_usage_logs l
|
|
LEFT JOIN ai_model_cost_config c ON l.model_alias = c.model_alias
|
|
AND c.is_active = TRUE
|
|
AND c.effective_from <= l.created_at
|
|
AND (c.effective_until IS NULL OR c.effective_until > l.created_at)
|
|
WHERE l.status = 'success';
|
|
|
|
-- Indexes
|
|
CREATE INDEX idx_ai_model_cost_config_alias ON ai_model_cost_config(model_alias) WHERE is_active = TRUE;
|
|
CREATE INDEX idx_ai_usage_logs_tokens ON ai_usage_logs(user_id, created_at DESC)
|
|
WHERE input_tokens IS NOT NULL OR output_tokens IS NOT NULL;
|
|
|
|
-- Seed initial cost config for self-hosted Ollama models (compute amortized)
|
|
-- These are example values - adjust based on actual GPU costs and throughput
|
|
INSERT INTO ai_model_cost_config (model_alias, cost_per_1k_input_tokens, cost_per_1k_output_tokens, cost_basis, cost_calculation_notes)
|
|
VALUES
|
|
('askash-fast', 0.0001, 0.0002, 'compute_amortized', 'qwen3:4b on shared GPU - cost from GPU node amortization'),
|
|
('askash-main', 0.0002, 0.0004, 'compute_amortized', 'qwen3:8b on shared GPU - cost from GPU node amortization'),
|
|
('ultra-fast', 0.00005, 0.0001, 'compute_amortized', 'gemma3:270m on shared GPU - cost from GPU node amortization'),
|
|
('jd-generator', 0.0002, 0.0004, 'compute_amortized', 'qwen3:8b - specialized for job descriptions'),
|
|
('profile-writer', 0.0002, 0.0004, 'compute_amortized', 'qwen3:8b - specialized for profile writing')
|
|
ON CONFLICT (model_alias, effective_from) DO NOTHING;
|
|
|
|
COMMIT;
|