mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
chore: resolve pr 377 checks
This commit is contained in:
4631
crates/aether-data/migrations/postgres/20260403000000_baseline.sql
Normal file
4631
crates/aether-data/migrations/postgres/20260403000000_baseline.sql
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE provider_endpoints
|
||||
ADD COLUMN IF NOT EXISTS health_score DOUBLE PRECISION NOT NULL DEFAULT 1.0;
|
||||
@@ -0,0 +1,124 @@
|
||||
-- Align Rust-managed migrations with legacy Alembic revision
|
||||
-- c3d4e5f6a7b8 (usage_token_semantics_v2).
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'usage'
|
||||
AND column_name = 'total_tokens'
|
||||
) AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'usage'
|
||||
AND column_name = 'input_output_total_tokens'
|
||||
) THEN
|
||||
ALTER TABLE "usage" RENAME COLUMN total_tokens TO input_output_total_tokens;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
ALTER TABLE "usage"
|
||||
ADD COLUMN IF NOT EXISTS input_output_total_tokens INTEGER DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_input_tokens_5m INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_input_tokens_1h INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS input_context_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS total_tokens INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_cost_usd_5m NUMERIC(20, 8) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_cost_usd_1h NUMERIC(20, 8) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS actual_cache_creation_cost_usd_5m NUMERIC(20, 8) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS actual_cache_creation_cost_usd_1h NUMERIC(20, 8) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS actual_cache_cost_usd NUMERIC(20, 8) NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_price_per_1m_5m NUMERIC(20, 8),
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_price_per_1m_1h NUMERIC(20, 8);
|
||||
|
||||
UPDATE "usage"
|
||||
SET
|
||||
input_output_total_tokens = src.new_iot,
|
||||
input_context_tokens = src.new_ict,
|
||||
total_tokens = src.new_total,
|
||||
cache_creation_cost_usd_5m = src.new_cc5m,
|
||||
cache_creation_cost_usd_1h = src.new_cc1h,
|
||||
actual_cache_creation_cost_usd_5m = src.new_acc5m,
|
||||
actual_cache_creation_cost_usd_1h = src.new_acc1h,
|
||||
actual_cache_cost_usd = src.new_accu,
|
||||
cache_creation_price_per_1m_5m = src.new_cp5m,
|
||||
cache_creation_price_per_1m_1h = src.new_cp1h,
|
||||
cache_cost_usd = src.new_ccu
|
||||
FROM (
|
||||
SELECT
|
||||
id,
|
||||
COALESCE(
|
||||
input_output_total_tokens,
|
||||
COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)
|
||||
) AS new_iot,
|
||||
COALESCE(input_tokens, 0) + COALESCE(cache_read_input_tokens, 0) AS new_ict,
|
||||
COALESCE(
|
||||
input_output_total_tokens,
|
||||
COALESCE(input_tokens, 0) + COALESCE(output_tokens, 0)
|
||||
) + COALESCE(cache_creation_input_tokens, 0) + COALESCE(cache_read_input_tokens, 0) AS new_total,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_5m, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_1h, 0) = 0
|
||||
THEN COALESCE(cache_creation_cost_usd, 0)
|
||||
WHEN COALESCE(cache_creation_input_tokens_5m, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens, 0) > 0
|
||||
THEN COALESCE(cache_creation_cost_usd, 0)
|
||||
* (COALESCE(cache_creation_input_tokens_5m, 0) * 1.0
|
||||
/ GREATEST(COALESCE(cache_creation_input_tokens, 0), 1))
|
||||
ELSE 0
|
||||
END AS new_cc5m,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_1h, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_5m, 0) = 0
|
||||
THEN COALESCE(cache_creation_cost_usd, 0)
|
||||
WHEN COALESCE(cache_creation_input_tokens_1h, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens, 0) > 0
|
||||
THEN COALESCE(cache_creation_cost_usd, 0)
|
||||
* (COALESCE(cache_creation_input_tokens_1h, 0) * 1.0
|
||||
/ GREATEST(COALESCE(cache_creation_input_tokens, 0), 1))
|
||||
ELSE 0
|
||||
END AS new_cc1h,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_5m, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_1h, 0) = 0
|
||||
THEN COALESCE(actual_cache_creation_cost_usd, 0)
|
||||
WHEN COALESCE(cache_creation_input_tokens_5m, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens, 0) > 0
|
||||
THEN COALESCE(actual_cache_creation_cost_usd, 0)
|
||||
* (COALESCE(cache_creation_input_tokens_5m, 0) * 1.0
|
||||
/ GREATEST(COALESCE(cache_creation_input_tokens, 0), 1))
|
||||
ELSE 0
|
||||
END AS new_acc5m,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_1h, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_5m, 0) = 0
|
||||
THEN COALESCE(actual_cache_creation_cost_usd, 0)
|
||||
WHEN COALESCE(cache_creation_input_tokens_1h, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens, 0) > 0
|
||||
THEN COALESCE(actual_cache_creation_cost_usd, 0)
|
||||
* (COALESCE(cache_creation_input_tokens_1h, 0) * 1.0
|
||||
/ GREATEST(COALESCE(cache_creation_input_tokens, 0), 1))
|
||||
ELSE 0
|
||||
END AS new_acc1h,
|
||||
COALESCE(actual_cache_creation_cost_usd, 0)
|
||||
+ COALESCE(actual_cache_read_cost_usd, 0) AS new_accu,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_5m, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_1h, 0) = 0
|
||||
THEN cache_creation_price_per_1m
|
||||
ELSE NULL
|
||||
END AS new_cp5m,
|
||||
CASE
|
||||
WHEN COALESCE(cache_creation_input_tokens_1h, 0) > 0
|
||||
AND COALESCE(cache_creation_input_tokens_5m, 0) = 0
|
||||
THEN cache_creation_price_per_1m
|
||||
ELSE NULL
|
||||
END AS new_cp1h,
|
||||
COALESCE(cache_creation_cost_usd, 0)
|
||||
+ COALESCE(cache_read_cost_usd, 0) AS new_ccu
|
||||
FROM "usage"
|
||||
) AS src
|
||||
WHERE "usage".id = src.id;
|
||||
@@ -0,0 +1,151 @@
|
||||
-- Squashed unreleased usage schema split:
|
||||
-- 20260412000000_add_usage_body_blobs.sql
|
||||
-- 20260412010000_add_usage_http_audits.sql
|
||||
-- 20260412020000_add_usage_routing_snapshots.sql
|
||||
-- 20260412030000_add_usage_settlement_snapshots.sql
|
||||
-- 20260413000000_expand_usage_settlement_snapshots_for_pricing.sql
|
||||
-- 20260413010000_mark_usage_legacy_columns_deprecated.sql
|
||||
-- 20260413020000_add_candidate_index_to_usage_routing_snapshots.sql
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.usage_body_blobs (
|
||||
body_ref character varying(160) NOT NULL,
|
||||
request_id character varying(100) NOT NULL,
|
||||
body_field character varying(50) NOT NULL,
|
||||
payload_gzip bytea NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT usage_body_blobs_pkey PRIMARY KEY (body_ref),
|
||||
CONSTRAINT usage_body_blobs_request_id_field_key UNIQUE (request_id, body_field),
|
||||
CONSTRAINT usage_body_blobs_request_id_fkey
|
||||
FOREIGN KEY (request_id)
|
||||
REFERENCES public.usage(request_id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_usage_body_blobs_request_id
|
||||
ON public.usage_body_blobs USING btree (request_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.usage_http_audits (
|
||||
request_id character varying(100) NOT NULL,
|
||||
request_headers json,
|
||||
provider_request_headers json,
|
||||
response_headers json,
|
||||
client_response_headers json,
|
||||
request_body_ref character varying(160),
|
||||
provider_request_body_ref character varying(160),
|
||||
response_body_ref character varying(160),
|
||||
client_response_body_ref character varying(160),
|
||||
request_body_state character varying(32),
|
||||
provider_request_body_state character varying(32),
|
||||
response_body_state character varying(32),
|
||||
client_response_body_state character varying(32),
|
||||
body_capture_mode character varying(32) DEFAULT 'none' NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT usage_http_audits_pkey PRIMARY KEY (request_id),
|
||||
CONSTRAINT usage_http_audits_request_id_fkey
|
||||
FOREIGN KEY (request_id)
|
||||
REFERENCES public.usage(request_id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_usage_http_audits_updated_at
|
||||
ON public.usage_http_audits USING btree (updated_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.usage_routing_snapshots (
|
||||
request_id character varying(100) NOT NULL,
|
||||
candidate_id character varying(160),
|
||||
candidate_index integer,
|
||||
key_name character varying(255),
|
||||
planner_kind character varying(120),
|
||||
route_family character varying(80),
|
||||
route_kind character varying(80),
|
||||
execution_path character varying(80),
|
||||
local_execution_runtime_miss_reason character varying(120),
|
||||
selected_provider_id character varying(100),
|
||||
selected_endpoint_id character varying(100),
|
||||
selected_provider_api_key_id character varying(100),
|
||||
has_format_conversion boolean,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT usage_routing_snapshots_pkey PRIMARY KEY (request_id),
|
||||
CONSTRAINT usage_routing_snapshots_request_id_fkey
|
||||
FOREIGN KEY (request_id)
|
||||
REFERENCES public.usage(request_id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_usage_routing_snapshots_route_family_kind
|
||||
ON public.usage_routing_snapshots USING btree (route_family, route_kind);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_usage_routing_snapshots_candidate_id
|
||||
ON public.usage_routing_snapshots USING btree (candidate_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.usage_settlement_snapshots (
|
||||
request_id character varying(100) NOT NULL,
|
||||
billing_status character varying(20) NOT NULL,
|
||||
wallet_id character varying(36),
|
||||
wallet_balance_before numeric(20,8),
|
||||
wallet_balance_after numeric(20,8),
|
||||
wallet_recharge_balance_before numeric(20,8),
|
||||
wallet_recharge_balance_after numeric(20,8),
|
||||
wallet_gift_balance_before numeric(20,8),
|
||||
wallet_gift_balance_after numeric(20,8),
|
||||
provider_monthly_used_usd numeric(20,8),
|
||||
finalized_at timestamp with time zone,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT usage_settlement_snapshots_pkey PRIMARY KEY (request_id),
|
||||
CONSTRAINT usage_settlement_snapshots_request_id_fkey
|
||||
FOREIGN KEY (request_id)
|
||||
REFERENCES public.usage(request_id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_usage_settlement_snapshots_wallet_id
|
||||
ON public.usage_settlement_snapshots USING btree (wallet_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_usage_settlement_snapshots_billing_status
|
||||
ON public.usage_settlement_snapshots USING btree (billing_status);
|
||||
|
||||
ALTER TABLE IF EXISTS public.usage_settlement_snapshots
|
||||
ADD COLUMN IF NOT EXISTS billing_snapshot_schema_version character varying(20),
|
||||
ADD COLUMN IF NOT EXISTS billing_snapshot_status character varying(20),
|
||||
ADD COLUMN IF NOT EXISTS rate_multiplier numeric(10,6),
|
||||
ADD COLUMN IF NOT EXISTS is_free_tier boolean,
|
||||
ADD COLUMN IF NOT EXISTS input_price_per_1m numeric(20,8),
|
||||
ADD COLUMN IF NOT EXISTS output_price_per_1m numeric(20,8),
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_price_per_1m numeric(20,8),
|
||||
ADD COLUMN IF NOT EXISTS cache_read_price_per_1m numeric(20,8),
|
||||
ADD COLUMN IF NOT EXISTS price_per_request numeric(20,8);
|
||||
|
||||
COMMENT ON COLUMN public.usage.wallet_id IS
|
||||
'DEPRECATED: settlement owner moved to public.usage_settlement_snapshots.wallet_id. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.wallet_balance_before IS
|
||||
'DEPRECATED: settlement owner moved to public.usage_settlement_snapshots.wallet_balance_before. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.wallet_balance_after IS
|
||||
'DEPRECATED: settlement owner moved to public.usage_settlement_snapshots.wallet_balance_after. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.wallet_recharge_balance_before IS
|
||||
'DEPRECATED: settlement owner moved to public.usage_settlement_snapshots.wallet_recharge_balance_before. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.wallet_recharge_balance_after IS
|
||||
'DEPRECATED: settlement owner moved to public.usage_settlement_snapshots.wallet_recharge_balance_after. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.wallet_gift_balance_before IS
|
||||
'DEPRECATED: settlement owner moved to public.usage_settlement_snapshots.wallet_gift_balance_before. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.wallet_gift_balance_after IS
|
||||
'DEPRECATED: settlement owner moved to public.usage_settlement_snapshots.wallet_gift_balance_after. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.rate_multiplier IS
|
||||
'DEPRECATED: settlement pricing owner moved to public.usage_settlement_snapshots.rate_multiplier. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.input_price_per_1m IS
|
||||
'DEPRECATED: settlement pricing owner moved to public.usage_settlement_snapshots.input_price_per_1m. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.output_price_per_1m IS
|
||||
'DEPRECATED: settlement pricing owner moved to public.usage_settlement_snapshots.output_price_per_1m. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.cache_creation_price_per_1m IS
|
||||
'DEPRECATED: settlement pricing owner moved to public.usage_settlement_snapshots.cache_creation_price_per_1m. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.cache_read_price_per_1m IS
|
||||
'DEPRECATED: settlement pricing owner moved to public.usage_settlement_snapshots.cache_read_price_per_1m. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.price_per_request IS
|
||||
'DEPRECATED: settlement pricing owner moved to public.usage_settlement_snapshots.price_per_request. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.username IS
|
||||
'DEPRECATED: display cache only. Prefer join-time lookup from user/auth records. Legacy compatibility only.';
|
||||
COMMENT ON COLUMN public.usage.api_key_name IS
|
||||
'DEPRECATED: display cache only. Prefer join-time lookup from API key records. Legacy compatibility only.';
|
||||
@@ -0,0 +1,28 @@
|
||||
COMMENT ON COLUMN public.usage.billing_status IS
|
||||
'DEPRECATED: authoritative owner moved to public.usage_settlement_snapshots.billing_status. Compatibility/index mirror only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.finalized_at IS
|
||||
'DEPRECATED: authoritative owner moved to public.usage_settlement_snapshots.finalized_at. Compatibility/index mirror only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.request_headers IS
|
||||
'DEPRECATED: HTTP audit owner moved to public.usage_http_audits.request_headers. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.provider_request_headers IS
|
||||
'DEPRECATED: HTTP audit owner moved to public.usage_http_audits.provider_request_headers. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.response_headers IS
|
||||
'DEPRECATED: HTTP audit owner moved to public.usage_http_audits.response_headers. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.client_response_headers IS
|
||||
'DEPRECATED: HTTP audit owner moved to public.usage_http_audits.client_response_headers. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.request_body IS
|
||||
'DEPRECATED: HTTP body owner moved to public.usage_body_blobs plus public.usage_http_audits.request_body_ref. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.request_body_compressed IS
|
||||
'DEPRECATED: HTTP body owner moved to public.usage_body_blobs plus public.usage_http_audits.request_body_ref. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.provider_request_body IS
|
||||
'DEPRECATED: HTTP body owner moved to public.usage_body_blobs plus public.usage_http_audits.provider_request_body_ref. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.provider_request_body_compressed IS
|
||||
'DEPRECATED: HTTP body owner moved to public.usage_body_blobs plus public.usage_http_audits.provider_request_body_ref. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.response_body IS
|
||||
'DEPRECATED: HTTP body owner moved to public.usage_body_blobs plus public.usage_http_audits.response_body_ref. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.response_body_compressed IS
|
||||
'DEPRECATED: HTTP body owner moved to public.usage_body_blobs plus public.usage_http_audits.response_body_ref. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.client_response_body IS
|
||||
'DEPRECATED: HTTP body owner moved to public.usage_body_blobs plus public.usage_http_audits.client_response_body_ref. Legacy compatibility only; do not write new values.';
|
||||
COMMENT ON COLUMN public.usage.client_response_body_compressed IS
|
||||
'DEPRECATED: HTTP body owner moved to public.usage_body_blobs plus public.usage_http_audits.client_response_body_ref. Legacy compatibility only; do not write new values.';
|
||||
@@ -0,0 +1,116 @@
|
||||
CREATE TABLE IF NOT EXISTS public.redeem_code_batches (
|
||||
id character varying(36) NOT NULL,
|
||||
name character varying(120) NOT NULL,
|
||||
amount_usd numeric(20,8) NOT NULL,
|
||||
currency character varying(3) DEFAULT 'USD'::character varying NOT NULL,
|
||||
balance_bucket character varying(20) DEFAULT 'gift'::character varying NOT NULL,
|
||||
total_count integer NOT NULL,
|
||||
status character varying(20) DEFAULT 'active'::character varying NOT NULL,
|
||||
description text,
|
||||
created_by character varying(36),
|
||||
expires_at timestamp with time zone,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
CONSTRAINT ck_redeem_code_batches_amount_positive CHECK ((amount_usd > (0)::numeric)),
|
||||
CONSTRAINT ck_redeem_code_batches_total_count_positive CHECK ((total_count > 0))
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.redeem_codes (
|
||||
id character varying(36) NOT NULL,
|
||||
batch_id character varying(36) NOT NULL,
|
||||
code_hash character varying(64) NOT NULL,
|
||||
code_prefix character varying(8) NOT NULL,
|
||||
code_suffix character varying(8) NOT NULL,
|
||||
status character varying(20) DEFAULT 'active'::character varying NOT NULL,
|
||||
redeemed_by_user_id character varying(36),
|
||||
redeemed_wallet_id character varying(36),
|
||||
redeemed_payment_order_id character varying(36),
|
||||
redeemed_at timestamp with time zone,
|
||||
disabled_by character varying(36),
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
DO $mig$ BEGIN
|
||||
ALTER TABLE ONLY public.redeem_code_batches
|
||||
ADD CONSTRAINT redeem_code_batches_pkey PRIMARY KEY (id);
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
WHEN duplicate_table THEN NULL;
|
||||
WHEN invalid_table_definition THEN NULL;
|
||||
END $mig$;
|
||||
|
||||
DO $mig$ BEGIN
|
||||
ALTER TABLE ONLY public.redeem_codes
|
||||
ADD CONSTRAINT redeem_codes_pkey PRIMARY KEY (id);
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
WHEN duplicate_table THEN NULL;
|
||||
WHEN invalid_table_definition THEN NULL;
|
||||
END $mig$;
|
||||
|
||||
DO $mig$ BEGIN
|
||||
ALTER TABLE ONLY public.redeem_codes
|
||||
ADD CONSTRAINT uq_redeem_codes_code_hash UNIQUE (code_hash);
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
WHEN duplicate_table THEN NULL;
|
||||
WHEN invalid_table_definition THEN NULL;
|
||||
END $mig$;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_redeem_code_batches_status
|
||||
ON public.redeem_code_batches USING btree (status, created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_redeem_codes_batch_created
|
||||
ON public.redeem_codes USING btree (batch_id, created_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_redeem_codes_status
|
||||
ON public.redeem_codes USING btree (status, updated_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_redeem_codes_redeemed_user
|
||||
ON public.redeem_codes USING btree (redeemed_by_user_id, redeemed_at);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_redeem_codes_redeemed_order
|
||||
ON public.redeem_codes USING btree (redeemed_payment_order_id);
|
||||
|
||||
DO $mig$ BEGIN
|
||||
ALTER TABLE ONLY public.redeem_code_batches
|
||||
ADD CONSTRAINT redeem_code_batches_created_by_fkey FOREIGN KEY (created_by) REFERENCES public.users(id) ON DELETE SET NULL;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END $mig$;
|
||||
|
||||
DO $mig$ BEGIN
|
||||
ALTER TABLE ONLY public.redeem_codes
|
||||
ADD CONSTRAINT redeem_codes_batch_id_fkey FOREIGN KEY (batch_id) REFERENCES public.redeem_code_batches(id) ON DELETE CASCADE;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END $mig$;
|
||||
|
||||
DO $mig$ BEGIN
|
||||
ALTER TABLE ONLY public.redeem_codes
|
||||
ADD CONSTRAINT redeem_codes_redeemed_by_user_id_fkey FOREIGN KEY (redeemed_by_user_id) REFERENCES public.users(id) ON DELETE SET NULL;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END $mig$;
|
||||
|
||||
DO $mig$ BEGIN
|
||||
ALTER TABLE ONLY public.redeem_codes
|
||||
ADD CONSTRAINT redeem_codes_redeemed_wallet_id_fkey FOREIGN KEY (redeemed_wallet_id) REFERENCES public.wallets(id) ON DELETE SET NULL;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END $mig$;
|
||||
|
||||
DO $mig$ BEGIN
|
||||
ALTER TABLE ONLY public.redeem_codes
|
||||
ADD CONSTRAINT redeem_codes_redeemed_payment_order_id_fkey FOREIGN KEY (redeemed_payment_order_id) REFERENCES public.payment_orders(id) ON DELETE SET NULL;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END $mig$;
|
||||
|
||||
DO $mig$ BEGIN
|
||||
ALTER TABLE ONLY public.redeem_codes
|
||||
ADD CONSTRAINT redeem_codes_disabled_by_fkey FOREIGN KEY (disabled_by) REFERENCES public.users(id) ON DELETE SET NULL;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN NULL;
|
||||
END $mig$;
|
||||
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE public.usage_http_audits
|
||||
ADD COLUMN IF NOT EXISTS request_body_state character varying(32),
|
||||
ADD COLUMN IF NOT EXISTS provider_request_body_state character varying(32),
|
||||
ADD COLUMN IF NOT EXISTS response_body_state character varying(32),
|
||||
ADD COLUMN IF NOT EXISTS client_response_body_state character varying(32);
|
||||
@@ -0,0 +1,127 @@
|
||||
ALTER TABLE public.stats_user_daily
|
||||
ADD COLUMN IF NOT EXISTS actual_total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS response_time_samples bigint DEFAULT 0 NOT NULL;
|
||||
|
||||
ALTER TABLE public.stats_hourly_user
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS actual_total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS response_time_samples bigint DEFAULT 0 NOT NULL;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_user_summary (
|
||||
id character varying(36) NOT NULL,
|
||||
user_id character varying(36) NOT NULL,
|
||||
username character varying(100),
|
||||
cutoff_date timestamp with time zone NOT NULL,
|
||||
all_time_requests integer DEFAULT 0 NOT NULL,
|
||||
all_time_success_requests integer DEFAULT 0 NOT NULL,
|
||||
all_time_error_requests integer DEFAULT 0 NOT NULL,
|
||||
all_time_input_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
all_time_output_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
all_time_cache_creation_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
all_time_cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
all_time_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
all_time_actual_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
active_days integer DEFAULT 0 NOT NULL,
|
||||
first_active_date timestamp with time zone,
|
||||
last_active_date timestamp with time zone,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_user_summary_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_user_summary_user_id UNIQUE (user_id),
|
||||
CONSTRAINT stats_user_summary_user_id_fkey
|
||||
FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_summary_cutoff_date
|
||||
ON public.stats_user_summary USING btree (cutoff_date);
|
||||
|
||||
ALTER TABLE public.stats_daily
|
||||
ADD COLUMN IF NOT EXISTS effective_input_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS total_input_context bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS response_time_samples bigint DEFAULT 0 NOT NULL;
|
||||
|
||||
ALTER TABLE public.stats_user_daily
|
||||
ADD COLUMN IF NOT EXISTS effective_input_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS total_input_context bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_read_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL;
|
||||
|
||||
ALTER TABLE public.stats_daily_model
|
||||
ADD COLUMN IF NOT EXISTS response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS response_time_samples bigint DEFAULT 0 NOT NULL;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_user_daily_model (
|
||||
id character varying(36) NOT NULL,
|
||||
user_id character varying(36) NOT NULL,
|
||||
username character varying(100),
|
||||
date timestamp with time zone NOT NULL,
|
||||
model character varying(100) NOT NULL,
|
||||
total_requests integer DEFAULT 0 NOT NULL,
|
||||
input_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
output_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_samples bigint DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_user_daily_model_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_user_daily_model_user_date_model UNIQUE (user_id, date, model),
|
||||
CONSTRAINT stats_user_daily_model_user_id_fkey
|
||||
FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_model_date
|
||||
ON public.stats_user_daily_model USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_model_user_id
|
||||
ON public.stats_user_daily_model USING btree (user_id);
|
||||
|
||||
ALTER TABLE public.stats_hourly
|
||||
ADD COLUMN IF NOT EXISTS response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS response_time_samples bigint DEFAULT 0 NOT NULL;
|
||||
|
||||
ALTER TABLE public.stats_hourly_model
|
||||
ADD COLUMN IF NOT EXISTS response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS response_time_samples bigint DEFAULT 0 NOT NULL;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_hourly_user_model (
|
||||
id character varying(36) NOT NULL,
|
||||
hour_utc timestamp with time zone NOT NULL,
|
||||
user_id character varying(36) NOT NULL,
|
||||
model character varying(100) NOT NULL,
|
||||
total_requests integer DEFAULT 0 NOT NULL,
|
||||
input_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
output_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_samples bigint DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_hourly_user_model_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_hourly_user_model UNIQUE (hour_utc, user_id, model),
|
||||
CONSTRAINT stats_hourly_user_model_user_id_fkey
|
||||
FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_hourly_user_model_hour
|
||||
ON public.stats_hourly_user_model USING btree (hour_utc);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_hourly_user_model_user_hour
|
||||
ON public.stats_hourly_user_model USING btree (user_id, hour_utc);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.schema_backfills (
|
||||
version bigint NOT NULL,
|
||||
description text NOT NULL,
|
||||
success boolean NOT NULL DEFAULT TRUE,
|
||||
checksum bytea NOT NULL,
|
||||
execution_time bigint NOT NULL DEFAULT 0,
|
||||
applied_at timestamp with time zone NOT NULL DEFAULT now(),
|
||||
CONSTRAINT schema_backfills_pkey PRIMARY KEY (version)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_schema_backfills_applied_at
|
||||
ON public.schema_backfills USING btree (applied_at DESC);
|
||||
@@ -0,0 +1,377 @@
|
||||
ALTER TABLE public.stats_user_daily_model
|
||||
ADD COLUMN IF NOT EXISTS success_requests integer DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS effective_input_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS total_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS total_input_context bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_ephemeral_5m_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_ephemeral_1h_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS actual_total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS successful_response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS successful_response_time_samples bigint DEFAULT 0 NOT NULL;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_user_daily_provider (
|
||||
id character varying(36) NOT NULL,
|
||||
user_id character varying(36) NOT NULL,
|
||||
username character varying(100),
|
||||
date timestamp with time zone NOT NULL,
|
||||
provider_name character varying(100) NOT NULL,
|
||||
total_requests integer DEFAULT 0 NOT NULL,
|
||||
success_requests integer DEFAULT 0 NOT NULL,
|
||||
input_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
effective_input_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
output_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
total_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
total_input_context bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_creation_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_creation_ephemeral_5m_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_creation_ephemeral_1h_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
actual_total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_samples bigint DEFAULT 0 NOT NULL,
|
||||
successful_response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
successful_response_time_samples bigint DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_user_daily_provider_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_user_daily_provider_user_date_provider UNIQUE (user_id, date, provider_name),
|
||||
CONSTRAINT stats_user_daily_provider_user_id_fkey
|
||||
FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_provider_date
|
||||
ON public.stats_user_daily_provider USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_provider_user_id
|
||||
ON public.stats_user_daily_provider USING btree (user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_user_daily_api_format (
|
||||
id character varying(36) NOT NULL,
|
||||
user_id character varying(36) NOT NULL,
|
||||
username character varying(100),
|
||||
date timestamp with time zone NOT NULL,
|
||||
api_format character varying(50) NOT NULL,
|
||||
total_requests integer DEFAULT 0 NOT NULL,
|
||||
success_requests integer DEFAULT 0 NOT NULL,
|
||||
input_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
effective_input_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
output_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
total_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
total_input_context bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_creation_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_creation_ephemeral_5m_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_creation_ephemeral_1h_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
actual_total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_samples bigint DEFAULT 0 NOT NULL,
|
||||
successful_response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
successful_response_time_samples bigint DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_user_daily_api_format_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_user_daily_api_format_user_date_api_format UNIQUE (user_id, date, api_format),
|
||||
CONSTRAINT stats_user_daily_api_format_user_id_fkey
|
||||
FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_api_format_date
|
||||
ON public.stats_user_daily_api_format USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_api_format_user_id
|
||||
ON public.stats_user_daily_api_format USING btree (user_id);
|
||||
CREATE TABLE IF NOT EXISTS public.stats_daily_model_provider (
|
||||
id character varying(36) NOT NULL,
|
||||
date timestamp with time zone NOT NULL,
|
||||
model character varying(100) NOT NULL,
|
||||
provider_name character varying(100) NOT NULL,
|
||||
total_requests integer DEFAULT 0 NOT NULL,
|
||||
total_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_samples bigint DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_daily_model_provider_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_daily_model_provider UNIQUE (date, model, provider_name)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_daily_model_provider_date
|
||||
ON public.stats_daily_model_provider USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_daily_model_provider_date_model_provider
|
||||
ON public.stats_daily_model_provider USING btree (date, model, provider_name);
|
||||
CREATE TABLE IF NOT EXISTS public.stats_user_daily_model_provider (
|
||||
id character varying(36) NOT NULL,
|
||||
user_id character varying(36) NOT NULL,
|
||||
username character varying(100),
|
||||
date timestamp with time zone NOT NULL,
|
||||
model character varying(100) NOT NULL,
|
||||
provider_name character varying(100) NOT NULL,
|
||||
total_requests integer DEFAULT 0 NOT NULL,
|
||||
total_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_sum_ms double precision DEFAULT '0'::double precision NOT NULL,
|
||||
response_time_samples bigint DEFAULT 0 NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_user_daily_model_provider_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_user_daily_model_provider UNIQUE (user_id, date, model, provider_name),
|
||||
CONSTRAINT stats_user_daily_model_provider_user_id_fkey
|
||||
FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_model_provider_date
|
||||
ON public.stats_user_daily_model_provider USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_model_provider_user_date
|
||||
ON public.stats_user_daily_model_provider USING btree (user_id, date);
|
||||
ALTER TABLE public.stats_daily
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_ephemeral_5m_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_ephemeral_1h_tokens bigint DEFAULT '0'::bigint NOT NULL;
|
||||
|
||||
ALTER TABLE public.stats_user_daily
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_ephemeral_5m_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_ephemeral_1h_tokens bigint DEFAULT '0'::bigint NOT NULL;
|
||||
ALTER TABLE public.stats_daily
|
||||
ADD COLUMN IF NOT EXISTS cache_hit_total_requests bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_hit_requests bigint DEFAULT 0 NOT NULL;
|
||||
|
||||
ALTER TABLE public.stats_hourly
|
||||
ADD COLUMN IF NOT EXISTS cache_hit_total_requests bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_hit_requests bigint DEFAULT 0 NOT NULL;
|
||||
ALTER TABLE public.stats_daily
|
||||
ADD COLUMN IF NOT EXISTS completed_total_requests bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_cache_hit_requests bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_input_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_cache_creation_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_cache_read_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_total_input_context bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_cache_creation_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_cache_read_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL;
|
||||
|
||||
ALTER TABLE public.stats_hourly
|
||||
ADD COLUMN IF NOT EXISTS completed_total_requests bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_cache_hit_requests bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_input_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_cache_creation_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_cache_read_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_total_input_context bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_cache_creation_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS completed_cache_read_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL;
|
||||
ALTER TABLE public.stats_daily
|
||||
ADD COLUMN IF NOT EXISTS settled_total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_total_requests bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_input_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_output_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_cache_creation_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_cache_read_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_first_finalized_at_unix_secs bigint,
|
||||
ADD COLUMN IF NOT EXISTS settled_last_finalized_at_unix_secs bigint;
|
||||
|
||||
ALTER TABLE public.stats_hourly
|
||||
ADD COLUMN IF NOT EXISTS settled_total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_total_requests bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_input_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_output_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_cache_creation_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_cache_read_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_first_finalized_at_unix_secs bigint,
|
||||
ADD COLUMN IF NOT EXISTS settled_last_finalized_at_unix_secs bigint;
|
||||
|
||||
ALTER TABLE public.stats_user_daily
|
||||
ADD COLUMN IF NOT EXISTS settled_total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_total_requests bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_input_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_output_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_cache_creation_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_cache_read_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_first_finalized_at_unix_secs bigint,
|
||||
ADD COLUMN IF NOT EXISTS settled_last_finalized_at_unix_secs bigint;
|
||||
|
||||
ALTER TABLE public.stats_hourly_user
|
||||
ADD COLUMN IF NOT EXISTS settled_total_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_total_requests bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_input_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_output_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_cache_creation_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_cache_read_tokens bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS settled_first_finalized_at_unix_secs bigint,
|
||||
ADD COLUMN IF NOT EXISTS settled_last_finalized_at_unix_secs bigint;
|
||||
CREATE TABLE IF NOT EXISTS public.stats_daily_cost_savings (
|
||||
id character varying(36) NOT NULL,
|
||||
date timestamp with time zone NOT NULL,
|
||||
cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_read_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
cache_creation_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
estimated_full_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_daily_cost_savings_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_daily_cost_savings_date UNIQUE (date)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_daily_cost_savings_date
|
||||
ON public.stats_daily_cost_savings USING btree (date);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_daily_cost_savings_provider (
|
||||
id character varying(36) NOT NULL,
|
||||
date timestamp with time zone NOT NULL,
|
||||
provider_name character varying(100) NOT NULL,
|
||||
cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_read_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
cache_creation_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
estimated_full_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_daily_cost_savings_provider_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_daily_cost_savings_provider UNIQUE (date, provider_name)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_daily_cost_savings_provider_date
|
||||
ON public.stats_daily_cost_savings_provider USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_daily_cost_savings_provider_date_provider
|
||||
ON public.stats_daily_cost_savings_provider USING btree (date, provider_name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_daily_cost_savings_model (
|
||||
id character varying(36) NOT NULL,
|
||||
date timestamp with time zone NOT NULL,
|
||||
model character varying(100) NOT NULL,
|
||||
cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_read_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
cache_creation_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
estimated_full_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_daily_cost_savings_model_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_daily_cost_savings_model UNIQUE (date, model)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_daily_cost_savings_model_date
|
||||
ON public.stats_daily_cost_savings_model USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_daily_cost_savings_model_date_model
|
||||
ON public.stats_daily_cost_savings_model USING btree (date, model);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_daily_cost_savings_model_provider (
|
||||
id character varying(36) NOT NULL,
|
||||
date timestamp with time zone NOT NULL,
|
||||
model character varying(100) NOT NULL,
|
||||
provider_name character varying(100) NOT NULL,
|
||||
cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_read_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
cache_creation_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
estimated_full_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_daily_cost_savings_model_provider_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_daily_cost_savings_model_provider UNIQUE (date, model, provider_name)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_daily_cost_savings_model_provider_date
|
||||
ON public.stats_daily_cost_savings_model_provider USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_daily_cost_savings_model_provider_date_dims
|
||||
ON public.stats_daily_cost_savings_model_provider USING btree (date, model, provider_name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_user_daily_cost_savings (
|
||||
id character varying(36) NOT NULL,
|
||||
user_id character varying(36) NOT NULL,
|
||||
username character varying(100),
|
||||
date timestamp with time zone NOT NULL,
|
||||
cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_read_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
cache_creation_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
estimated_full_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_user_daily_cost_savings_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_user_daily_cost_savings UNIQUE (user_id, date),
|
||||
CONSTRAINT stats_user_daily_cost_savings_user_id_fkey
|
||||
FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_cost_savings_date
|
||||
ON public.stats_user_daily_cost_savings USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_cost_savings_user_date
|
||||
ON public.stats_user_daily_cost_savings USING btree (user_id, date);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_user_daily_cost_savings_provider (
|
||||
id character varying(36) NOT NULL,
|
||||
user_id character varying(36) NOT NULL,
|
||||
username character varying(100),
|
||||
date timestamp with time zone NOT NULL,
|
||||
provider_name character varying(100) NOT NULL,
|
||||
cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_read_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
cache_creation_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
estimated_full_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_user_daily_cost_savings_provider_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_user_daily_cost_savings_provider UNIQUE (user_id, date, provider_name),
|
||||
CONSTRAINT stats_user_daily_cost_savings_provider_user_id_fkey
|
||||
FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_cost_savings_provider_date
|
||||
ON public.stats_user_daily_cost_savings_provider USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_cost_savings_provider_user_date
|
||||
ON public.stats_user_daily_cost_savings_provider USING btree (user_id, date);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_user_daily_cost_savings_model (
|
||||
id character varying(36) NOT NULL,
|
||||
user_id character varying(36) NOT NULL,
|
||||
username character varying(100),
|
||||
date timestamp with time zone NOT NULL,
|
||||
model character varying(100) NOT NULL,
|
||||
cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_read_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
cache_creation_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
estimated_full_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_user_daily_cost_savings_model_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_user_daily_cost_savings_model UNIQUE (user_id, date, model),
|
||||
CONSTRAINT stats_user_daily_cost_savings_model_user_id_fkey
|
||||
FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_cost_savings_model_date
|
||||
ON public.stats_user_daily_cost_savings_model USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_cost_savings_model_user_date
|
||||
ON public.stats_user_daily_cost_savings_model USING btree (user_id, date);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.stats_user_daily_cost_savings_model_provider (
|
||||
id character varying(36) NOT NULL,
|
||||
user_id character varying(36) NOT NULL,
|
||||
username character varying(100),
|
||||
date timestamp with time zone NOT NULL,
|
||||
model character varying(100) NOT NULL,
|
||||
provider_name character varying(100) NOT NULL,
|
||||
cache_read_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
cache_read_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
cache_creation_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
estimated_full_cost numeric(20,8) DEFAULT '0'::double precision NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT stats_user_daily_cost_savings_model_provider_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT uq_stats_user_daily_cost_savings_model_provider
|
||||
UNIQUE (user_id, date, model, provider_name),
|
||||
CONSTRAINT stats_user_daily_cost_savings_model_provider_user_id_fkey
|
||||
FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_cost_savings_model_provider_date
|
||||
ON public.stats_user_daily_cost_savings_model_provider USING btree (date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_stats_user_daily_cost_savings_model_provider_user_date
|
||||
ON public.stats_user_daily_cost_savings_model_provider USING btree (user_id, date);
|
||||
@@ -0,0 +1,8 @@
|
||||
ALTER TABLE public.api_keys
|
||||
ADD COLUMN IF NOT EXISTS total_tokens bigint DEFAULT '0'::bigint NOT NULL;
|
||||
|
||||
UPDATE public.provider_api_keys
|
||||
SET
|
||||
fingerprint = NULL,
|
||||
updated_at = NOW()
|
||||
WHERE fingerprint IS NOT NULL;
|
||||
@@ -0,0 +1,38 @@
|
||||
ALTER TABLE public.provider_api_keys
|
||||
ALTER COLUMN api_formats DROP DEFAULT,
|
||||
ALTER COLUMN api_formats DROP NOT NULL;
|
||||
|
||||
UPDATE public.provider_api_keys AS pak
|
||||
SET
|
||||
api_formats = NULL,
|
||||
updated_at = NOW()
|
||||
FROM public.providers AS p
|
||||
WHERE p.id = pak.provider_id
|
||||
AND pak.api_formats IS NOT NULL
|
||||
AND pak.api_formats::jsonb = '[]'::jsonb
|
||||
AND (
|
||||
(
|
||||
LOWER(BTRIM(p.provider_type)) IN (
|
||||
'claude_code',
|
||||
'codex',
|
||||
'gemini_cli',
|
||||
'vertex_ai',
|
||||
'antigravity'
|
||||
)
|
||||
AND LOWER(BTRIM(pak.auth_type)) = 'oauth'
|
||||
)
|
||||
OR (
|
||||
LOWER(BTRIM(p.provider_type)) = 'kiro'
|
||||
AND (
|
||||
LOWER(BTRIM(pak.auth_type)) = 'oauth'
|
||||
OR (
|
||||
LOWER(BTRIM(pak.auth_type)) = 'bearer'
|
||||
AND COALESCE(BTRIM(pak.auth_config), '') <> ''
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
ALTER TABLE public.stats_daily_model
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_ephemeral_5m_tokens bigint DEFAULT '0'::bigint NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_ephemeral_1h_tokens bigint DEFAULT '0'::bigint NOT NULL;
|
||||
@@ -0,0 +1,262 @@
|
||||
ALTER TABLE IF EXISTS public.usage_settlement_snapshots
|
||||
ADD COLUMN IF NOT EXISTS settlement_snapshot_schema_version character varying(20),
|
||||
ADD COLUMN IF NOT EXISTS settlement_snapshot jsonb,
|
||||
ADD COLUMN IF NOT EXISTS billing_dimensions jsonb,
|
||||
ADD COLUMN IF NOT EXISTS billing_input_tokens bigint,
|
||||
ADD COLUMN IF NOT EXISTS billing_effective_input_tokens bigint,
|
||||
ADD COLUMN IF NOT EXISTS billing_output_tokens bigint,
|
||||
ADD COLUMN IF NOT EXISTS billing_cache_creation_tokens bigint,
|
||||
ADD COLUMN IF NOT EXISTS billing_cache_creation_5m_tokens bigint,
|
||||
ADD COLUMN IF NOT EXISTS billing_cache_creation_1h_tokens bigint,
|
||||
ADD COLUMN IF NOT EXISTS billing_cache_read_tokens bigint,
|
||||
ADD COLUMN IF NOT EXISTS billing_total_input_context bigint,
|
||||
ADD COLUMN IF NOT EXISTS billing_cache_creation_cost_usd numeric(20,8),
|
||||
ADD COLUMN IF NOT EXISTS billing_cache_read_cost_usd numeric(20,8),
|
||||
ADD COLUMN IF NOT EXISTS billing_total_cost_usd numeric(20,8),
|
||||
ADD COLUMN IF NOT EXISTS billing_actual_total_cost_usd numeric(20,8),
|
||||
ADD COLUMN IF NOT EXISTS billing_pricing_source character varying(50),
|
||||
ADD COLUMN IF NOT EXISTS billing_rule_id character varying(100),
|
||||
ADD COLUMN IF NOT EXISTS billing_rule_version character varying(50);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_usage_settlement_snapshots_schema_version
|
||||
ON public.usage_settlement_snapshots USING btree (settlement_snapshot_schema_version);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_usage_settlement_snapshots_pricing_source
|
||||
ON public.usage_settlement_snapshots USING btree (billing_pricing_source);
|
||||
|
||||
CREATE OR REPLACE VIEW public.usage_billing_facts AS
|
||||
SELECT
|
||||
usage_rows.id,
|
||||
usage_rows.request_id,
|
||||
usage_rows.user_id,
|
||||
usage_rows.api_key_id,
|
||||
usage_rows.username,
|
||||
usage_rows.api_key_name,
|
||||
usage_rows.provider_name,
|
||||
usage_rows.model,
|
||||
usage_rows.target_model,
|
||||
usage_rows.provider_id,
|
||||
usage_rows.provider_endpoint_id,
|
||||
usage_rows.provider_api_key_id,
|
||||
usage_rows.request_type,
|
||||
usage_rows.api_format,
|
||||
usage_rows.api_family,
|
||||
usage_rows.endpoint_kind,
|
||||
usage_rows.endpoint_api_format,
|
||||
usage_rows.provider_api_family,
|
||||
usage_rows.provider_endpoint_kind,
|
||||
COALESCE(usage_rows.has_format_conversion, FALSE) AS has_format_conversion,
|
||||
COALESCE(usage_rows.is_stream, FALSE) AS is_stream,
|
||||
usage_rows.status_code,
|
||||
usage_rows.error_message,
|
||||
usage_rows.error_category,
|
||||
usage_rows.response_time_ms,
|
||||
usage_rows.first_byte_time_ms,
|
||||
usage_rows.status,
|
||||
COALESCE(settlement.billing_status, usage_rows.billing_status) AS billing_status,
|
||||
usage_rows.created_at,
|
||||
COALESCE(settlement.finalized_at, usage_rows.finalized_at) AS finalized_at,
|
||||
GREATEST(COALESCE(settlement.billing_input_tokens, usage_rows.input_tokens, 0), 0)::bigint
|
||||
AS input_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_effective_input_tokens,
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
ELSE GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
)::bigint AS effective_input_tokens,
|
||||
GREATEST(COALESCE(settlement.billing_output_tokens, usage_rows.output_tokens, 0), 0)::bigint
|
||||
AS output_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_creation_tokens,
|
||||
CASE
|
||||
WHEN COALESCE(usage_rows.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(usage_rows.cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS cache_creation_input_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_creation_5m_tokens,
|
||||
usage_rows.cache_creation_input_tokens_5m,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS cache_creation_input_tokens_5m,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_creation_1h_tokens,
|
||||
usage_rows.cache_creation_input_tokens_1h,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS cache_creation_input_tokens_1h,
|
||||
GREATEST(COALESCE(settlement.billing_cache_read_tokens, usage_rows.cache_read_input_tokens, 0), 0)::bigint
|
||||
AS cache_read_input_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
CASE
|
||||
WHEN settlement.billing_input_tokens IS NOT NULL
|
||||
OR settlement.billing_output_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_read_tokens IS NOT NULL
|
||||
THEN COALESCE(settlement.billing_input_tokens, 0)
|
||||
+ COALESCE(settlement.billing_output_tokens, 0)
|
||||
+ COALESCE(
|
||||
settlement.billing_cache_creation_tokens,
|
||||
COALESCE(settlement.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(settlement.billing_cache_creation_1h_tokens, 0),
|
||||
0
|
||||
)
|
||||
+ COALESCE(settlement.billing_cache_read_tokens, 0)
|
||||
END,
|
||||
usage_rows.total_tokens,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS total_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_total_input_context,
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
IN ('claude', 'anthropic')
|
||||
THEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE(usage_rows.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(usage_rows.cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN CASE
|
||||
WHEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
ELSE GREATEST(
|
||||
GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
+ GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE(usage_rows.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(usage_rows.cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0)
|
||||
END,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS total_input_context,
|
||||
COALESCE(CAST(usage_rows.input_cost_usd AS DOUBLE PRECISION), 0) AS input_cost_usd,
|
||||
COALESCE(CAST(usage_rows.output_cost_usd AS DOUBLE PRECISION), 0) AS output_cost_usd,
|
||||
COALESCE(
|
||||
CAST(settlement.billing_cache_creation_cost_usd AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.cache_creation_cost_usd AS DOUBLE PRECISION),
|
||||
0
|
||||
) AS cache_creation_cost_usd,
|
||||
COALESCE(
|
||||
CAST(settlement.billing_cache_read_cost_usd AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.cache_read_cost_usd AS DOUBLE PRECISION),
|
||||
0
|
||||
) AS cache_read_cost_usd,
|
||||
COALESCE(
|
||||
CAST(settlement.billing_total_cost_usd AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.total_cost_usd AS DOUBLE PRECISION),
|
||||
0
|
||||
) AS total_cost_usd,
|
||||
COALESCE(
|
||||
CAST(settlement.billing_actual_total_cost_usd AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.actual_total_cost_usd AS DOUBLE PRECISION),
|
||||
0
|
||||
) AS actual_total_cost_usd,
|
||||
COALESCE(
|
||||
CAST(settlement.output_price_per_1m AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.output_price_per_1m AS DOUBLE PRECISION)
|
||||
) AS output_price_per_1m,
|
||||
COALESCE(
|
||||
CAST(settlement.input_price_per_1m AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.input_price_per_1m AS DOUBLE PRECISION)
|
||||
) AS input_price_per_1m,
|
||||
COALESCE(
|
||||
CAST(settlement.cache_creation_price_per_1m AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.cache_creation_price_per_1m AS DOUBLE PRECISION)
|
||||
) AS cache_creation_price_per_1m,
|
||||
COALESCE(
|
||||
CAST(settlement.cache_read_price_per_1m AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.cache_read_price_per_1m AS DOUBLE PRECISION)
|
||||
) AS cache_read_price_per_1m,
|
||||
COALESCE(
|
||||
CAST(settlement.price_per_request AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.price_per_request AS DOUBLE PRECISION)
|
||||
) AS price_per_request,
|
||||
settlement.billing_pricing_source,
|
||||
settlement.billing_rule_id,
|
||||
settlement.billing_rule_version
|
||||
FROM public."usage" AS usage_rows
|
||||
LEFT JOIN public.usage_settlement_snapshots AS settlement
|
||||
ON settlement.request_id = usage_rows.request_id;
|
||||
|
||||
COMMENT ON VIEW public.usage_billing_facts IS
|
||||
'Canonical billing read model. Token/cost fields prefer usage_settlement_snapshots.billing_* and fall back to deprecated usage mirrors for legacy rows.';
|
||||
|
||||
COMMENT ON COLUMN public.usage.input_tokens IS
|
||||
'DEPRECATED: billing dimension mirror. Use public.usage_settlement_snapshots.billing_input_tokens or public.usage_billing_facts.input_tokens.';
|
||||
COMMENT ON COLUMN public.usage.output_tokens IS
|
||||
'DEPRECATED: billing dimension mirror. Use public.usage_settlement_snapshots.billing_output_tokens or public.usage_billing_facts.output_tokens.';
|
||||
COMMENT ON COLUMN public.usage.total_tokens IS
|
||||
'DEPRECATED: billing dimension mirror. Use public.usage_billing_facts.total_tokens.';
|
||||
COMMENT ON COLUMN public.usage.cache_creation_input_tokens IS
|
||||
'DEPRECATED: billing dimension mirror. Use public.usage_settlement_snapshots.billing_cache_creation_tokens or public.usage_billing_facts.cache_creation_input_tokens.';
|
||||
COMMENT ON COLUMN public.usage.cache_creation_input_tokens_5m IS
|
||||
'DEPRECATED: billing dimension mirror. Use public.usage_settlement_snapshots.billing_cache_creation_5m_tokens or public.usage_billing_facts.cache_creation_input_tokens_5m.';
|
||||
COMMENT ON COLUMN public.usage.cache_creation_input_tokens_1h IS
|
||||
'DEPRECATED: billing dimension mirror. Use public.usage_settlement_snapshots.billing_cache_creation_1h_tokens or public.usage_billing_facts.cache_creation_input_tokens_1h.';
|
||||
COMMENT ON COLUMN public.usage.cache_read_input_tokens IS
|
||||
'DEPRECATED: billing dimension mirror. Use public.usage_settlement_snapshots.billing_cache_read_tokens or public.usage_billing_facts.cache_read_input_tokens.';
|
||||
COMMENT ON COLUMN public.usage.cache_creation_cost_usd IS
|
||||
'DEPRECATED: billing cost mirror. Use public.usage_settlement_snapshots.billing_cache_creation_cost_usd or public.usage_billing_facts.cache_creation_cost_usd.';
|
||||
COMMENT ON COLUMN public.usage.cache_read_cost_usd IS
|
||||
'DEPRECATED: billing cost mirror. Use public.usage_settlement_snapshots.billing_cache_read_cost_usd or public.usage_billing_facts.cache_read_cost_usd.';
|
||||
COMMENT ON COLUMN public.usage.total_cost_usd IS
|
||||
'DEPRECATED: billing cost mirror. Use public.usage_settlement_snapshots.billing_total_cost_usd or public.usage_billing_facts.total_cost_usd.';
|
||||
COMMENT ON COLUMN public.usage.actual_total_cost_usd IS
|
||||
'DEPRECATED: billing cost mirror. Use public.usage_settlement_snapshots.billing_actual_total_cost_usd or public.usage_billing_facts.actual_total_cost_usd.';
|
||||
@@ -0,0 +1,500 @@
|
||||
CREATE OR REPLACE FUNCTION public.aether_canonical_api_format_alias(value text)
|
||||
RETURNS text
|
||||
LANGUAGE sql
|
||||
IMMUTABLE
|
||||
AS $$
|
||||
SELECT CASE LOWER(BTRIM(COALESCE(value, '')))
|
||||
WHEN 'openai:cli' THEN 'openai:responses'
|
||||
WHEN 'openai:compact' THEN 'openai:responses:compact'
|
||||
WHEN 'claude:chat' THEN 'claude:messages'
|
||||
WHEN 'claude:cli' THEN 'claude:messages'
|
||||
WHEN 'gemini:chat' THEN 'gemini:generate_content'
|
||||
WHEN 'gemini:cli' THEN 'gemini:generate_content'
|
||||
ELSE LOWER(BTRIM(COALESCE(value, '')))
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.aether_legacy_raw_api_format_auth_type(value text)
|
||||
RETURNS text
|
||||
LANGUAGE sql
|
||||
IMMUTABLE
|
||||
AS $$
|
||||
SELECT CASE LOWER(BTRIM(COALESCE(value, '')))
|
||||
WHEN 'claude:chat' THEN 'api_key'
|
||||
WHEN 'claude:cli' THEN 'bearer'
|
||||
WHEN 'gemini:chat' THEN 'api_key'
|
||||
WHEN 'gemini:cli' THEN 'bearer'
|
||||
ELSE NULL
|
||||
END
|
||||
$$;
|
||||
|
||||
ALTER TABLE IF EXISTS public.provider_endpoints
|
||||
DROP CONSTRAINT IF EXISTS uq_provider_api_format;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_provider_endpoints_provider_api_format
|
||||
ON public.provider_endpoints USING btree (provider_id, api_format);
|
||||
|
||||
ALTER TABLE IF EXISTS public.provider_api_keys
|
||||
ADD COLUMN IF NOT EXISTS auth_type_by_format json;
|
||||
|
||||
ALTER TABLE IF EXISTS public.provider_api_keys
|
||||
ALTER COLUMN api_key DROP NOT NULL;
|
||||
|
||||
WITH legacy_key_formats AS (
|
||||
SELECT
|
||||
pak.id,
|
||||
LOWER(BTRIM(COALESCE(provider.provider_type, ''))) AS provider_type,
|
||||
LOWER(BTRIM(COALESCE(pak.auth_type, ''))) AS current_auth_type,
|
||||
COALESCE(BOOL_OR(LOWER(BTRIM(format.value)) = 'claude:chat'), false) AS has_claude_chat,
|
||||
COALESCE(BOOL_OR(LOWER(BTRIM(format.value)) = 'claude:cli'), false) AS has_claude_cli,
|
||||
COALESCE(BOOL_OR(LOWER(BTRIM(format.value)) = 'gemini:chat'), false) AS has_gemini_chat,
|
||||
COALESCE(BOOL_OR(LOWER(BTRIM(format.value)) = 'gemini:cli'), false) AS has_gemini_cli
|
||||
FROM public.provider_api_keys AS pak
|
||||
INNER JOIN public.providers AS provider
|
||||
ON provider.id = pak.provider_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT key_format.value
|
||||
FROM json_array_elements_text(
|
||||
CASE
|
||||
WHEN pak.api_formats IS NOT NULL
|
||||
AND json_typeof(pak.api_formats) = 'array'
|
||||
THEN pak.api_formats
|
||||
ELSE '[]'::json
|
||||
END
|
||||
) AS key_format(value)
|
||||
UNION ALL
|
||||
SELECT endpoint.api_format AS value
|
||||
FROM public.provider_endpoints AS endpoint
|
||||
WHERE pak.api_formats IS NULL
|
||||
AND endpoint.provider_id = pak.provider_id
|
||||
) AS format ON true
|
||||
GROUP BY pak.id, provider_type, current_auth_type
|
||||
),
|
||||
inferred_key_auth AS (
|
||||
SELECT
|
||||
id,
|
||||
current_auth_type,
|
||||
CASE
|
||||
WHEN provider_type IN ('claude_code', 'kiro')
|
||||
AND has_claude_cli
|
||||
THEN 'oauth'
|
||||
WHEN provider_type IN ('gemini_cli', 'antigravity')
|
||||
AND has_gemini_cli
|
||||
THEN 'oauth'
|
||||
WHEN provider_type NOT IN ('claude_code', 'codex', 'gemini_cli', 'vertex_ai', 'antigravity', 'kiro')
|
||||
AND (has_claude_chat OR has_gemini_chat)
|
||||
AND NOT (has_claude_cli OR has_gemini_cli)
|
||||
THEN 'api_key'
|
||||
ELSE NULL
|
||||
END AS inferred_auth_type
|
||||
FROM legacy_key_formats
|
||||
)
|
||||
UPDATE public.provider_api_keys AS pak
|
||||
SET
|
||||
auth_type = inferred.inferred_auth_type,
|
||||
updated_at = NOW()
|
||||
FROM inferred_key_auth AS inferred
|
||||
WHERE pak.id = inferred.id
|
||||
AND inferred.inferred_auth_type IS NOT NULL
|
||||
AND inferred.current_auth_type IS DISTINCT FROM inferred.inferred_auth_type
|
||||
AND (
|
||||
(
|
||||
inferred.inferred_auth_type = 'oauth'
|
||||
AND inferred.current_auth_type IN ('', 'api_key', 'bearer')
|
||||
)
|
||||
OR (
|
||||
inferred.inferred_auth_type = 'api_key'
|
||||
AND inferred.current_auth_type IN ('', 'bearer')
|
||||
)
|
||||
);
|
||||
|
||||
WITH existing_auth_entries AS (
|
||||
SELECT
|
||||
pak.id,
|
||||
public.aether_canonical_api_format_alias(entry.key) AS api_format,
|
||||
CASE LOWER(BTRIM(COALESCE(entry.value #>> '{}', '')))
|
||||
WHEN 'api_key' THEN 'api_key'
|
||||
WHEN 'apikey' THEN 'api_key'
|
||||
WHEN 'api-key' THEN 'api_key'
|
||||
WHEN 'bearer' THEN 'bearer'
|
||||
WHEN 'bearer_token' THEN 'bearer'
|
||||
WHEN 'bearer-token' THEN 'bearer'
|
||||
WHEN 'authorization' THEN 'bearer'
|
||||
ELSE NULL
|
||||
END AS auth_type,
|
||||
0 AS source_priority,
|
||||
entry.ordinality
|
||||
FROM public.provider_api_keys AS pak
|
||||
CROSS JOIN LATERAL json_each(
|
||||
CASE
|
||||
WHEN json_typeof(pak.auth_type_by_format) = 'object' THEN pak.auth_type_by_format
|
||||
ELSE '{}'::json
|
||||
END
|
||||
) WITH ORDINALITY AS entry(key, value, ordinality)
|
||||
WHERE pak.auth_type_by_format IS NOT NULL
|
||||
),
|
||||
legacy_auth_entries AS (
|
||||
SELECT
|
||||
pak.id,
|
||||
public.aether_canonical_api_format_alias(format.value) AS api_format,
|
||||
MIN(public.aether_legacy_raw_api_format_auth_type(format.value)) AS auth_type,
|
||||
1 AS source_priority,
|
||||
MIN(format.ordinality) AS ordinality
|
||||
FROM public.provider_api_keys AS pak
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT key_format.value, key_format.ordinality
|
||||
FROM json_array_elements_text(
|
||||
CASE
|
||||
WHEN pak.api_formats IS NOT NULL
|
||||
AND json_typeof(pak.api_formats) = 'array'
|
||||
THEN pak.api_formats
|
||||
ELSE '[]'::json
|
||||
END
|
||||
) WITH ORDINALITY AS key_format(value, ordinality)
|
||||
UNION ALL
|
||||
SELECT endpoint.api_format AS value, endpoint.ordinality
|
||||
FROM (
|
||||
SELECT endpoint.api_format, ROW_NUMBER() OVER (ORDER BY endpoint.id) AS ordinality
|
||||
FROM public.provider_endpoints AS endpoint
|
||||
WHERE pak.api_formats IS NULL
|
||||
AND endpoint.provider_id = pak.provider_id
|
||||
) AS endpoint
|
||||
) AS format ON true
|
||||
WHERE LOWER(BTRIM(COALESCE(pak.auth_type, ''))) IN ('api_key', 'bearer')
|
||||
AND public.aether_legacy_raw_api_format_auth_type(format.value) IS NOT NULL
|
||||
GROUP BY
|
||||
pak.id,
|
||||
public.aether_canonical_api_format_alias(format.value)
|
||||
HAVING COUNT(DISTINCT public.aether_legacy_raw_api_format_auth_type(format.value)) = 1
|
||||
),
|
||||
auth_entries AS (
|
||||
SELECT id, api_format, auth_type, source_priority, ordinality
|
||||
FROM existing_auth_entries
|
||||
WHERE api_format <> ''
|
||||
AND auth_type IS NOT NULL
|
||||
UNION ALL
|
||||
SELECT
|
||||
legacy.id,
|
||||
legacy.api_format,
|
||||
legacy.auth_type,
|
||||
legacy.source_priority,
|
||||
legacy.ordinality
|
||||
FROM legacy_auth_entries AS legacy
|
||||
INNER JOIN public.provider_api_keys AS pak
|
||||
ON pak.id = legacy.id
|
||||
WHERE legacy.api_format <> ''
|
||||
AND legacy.auth_type IS NOT NULL
|
||||
AND legacy.auth_type IS DISTINCT FROM LOWER(BTRIM(COALESCE(pak.auth_type, '')))
|
||||
),
|
||||
ranked AS (
|
||||
SELECT
|
||||
id,
|
||||
api_format,
|
||||
auth_type,
|
||||
source_priority,
|
||||
ordinality,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY id, api_format
|
||||
ORDER BY source_priority, ordinality
|
||||
) AS rank
|
||||
FROM auth_entries
|
||||
),
|
||||
rebuilt AS (
|
||||
SELECT
|
||||
id,
|
||||
jsonb_object_agg(api_format, to_jsonb(auth_type) ORDER BY source_priority, ordinality) AS auth_type_by_format
|
||||
FROM ranked
|
||||
WHERE rank = 1
|
||||
GROUP BY id
|
||||
)
|
||||
UPDATE public.provider_api_keys AS pak
|
||||
SET
|
||||
auth_type_by_format = rebuilt.auth_type_by_format::json,
|
||||
updated_at = NOW()
|
||||
FROM rebuilt
|
||||
WHERE pak.id = rebuilt.id
|
||||
AND pak.auth_type_by_format::jsonb IS DISTINCT FROM rebuilt.auth_type_by_format;
|
||||
|
||||
WITH normalized AS (
|
||||
SELECT
|
||||
id,
|
||||
public.aether_canonical_api_format_alias(api_format) AS canonical_api_format
|
||||
FROM public.provider_endpoints
|
||||
WHERE api_format IN ('openai:responses', 'openai:cli', 'openai:responses:compact', 'openai:compact', 'claude:messages', 'claude:chat', 'claude:cli', 'gemini:generate_content', 'gemini:chat', 'gemini:cli')
|
||||
)
|
||||
UPDATE public.provider_endpoints AS endpoint
|
||||
SET
|
||||
api_format = normalized.canonical_api_format,
|
||||
api_family = SPLIT_PART(normalized.canonical_api_format, ':', 1),
|
||||
endpoint_kind = SUBSTRING(normalized.canonical_api_format FROM POSITION(':' IN normalized.canonical_api_format) + 1),
|
||||
updated_at = NOW()
|
||||
FROM normalized
|
||||
WHERE endpoint.id = normalized.id
|
||||
AND (
|
||||
endpoint.api_format IS DISTINCT FROM normalized.canonical_api_format
|
||||
OR endpoint.api_family IS DISTINCT FROM SPLIT_PART(normalized.canonical_api_format, ':', 1)
|
||||
OR endpoint.endpoint_kind IS DISTINCT FROM SUBSTRING(normalized.canonical_api_format FROM POSITION(':' IN normalized.canonical_api_format) + 1)
|
||||
);
|
||||
|
||||
WITH expanded AS (
|
||||
SELECT
|
||||
pak.id,
|
||||
formats.ordinality,
|
||||
public.aether_canonical_api_format_alias(formats.value) AS api_format
|
||||
FROM public.provider_api_keys AS pak
|
||||
CROSS JOIN LATERAL json_array_elements_text(
|
||||
CASE
|
||||
WHEN json_typeof(pak.api_formats) = 'array' THEN pak.api_formats
|
||||
ELSE '[]'::json
|
||||
END
|
||||
) WITH ORDINALITY AS formats(value, ordinality)
|
||||
WHERE pak.api_formats IS NOT NULL
|
||||
),
|
||||
deduped AS (
|
||||
SELECT id, api_format, MIN(ordinality) AS first_ordinality
|
||||
FROM expanded
|
||||
WHERE api_format <> ''
|
||||
GROUP BY id, api_format
|
||||
),
|
||||
rebuilt AS (
|
||||
SELECT id, json_agg(api_format ORDER BY first_ordinality) AS api_formats
|
||||
FROM deduped
|
||||
GROUP BY id
|
||||
)
|
||||
UPDATE public.provider_api_keys AS pak
|
||||
SET
|
||||
api_formats = rebuilt.api_formats,
|
||||
updated_at = NOW()
|
||||
FROM rebuilt
|
||||
WHERE pak.id = rebuilt.id
|
||||
AND pak.api_formats::jsonb IS DISTINCT FROM rebuilt.api_formats::jsonb;
|
||||
|
||||
WITH expanded AS (
|
||||
SELECT
|
||||
key.id,
|
||||
formats.ordinality,
|
||||
public.aether_canonical_api_format_alias(formats.value) AS api_format
|
||||
FROM public.api_keys AS key
|
||||
CROSS JOIN LATERAL json_array_elements_text(
|
||||
CASE
|
||||
WHEN json_typeof(key.allowed_api_formats) = 'array' THEN key.allowed_api_formats
|
||||
ELSE '[]'::json
|
||||
END
|
||||
) WITH ORDINALITY AS formats(value, ordinality)
|
||||
WHERE key.allowed_api_formats IS NOT NULL
|
||||
),
|
||||
deduped AS (
|
||||
SELECT id, api_format, MIN(ordinality) AS first_ordinality
|
||||
FROM expanded
|
||||
WHERE api_format <> ''
|
||||
GROUP BY id, api_format
|
||||
),
|
||||
rebuilt AS (
|
||||
SELECT id, json_agg(api_format ORDER BY first_ordinality) AS allowed_api_formats
|
||||
FROM deduped
|
||||
GROUP BY id
|
||||
)
|
||||
UPDATE public.api_keys AS key
|
||||
SET
|
||||
allowed_api_formats = rebuilt.allowed_api_formats,
|
||||
updated_at = NOW()
|
||||
FROM rebuilt
|
||||
WHERE key.id = rebuilt.id
|
||||
AND key.allowed_api_formats::jsonb IS DISTINCT FROM rebuilt.allowed_api_formats::jsonb;
|
||||
|
||||
WITH expanded AS (
|
||||
SELECT
|
||||
users.id,
|
||||
formats.ordinality,
|
||||
public.aether_canonical_api_format_alias(formats.value) AS api_format
|
||||
FROM public.users AS users
|
||||
CROSS JOIN LATERAL json_array_elements_text(
|
||||
CASE
|
||||
WHEN json_typeof(users.allowed_api_formats) = 'array' THEN users.allowed_api_formats
|
||||
ELSE '[]'::json
|
||||
END
|
||||
) WITH ORDINALITY AS formats(value, ordinality)
|
||||
WHERE users.allowed_api_formats IS NOT NULL
|
||||
),
|
||||
deduped AS (
|
||||
SELECT id, api_format, MIN(ordinality) AS first_ordinality
|
||||
FROM expanded
|
||||
WHERE api_format <> ''
|
||||
GROUP BY id, api_format
|
||||
),
|
||||
rebuilt AS (
|
||||
SELECT id, json_agg(api_format ORDER BY first_ordinality) AS allowed_api_formats
|
||||
FROM deduped
|
||||
GROUP BY id
|
||||
)
|
||||
UPDATE public.users AS users
|
||||
SET
|
||||
allowed_api_formats = rebuilt.allowed_api_formats,
|
||||
updated_at = NOW()
|
||||
FROM rebuilt
|
||||
WHERE users.id = rebuilt.id
|
||||
AND users.allowed_api_formats::jsonb IS DISTINCT FROM rebuilt.allowed_api_formats::jsonb;
|
||||
|
||||
WITH mapping_items AS (
|
||||
SELECT
|
||||
models.id,
|
||||
item.ordinality AS item_ordinality,
|
||||
item.value AS item
|
||||
FROM public.models AS models
|
||||
CROSS JOIN LATERAL jsonb_array_elements(
|
||||
CASE
|
||||
WHEN jsonb_typeof(models.provider_model_mappings) = 'array' THEN models.provider_model_mappings
|
||||
ELSE '[]'::jsonb
|
||||
END
|
||||
) WITH ORDINALITY AS item(value, ordinality)
|
||||
WHERE models.provider_model_mappings IS NOT NULL
|
||||
),
|
||||
rebuilt_items AS (
|
||||
SELECT
|
||||
id,
|
||||
item_ordinality,
|
||||
CASE
|
||||
WHEN jsonb_typeof(item) = 'object'
|
||||
AND jsonb_typeof(item->'api_formats') = 'array'
|
||||
THEN jsonb_set(
|
||||
item,
|
||||
'{api_formats}',
|
||||
COALESCE(
|
||||
(
|
||||
SELECT jsonb_agg(api_format ORDER BY first_ordinality)
|
||||
FROM (
|
||||
SELECT
|
||||
public.aether_canonical_api_format_alias(format.value) AS api_format,
|
||||
MIN(format.ordinality) AS first_ordinality
|
||||
FROM jsonb_array_elements_text(item->'api_formats') WITH ORDINALITY AS format(value, ordinality)
|
||||
GROUP BY public.aether_canonical_api_format_alias(format.value)
|
||||
) AS deduped_formats
|
||||
WHERE api_format <> ''
|
||||
),
|
||||
'[]'::jsonb
|
||||
),
|
||||
true
|
||||
)
|
||||
ELSE item
|
||||
END AS item
|
||||
FROM mapping_items
|
||||
),
|
||||
rebuilt AS (
|
||||
SELECT id, jsonb_agg(item ORDER BY item_ordinality) AS provider_model_mappings
|
||||
FROM rebuilt_items
|
||||
GROUP BY id
|
||||
)
|
||||
UPDATE public.models AS models
|
||||
SET
|
||||
provider_model_mappings = rebuilt.provider_model_mappings,
|
||||
updated_at = NOW()
|
||||
FROM rebuilt
|
||||
WHERE models.id = rebuilt.id
|
||||
AND models.provider_model_mappings IS DISTINCT FROM rebuilt.provider_model_mappings;
|
||||
|
||||
WITH rebuilt AS (
|
||||
SELECT
|
||||
pak.id,
|
||||
jsonb_object_agg(
|
||||
public.aether_canonical_api_format_alias(entry.key),
|
||||
entry.value
|
||||
ORDER BY entry.ordinality
|
||||
) AS rate_multipliers
|
||||
FROM public.provider_api_keys AS pak
|
||||
CROSS JOIN LATERAL json_each(
|
||||
CASE
|
||||
WHEN json_typeof(pak.rate_multipliers) = 'object' THEN pak.rate_multipliers
|
||||
ELSE '{}'::json
|
||||
END
|
||||
) WITH ORDINALITY AS entry(key, value, ordinality)
|
||||
WHERE pak.rate_multipliers IS NOT NULL
|
||||
GROUP BY pak.id
|
||||
)
|
||||
UPDATE public.provider_api_keys AS pak
|
||||
SET
|
||||
rate_multipliers = rebuilt.rate_multipliers::json,
|
||||
updated_at = NOW()
|
||||
FROM rebuilt
|
||||
WHERE pak.id = rebuilt.id
|
||||
AND pak.rate_multipliers::jsonb IS DISTINCT FROM rebuilt.rate_multipliers;
|
||||
|
||||
WITH rebuilt AS (
|
||||
SELECT
|
||||
pak.id,
|
||||
jsonb_object_agg(
|
||||
public.aether_canonical_api_format_alias(entry.key),
|
||||
entry.value
|
||||
ORDER BY entry.ordinality
|
||||
) AS global_priority_by_format
|
||||
FROM public.provider_api_keys AS pak
|
||||
CROSS JOIN LATERAL json_each(
|
||||
CASE
|
||||
WHEN json_typeof(pak.global_priority_by_format) = 'object' THEN pak.global_priority_by_format
|
||||
ELSE '{}'::json
|
||||
END
|
||||
) WITH ORDINALITY AS entry(key, value, ordinality)
|
||||
WHERE pak.global_priority_by_format IS NOT NULL
|
||||
GROUP BY pak.id
|
||||
)
|
||||
UPDATE public.provider_api_keys AS pak
|
||||
SET
|
||||
global_priority_by_format = rebuilt.global_priority_by_format::json,
|
||||
updated_at = NOW()
|
||||
FROM rebuilt
|
||||
WHERE pak.id = rebuilt.id
|
||||
AND pak.global_priority_by_format::jsonb IS DISTINCT FROM rebuilt.global_priority_by_format;
|
||||
|
||||
WITH rebuilt AS (
|
||||
SELECT
|
||||
pak.id,
|
||||
jsonb_object_agg(
|
||||
public.aether_canonical_api_format_alias(entry.key),
|
||||
entry.value
|
||||
ORDER BY entry.ordinality
|
||||
) AS health_by_format
|
||||
FROM public.provider_api_keys AS pak
|
||||
CROSS JOIN LATERAL jsonb_each(
|
||||
CASE
|
||||
WHEN jsonb_typeof(pak.health_by_format) = 'object' THEN pak.health_by_format
|
||||
ELSE '{}'::jsonb
|
||||
END
|
||||
) WITH ORDINALITY AS entry(key, value, ordinality)
|
||||
WHERE pak.health_by_format IS NOT NULL
|
||||
GROUP BY pak.id
|
||||
)
|
||||
UPDATE public.provider_api_keys AS pak
|
||||
SET
|
||||
health_by_format = rebuilt.health_by_format,
|
||||
updated_at = NOW()
|
||||
FROM rebuilt
|
||||
WHERE pak.id = rebuilt.id
|
||||
AND pak.health_by_format IS DISTINCT FROM rebuilt.health_by_format;
|
||||
|
||||
WITH rebuilt AS (
|
||||
SELECT
|
||||
pak.id,
|
||||
jsonb_object_agg(
|
||||
public.aether_canonical_api_format_alias(entry.key),
|
||||
entry.value
|
||||
ORDER BY entry.ordinality
|
||||
) AS circuit_breaker_by_format
|
||||
FROM public.provider_api_keys AS pak
|
||||
CROSS JOIN LATERAL jsonb_each(
|
||||
CASE
|
||||
WHEN jsonb_typeof(pak.circuit_breaker_by_format) = 'object' THEN pak.circuit_breaker_by_format
|
||||
ELSE '{}'::jsonb
|
||||
END
|
||||
) WITH ORDINALITY AS entry(key, value, ordinality)
|
||||
WHERE pak.circuit_breaker_by_format IS NOT NULL
|
||||
GROUP BY pak.id
|
||||
)
|
||||
UPDATE public.provider_api_keys AS pak
|
||||
SET
|
||||
circuit_breaker_by_format = rebuilt.circuit_breaker_by_format,
|
||||
updated_at = NOW()
|
||||
FROM rebuilt
|
||||
WHERE pak.id = rebuilt.id
|
||||
AND pak.circuit_breaker_by_format IS DISTINCT FROM rebuilt.circuit_breaker_by_format;
|
||||
|
||||
DROP FUNCTION public.aether_legacy_raw_api_format_auth_type(text);
|
||||
DROP FUNCTION public.aether_canonical_api_format_alias(text);
|
||||
@@ -0,0 +1,80 @@
|
||||
ALTER TABLE public.provider_api_keys
|
||||
ADD COLUMN IF NOT EXISTS allow_auth_channel_mismatch_formats json;
|
||||
|
||||
ALTER TABLE public.provider_api_keys
|
||||
ADD COLUMN IF NOT EXISTS concurrent_limit integer;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.aether_default_auth_mismatch_api_format(value text)
|
||||
RETURNS text
|
||||
LANGUAGE sql
|
||||
IMMUTABLE
|
||||
AS $$
|
||||
SELECT CASE LOWER(BTRIM(COALESCE(value, '')))
|
||||
WHEN 'openai:cli' THEN 'openai:responses'
|
||||
WHEN 'openai:compact' THEN 'openai:responses:compact'
|
||||
WHEN 'claude:chat' THEN 'claude:messages'
|
||||
WHEN 'claude:cli' THEN 'claude:messages'
|
||||
WHEN 'gemini:chat' THEN 'gemini:generate_content'
|
||||
WHEN 'gemini:cli' THEN 'gemini:generate_content'
|
||||
ELSE LOWER(BTRIM(COALESCE(value, '')))
|
||||
END
|
||||
$$;
|
||||
|
||||
WITH supported_formats AS (
|
||||
SELECT
|
||||
pak.id,
|
||||
public.aether_default_auth_mismatch_api_format(format.value) AS api_format,
|
||||
0 AS source_priority,
|
||||
MIN(format.ordinality) AS first_ordinality
|
||||
FROM public.provider_api_keys AS pak
|
||||
CROSS JOIN LATERAL json_array_elements_text(
|
||||
CASE
|
||||
WHEN pak.api_formats IS NOT NULL
|
||||
AND json_typeof(pak.api_formats) = 'array'
|
||||
THEN pak.api_formats
|
||||
ELSE '[]'::json
|
||||
END
|
||||
) WITH ORDINALITY AS format(value, ordinality)
|
||||
WHERE pak.api_formats IS NOT NULL
|
||||
AND json_typeof(pak.api_formats) = 'array'
|
||||
GROUP BY pak.id, api_format
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
pak.id,
|
||||
public.aether_default_auth_mismatch_api_format(endpoint.api_format) AS api_format,
|
||||
1 AS source_priority,
|
||||
0 AS first_ordinality
|
||||
FROM public.provider_api_keys AS pak
|
||||
INNER JOIN public.provider_endpoints AS endpoint
|
||||
ON endpoint.provider_id = pak.provider_id
|
||||
WHERE pak.api_formats IS NULL
|
||||
OR json_typeof(pak.api_formats) <> 'array'
|
||||
),
|
||||
deduplicated_formats AS (
|
||||
SELECT
|
||||
id,
|
||||
api_format,
|
||||
MIN(source_priority) AS source_priority,
|
||||
MIN(first_ordinality) AS first_ordinality
|
||||
FROM supported_formats
|
||||
WHERE api_format <> ''
|
||||
GROUP BY id, api_format
|
||||
),
|
||||
rebuilt AS (
|
||||
SELECT
|
||||
id,
|
||||
json_agg(api_format ORDER BY source_priority, first_ordinality, api_format) AS api_formats
|
||||
FROM deduplicated_formats
|
||||
GROUP BY id
|
||||
)
|
||||
UPDATE public.provider_api_keys AS pak
|
||||
SET
|
||||
allow_auth_channel_mismatch_formats = rebuilt.api_formats,
|
||||
updated_at = NOW()
|
||||
FROM rebuilt
|
||||
WHERE pak.id = rebuilt.id
|
||||
AND pak.allow_auth_channel_mismatch_formats IS NULL;
|
||||
|
||||
DROP FUNCTION public.aether_default_auth_mismatch_api_format(text);
|
||||
@@ -0,0 +1,61 @@
|
||||
ALTER TABLE public.users
|
||||
ADD COLUMN IF NOT EXISTS external_id character varying(255),
|
||||
ADD COLUMN IF NOT EXISTS metadata json;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.auth_modules (
|
||||
id character varying(36) NOT NULL,
|
||||
module_type character varying(128) NOT NULL,
|
||||
enabled boolean DEFAULT true NOT NULL,
|
||||
config json NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT auth_modules_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT auth_modules_module_type_key UNIQUE (module_type)
|
||||
);
|
||||
|
||||
ALTER TABLE public.api_keys
|
||||
ADD COLUMN IF NOT EXISTS key_prefix character varying(64),
|
||||
ADD COLUMN IF NOT EXISTS status character varying(64) DEFAULT 'active'::character varying NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS metadata json;
|
||||
|
||||
ALTER TABLE public.providers
|
||||
ADD COLUMN IF NOT EXISTS enabled boolean DEFAULT true NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS priority bigint DEFAULT 0 NOT NULL;
|
||||
|
||||
ALTER TABLE public.provider_api_keys
|
||||
ADD COLUMN IF NOT EXISTS encrypted_key text,
|
||||
ADD COLUMN IF NOT EXISTS status character varying(64) DEFAULT 'active'::character varying NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS weight bigint DEFAULT 1 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS metadata json;
|
||||
|
||||
ALTER TABLE public.provider_endpoints
|
||||
ADD COLUMN IF NOT EXISTS name character varying(255),
|
||||
ADD COLUMN IF NOT EXISTS enabled boolean DEFAULT true NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS weight bigint DEFAULT 1 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS metadata json;
|
||||
|
||||
ALTER TABLE public.provider_endpoints
|
||||
ALTER COLUMN api_format DROP NOT NULL;
|
||||
|
||||
ALTER TABLE public.global_models
|
||||
ADD COLUMN IF NOT EXISTS enabled boolean DEFAULT true NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS metadata json;
|
||||
|
||||
ALTER TABLE public.global_models
|
||||
ALTER COLUMN display_name DROP NOT NULL,
|
||||
ALTER COLUMN default_tiered_pricing DROP NOT NULL;
|
||||
|
||||
ALTER TABLE public.models
|
||||
ADD COLUMN IF NOT EXISTS global_model_name character varying(255),
|
||||
ADD COLUMN IF NOT EXISTS api_format character varying(128),
|
||||
ADD COLUMN IF NOT EXISTS enabled boolean DEFAULT true NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS metadata json;
|
||||
|
||||
ALTER TABLE public.models
|
||||
ALTER COLUMN global_model_id DROP NOT NULL;
|
||||
|
||||
ALTER TABLE public.usage
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_ephemeral_5m_input_tokens integer DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS cache_creation_ephemeral_1h_input_tokens integer DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS created_at_unix_ms bigint DEFAULT 0 NOT NULL,
|
||||
ADD COLUMN IF NOT EXISTS updated_at_unix_secs bigint DEFAULT 0 NOT NULL;
|
||||
@@ -0,0 +1,233 @@
|
||||
ALTER TABLE IF EXISTS public.usage
|
||||
ADD COLUMN IF NOT EXISTS upstream_is_stream boolean;
|
||||
|
||||
UPDATE public.usage
|
||||
SET upstream_is_stream = COALESCE(
|
||||
CASE
|
||||
WHEN (request_metadata->>'upstream_is_stream') IN ('true', 'false')
|
||||
THEN (request_metadata->>'upstream_is_stream')::boolean
|
||||
ELSE NULL
|
||||
END,
|
||||
COALESCE(is_stream, FALSE)
|
||||
)
|
||||
WHERE upstream_is_stream IS NULL;
|
||||
|
||||
ANALYZE public.usage;
|
||||
|
||||
COMMENT ON COLUMN public.usage.upstream_is_stream IS
|
||||
'Resolved upstream stream mode from request_metadata.upstream_is_stream, falling back to is_stream for legacy rows.';
|
||||
|
||||
CREATE OR REPLACE VIEW public.usage_billing_facts AS
|
||||
SELECT
|
||||
usage_rows.id,
|
||||
usage_rows.request_id,
|
||||
usage_rows.user_id,
|
||||
usage_rows.api_key_id,
|
||||
usage_rows.username,
|
||||
usage_rows.api_key_name,
|
||||
usage_rows.provider_name,
|
||||
usage_rows.model,
|
||||
usage_rows.target_model,
|
||||
usage_rows.provider_id,
|
||||
usage_rows.provider_endpoint_id,
|
||||
usage_rows.provider_api_key_id,
|
||||
usage_rows.request_type,
|
||||
usage_rows.api_format,
|
||||
usage_rows.api_family,
|
||||
usage_rows.endpoint_kind,
|
||||
usage_rows.endpoint_api_format,
|
||||
usage_rows.provider_api_family,
|
||||
usage_rows.provider_endpoint_kind,
|
||||
COALESCE(usage_rows.has_format_conversion, FALSE) AS has_format_conversion,
|
||||
COALESCE(usage_rows.is_stream, FALSE) AS is_stream,
|
||||
usage_rows.status_code,
|
||||
usage_rows.error_message,
|
||||
usage_rows.error_category,
|
||||
usage_rows.response_time_ms,
|
||||
usage_rows.first_byte_time_ms,
|
||||
usage_rows.status,
|
||||
COALESCE(settlement.billing_status, usage_rows.billing_status) AS billing_status,
|
||||
usage_rows.created_at,
|
||||
COALESCE(settlement.finalized_at, usage_rows.finalized_at) AS finalized_at,
|
||||
GREATEST(COALESCE(settlement.billing_input_tokens, usage_rows.input_tokens, 0), 0)::bigint
|
||||
AS input_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_effective_input_tokens,
|
||||
CASE
|
||||
WHEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN GREATEST(
|
||||
GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
ELSE GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
END
|
||||
),
|
||||
0
|
||||
)::bigint AS effective_input_tokens,
|
||||
GREATEST(COALESCE(settlement.billing_output_tokens, usage_rows.output_tokens, 0), 0)::bigint
|
||||
AS output_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_creation_tokens,
|
||||
CASE
|
||||
WHEN COALESCE(usage_rows.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(usage_rows.cache_creation_input_tokens, 0)
|
||||
END,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS cache_creation_input_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_creation_5m_tokens,
|
||||
usage_rows.cache_creation_input_tokens_5m,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS cache_creation_input_tokens_5m,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_cache_creation_1h_tokens,
|
||||
usage_rows.cache_creation_input_tokens_1h,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS cache_creation_input_tokens_1h,
|
||||
GREATEST(COALESCE(settlement.billing_cache_read_tokens, usage_rows.cache_read_input_tokens, 0), 0)::bigint
|
||||
AS cache_read_input_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
CASE
|
||||
WHEN settlement.billing_input_tokens IS NOT NULL
|
||||
OR settlement.billing_output_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_5m_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_creation_1h_tokens IS NOT NULL
|
||||
OR settlement.billing_cache_read_tokens IS NOT NULL
|
||||
THEN COALESCE(settlement.billing_input_tokens, 0)
|
||||
+ COALESCE(settlement.billing_output_tokens, 0)
|
||||
+ COALESCE(
|
||||
settlement.billing_cache_creation_tokens,
|
||||
COALESCE(settlement.billing_cache_creation_5m_tokens, 0)
|
||||
+ COALESCE(settlement.billing_cache_creation_1h_tokens, 0),
|
||||
0
|
||||
)
|
||||
+ COALESCE(settlement.billing_cache_read_tokens, 0)
|
||||
END,
|
||||
usage_rows.total_tokens,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS total_tokens,
|
||||
GREATEST(
|
||||
COALESCE(
|
||||
settlement.billing_total_input_context,
|
||||
CASE
|
||||
WHEN split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
IN ('claude', 'anthropic')
|
||||
THEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE(usage_rows.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(usage_rows.cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0)
|
||||
WHEN split_part(lower(COALESCE(COALESCE(usage_rows.endpoint_api_format, usage_rows.api_format), '')), ':', 1)
|
||||
IN ('openai', 'gemini', 'google')
|
||||
THEN CASE
|
||||
WHEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0) <= 0 THEN 0
|
||||
WHEN GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0) <= 0
|
||||
THEN GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
ELSE GREATEST(
|
||||
GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
- GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0),
|
||||
0
|
||||
)
|
||||
END
|
||||
+ GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0)
|
||||
ELSE GREATEST(COALESCE(usage_rows.input_tokens, 0), 0)
|
||||
+ CASE
|
||||
WHEN COALESCE(usage_rows.cache_creation_input_tokens, 0) = 0
|
||||
AND (
|
||||
COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
) > 0
|
||||
THEN COALESCE(usage_rows.cache_creation_input_tokens_5m, 0)
|
||||
+ COALESCE(usage_rows.cache_creation_input_tokens_1h, 0)
|
||||
ELSE COALESCE(usage_rows.cache_creation_input_tokens, 0)
|
||||
END
|
||||
+ GREATEST(COALESCE(usage_rows.cache_read_input_tokens, 0), 0)
|
||||
END,
|
||||
0
|
||||
),
|
||||
0
|
||||
)::bigint AS total_input_context,
|
||||
COALESCE(CAST(usage_rows.input_cost_usd AS DOUBLE PRECISION), 0) AS input_cost_usd,
|
||||
COALESCE(CAST(usage_rows.output_cost_usd AS DOUBLE PRECISION), 0) AS output_cost_usd,
|
||||
COALESCE(
|
||||
CAST(settlement.billing_cache_creation_cost_usd AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.cache_creation_cost_usd AS DOUBLE PRECISION),
|
||||
0
|
||||
) AS cache_creation_cost_usd,
|
||||
COALESCE(
|
||||
CAST(settlement.billing_cache_read_cost_usd AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.cache_read_cost_usd AS DOUBLE PRECISION),
|
||||
0
|
||||
) AS cache_read_cost_usd,
|
||||
COALESCE(
|
||||
CAST(settlement.billing_total_cost_usd AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.total_cost_usd AS DOUBLE PRECISION),
|
||||
0
|
||||
) AS total_cost_usd,
|
||||
COALESCE(
|
||||
CAST(settlement.billing_actual_total_cost_usd AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.actual_total_cost_usd AS DOUBLE PRECISION),
|
||||
0
|
||||
) AS actual_total_cost_usd,
|
||||
COALESCE(
|
||||
CAST(settlement.output_price_per_1m AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.output_price_per_1m AS DOUBLE PRECISION)
|
||||
) AS output_price_per_1m,
|
||||
COALESCE(
|
||||
CAST(settlement.input_price_per_1m AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.input_price_per_1m AS DOUBLE PRECISION)
|
||||
) AS input_price_per_1m,
|
||||
COALESCE(
|
||||
CAST(settlement.cache_creation_price_per_1m AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.cache_creation_price_per_1m AS DOUBLE PRECISION)
|
||||
) AS cache_creation_price_per_1m,
|
||||
COALESCE(
|
||||
CAST(settlement.cache_read_price_per_1m AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.cache_read_price_per_1m AS DOUBLE PRECISION)
|
||||
) AS cache_read_price_per_1m,
|
||||
COALESCE(
|
||||
CAST(settlement.price_per_request AS DOUBLE PRECISION),
|
||||
CAST(usage_rows.price_per_request AS DOUBLE PRECISION)
|
||||
) AS price_per_request,
|
||||
settlement.billing_pricing_source,
|
||||
settlement.billing_rule_id,
|
||||
settlement.billing_rule_version,
|
||||
COALESCE(usage_rows.upstream_is_stream, COALESCE(usage_rows.is_stream, FALSE)) AS upstream_is_stream
|
||||
FROM public."usage" AS usage_rows
|
||||
LEFT JOIN public.usage_settlement_snapshots AS settlement
|
||||
ON settlement.request_id = usage_rows.request_id;
|
||||
|
||||
COMMENT ON COLUMN public.usage_billing_facts.upstream_is_stream IS
|
||||
'Resolved upstream stream mode from public.usage.upstream_is_stream, falling back to usage.is_stream for legacy rows.';
|
||||
Reference in New Issue
Block a user